7-Segmente zum Dritten

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Samstag 1. Januar 2005, 16:37

Hallo,

ich hab mich in den letzten Tagen. auch mal an einer 7-Segmentanzeige herangemacht. Herausgekommen ist ein Modul, das sich GUI-Unabhängig verwenden lässt.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
    Modul:          SevenSegs
    Description:    GUI-indipend class for sevensegsdisplays
    Version:        0.1
    Copyright:      2004 by Fritz Cizmarov fritz@sol.at
    Created:        26. Dez. 2004
    Last modified:  01. Jän. 2005
    License:        free
    Requirements:   Python2.3
    Exports:        Drawer, Elem, Digit, Point, DPoint, Segments
"""


class Drawer(object):
    """ Baseclass for interface to the drawing_area or canvas to draw at """
    def __init__(self, *data, **kw):
        super(Drawer, self).__init__()

    def poly(self, points, color):
        """ redefine this in your own Drawerclass """
        raise NotImplemented("poly() is not implemented in baseclass!")
    
    def rect(self, x, y, w, h, color):
        self.poly(((x, y), (x, y+h), (x+w, y+h), (x+w, y)), color)


class ElemType(type):
    
    __classes = {}
    def classes(cls):
        return cls.__classes
    classes = property(classes)
    
    def __init__(cls, name, bases, dct):
        super(ElemType, cls).__init__(name, bases, dct)
        for key in dct["idkey"]:
            if cls.__classes.has_key(key):
                raise ValueError("'%s' already used!" % key)
            cls.__classes[key] = cls

    def create(cls, key, *args, **kw):
        return cls.__classes[key](*args, **kw)

    def calc_size(cls, fmt, scale):
        w = 0
        h = []
        for ch in fmt:
            tmp = cls.create(ch, " ")
            w += tmp.width * scale
            h += [tmp.height * scale]
        return w, max(h)

class Elem(object):

    __metaclass__ = ElemType
    idkey = ""
    values = ""
    shape = {"" : ()}
    llcd = {"" : ()}
    
    def __get_value(self):
        return self.__value

    def __set_value(self, value):
        if not value in self.values:
            msg = "value can only be one of '%s' not '%s!'"
            raise ValueError(msg % (self.values, value))
        try:
            if value == self.__value:
                return
            old = self.llcd[self.__value]
        except AttributeError:
            old = None
        self.__value = value
        if self.drawer:
            self.redraw(old)
    value = property(__get_value, __set_value)
    
    def get_width(self):
        max_x = 0
        for poly in self.shape.itervalues():
            max_x = max([a for a, b, in poly] + [max_x])
        return (max_x + 1) * self.scale
    width = property(get_width)

    def get_height(self):
        max_y = 0
        for poly in self.shape.itervalues():
            max_y = max([b for a, b, in poly] + [max_y])
        return (max_y + 1) * self.scale
    height = property(get_height)

    def get_xoffset(self):
        xoffset = self.parent.x
        if self.parent is not None:
            for seg in self.parent:
                if seg is self:
                    break
                xoffset += seg.width
        return xoffset
    xoffset = property(get_xoffset)
    
    def get_yoffset(self):
        return self.parent.y
    yoffset = property(get_yoffset)
    
    def __init__(self, value="", scale=1, parent=None, drawer=None,
                 on_color="#00ff00", off_color="#002200", bg_color="#000000"):
        super(Elem, self).__init__()
        self.scale = scale
        self.parent = parent
        self.drawer = drawer
        self.on_color = on_color
        self.off_color = off_color
        self.bg_color = bg_color
        self.rescale()
        self.value = value
    
    def move(self, pts):
        xoffset = self.xoffset
        yoffset = self.yoffset
        return [(a + xoffset, b + yoffset) for a, b in pts]
        
    def redraw(self, old=None):
        if self.drawer is None:
            return
        if old is None:
            old = self.polys.keys()
            for i in old:
                self.drawer.poly(self.move(self.polys[i]),
                                 self.on_color)
        for i in [i for i in old if i not in self.llcd[self.value]]:
            self.drawer.poly(self.move(self.polys[i]), self.off_color)
        for i in [i for i in self.llcd[self.value] if i not in old]:
            self.drawer.poly(self.move(self.polys[i]), self.on_color)
    
    def set_drawer(self, drawer):
        self.drawer = drawer
        self.redraw()

    def rescale(self):
        scale = self.scale
        self.polys = {}
        for key, value in self.shape.iteritems():
            self.polys[key] = [(a * scale, b * scale) for a, b, in value]

class Digit(Elem):
    
    idkey = "#"
    values = "0123456789E+-* "

    #    -      b
    #   |,|    ahc
    #    -      g
    #   |'|    fid
    #    -      e
    # polygon coordinates for height=50    
    shape = {
        'a' : (( 1, 6), ( 3, 4), ( 5, 6), ( 5,16), ( 3,18), ( 1,16)),
        'b' : (( 6, 1), (16, 1), (18, 3), (16, 5), ( 6, 5), ( 4, 3)),
        'c' : ((17, 6), (19, 4), (21, 6), (21,16), (19,18), (17,16)),
        'd' : ((17,22), (19,20), (21,22), (21,32), (19,34), (17,32)),
        'e' : (( 6,33), (16,33), (18,35), (16,37), ( 6,37), ( 4,35)),
        'f' : (( 1,22), ( 3,20), ( 5,22), ( 5,32), ( 3,34), ( 1,32)),
        'g' : (( 6,17), (16,17), (18,19), (16,21), ( 6,21), ( 4,19)),
        'h' : (( 9,16), ( 9,14), (11,12), (13,14), (13,16)),
        'i' : (( 9,22), ( 9,24), (11,26), (13,24), (13,22)),
    }

    # llcd defines a list of segments to turn on for any particular symbol.
    llcd = {
        '0' : "abcdef",
        '1' : "cd",
        '2' : "bcefg",
        '3' : "bcdeg",
        '4' : "acdg",
        '5' : "abdeg",
        '6' : "abdefg",
        '7' : "bcd",
        '8' : "abcdefg",
        '9' : "abcdeg",
        '-' : "g",
        '+' : "ghi",
        'E' : "abefg",
        ' ' : "",
        '*' : "abcdefghi"
    }


class Point(Elem):

    idkey = "."
    values = ". "
    
    shape = {"a" : (( 1,37), ( 1,33), ( 5,33), ( 5,37))}

    llcd = {"." : "a", " " : ""}
    

class DPoint(Elem):
    

    idkey = ":"
    values = ": "
    
    shape = {"a" : (( 1,16), ( 1,12), ( 5,12), ( 5,16)),
             "b" : (( 1,26), ( 1,22), ( 5,22), ( 5,26))}
    
    llcd = {":" : "ab", " " : ""}


class Segments(list):
    
    def get_values(self):
        return [x.value for x in self]

    def set_values(self, values):
        for elem, value in zip(self, values):
            elem.value = value
    
    values = property(get_values, set_values)
    
    def get_fmt(self):
        return self.__fmt
    fmt = property(get_fmt)
    
    def get_x(self):
        return self.__x
    x = property(get_x)

    def get_y(self):
        return self.__y
    y = property(get_y)

    def get_width(self):
        return sum([elem.width for elem in self])
    width = property(get_width)
    
    def get_height(self):
        return max([elem.height for elem in self])
    height = property(get_height)
    
    def __init__(self, fmt, values, drawer=None, scale=1, x=0, y=0,
                 on_color="#00ff00", off_color="#002200", bg_color="#000000"):
        super(Segments, self).__init__()
        self.__x = x
        self.__y = y

        self.__fmt = fmt
        self.drawer = drawer
        self.bg_color = bg_color
        for key, value in zip(fmt,values):
            self.append(Elem.create(key, value, scale, self, drawer,
                                    on_color, off_color, bg_color))
    def redraw(self):
        self.drawer.rect(self.x, self.y,
                         self.width, self.height,
                         self.bg_color)
        for elem in self:
            elem.redraw()

    def calc_size(fmt, scale):
        return Elem.calc_size(fmt, scale)
    calc_size = staticmethod(calc_size)

und hier ein Beispiel mit gtk2:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
Modul:          gtk_clock
Description:    Clock with big sevensegmentdisplay
Version:        0.1
Copyright:      2004 by Fritz Cizmarov fritz@sol.at
Created:        01. Jän. 2005
Last modified:  01. Jän. 2005
License:        free
Requirements:   Python2.3
"""

import time

import pygtk
pygtk.require('2.0')
import gtk
import gobject

from SevenSegs import Drawer, Segments


class gtkDrawer(Drawer):
    """ interface zu draw filled polygons on an drawable """
    def __init__(self, drawable, colormap):
        self.drawable = drawable
        self.colormap = colormap
        self.gc = drawable.new_gc()
        black = self.colormap.alloc_color("#000000")
        self.colors = {"#000000" : black}

    def poly(self, points, color):
        """ draw the Poly with pointlist points and color """
        if color not in self.colors:
            self.colors[color] = self.colormap.alloc_color(color)
        self.gc.set_foreground(self.colors[color])
        self.drawable.draw_polygon(self.gc, gtk.TRUE, points)

    """
    def rect(self, x, y, w, h, color):
        if color not in self.colors:
            self.colors[color] = self.colormap.alloc_color(color)
        self.gc.set_foreground(self.colors[color])
        self.drawable.draw_rectangle(self.gc, gtk.TRUE, x, y, w, h)
    """

        
class APP(object):

    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("delete_event", self.delete_event)
        self.window.connect("destroy", self.destroy)
        self.window.set_resizable(gtk.FALSE)
        self.window.set_position(gtk.WIN_POS_CENTER)

        self.scale = 10
        self.drawing_area = gtk.DrawingArea()
        sc_width = gtk.gdk.screen_width()
        while self.scale:
            x, y = Segments.calc_size("##:##:##", self.scale)
            if x+20 < sc_width:
                break
            self.scale -= 1
        width = x + self.scale*4
        height = y + self.scale*4
        self.drawing_area.set_size_request(width, height)
        self.drawing_area.connect("expose-event", self.area_expose_cb)
        self.drawing_area.show()
        self.window.add(self.drawing_area)
        self.window.show()
        gobject.timeout_add(1000, self.do_clock)

    def area_expose_cb(self, area, event):
        if not hasattr(self, "segments"):
            self.drawer = gtkDrawer(area.window, area.get_colormap())
            xoffset = self.scale*2
            yoffset = self.scale*2
            self.segments = Segments("##:##:##", "00:00:00", self.drawer,
                                     self.scale, xoffset, yoffset)
        w, h = self.drawer.drawable.get_size()
        self.drawer.rect(0, 0, w, h, "#000000")
        self.segments.redraw()
            
    def delete_event(self, widget, event, data=None):
        #print "delete_event occured"
        return gtk.FALSE
        
    def destroy(self, widget, date=None):
        gtk.main_quit()

    def do_clock(self):
        if hasattr(self, "segments"):
            self.segments.values = time.strftime("%H:%M:%S")
        return True

    def main_loop(self):
        gtk.main()


app = APP()
app.main_loop()

Wie zu sehen ist, muss in der Anwendung nur eine Klasse als Interface zum Zeichnen von Polygonen definiert werden. Die Instanz die mit dieser Klasse erzeugt wird, enthält dann alle Informationen um in einem Widget die Segmente zu zeichnen. Auch ein Erweitern um zusätzliche Elemente ist durch Verwendung einer Metaklasse für die Segmentelemente sehr einfach.

Vielleicht hat ja wer lust, noch Beispiele für gt oder wxwidgets oder ... zu machen.


Gruß und gutes neues Jahr,

Dookie
Edit, kleine Änderungen am Code
Zuletzt geändert von Dookie am Sonntag 2. Januar 2005, 00:29, insgesamt 3-mal geändert.
[code]#!/usr/bin/env python
import this[/code]
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

Samstag 1. Januar 2005, 19:46

Hi!

Ich kann nur sagen: geschummelt!! Das sind ja mehr als 7 Segmente :D
Nein ernsthaft, sieht interessant aus. Muß mich aber erst durch den Code wühlen :wink:

Ach ja, ein Problem. Ich bekomm folgende Fehlermeldung:
Python hat geschrieben: Traceback (most recent call last):
File "7_gtk.py", line 103, in ?
app = APP()
File "7_gtk.py", line 60, in __init__
sc_width = self.window.get_screen().get_width()
AttributeError: 'gtk.Window' object has no attribute 'get_screen'
Kenn mich mit gtk leider noch nicht wirklich aus. Was mach ich da, bzw. was mach ich falsch?.
(Hab mal zu Testzwecken sc_width einfach auf einen fixen Wert gesetzt.)

Gruß, mawe
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Samstag 1. Januar 2005, 21:18

Hi mawe,

hmm da hast du wohl eine zu alte gtk Version.

Code: Alles auswählen

>>> print gtk.gtk_version
(2, 4, 14)
ists bei mir. Laut Doku steht gtk.window.get_screen() erst ab gtk_version 2.2 zur Verfügung. Mit folgender Änderung gehts auch mit mit < 2.2 :)

Code: Alles auswählen

        sc_width = gtk.gdk.screen_width()

Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Sonntag 2. Januar 2005, 02:16

hab noch eine Pygame-clock gemacht

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
    Modul:          pygame_clock
    Description:    Beschreibung
    Version:        0.1
    Copyright:      2004 by Fritz Cizmarov fritz@sol.at
    Created:        02. Jän. 2005
    Last modified:  02. Jän. 2005
    License:        free
    Requirements:   Python2.3
    Exports:        Classes and Functions to export
"""

import time
import pygame

from SevenSegs import Segments, Drawer

class pgDrawer(Drawer):
    def __init__(self, surface):
        self.surface = surface

    def poly(self, points, color):
        """ extract RGB-values from string of format #RRGGBB """
        col = int(color[1:3],16), int(color[3:5],16), int(color[5:7],16)
        pygame.draw.polygon(self.surface, col, points)


pygame.init()

maxw, maxh =  pygame.display.list_modes()[0]
scale = 10
while scale:
    width, height = Segments.calc_size("##:##:##",scale)
    if width + 20 < maxw:
        break
    scale -= 1

size = (width + scale*4, height + scale*4)
screen = pygame.display.set_mode(size)
surface = pygame.display.get_surface()
drawer = pgDrawer(surface)
xoffset = scale*2
yoffset = scale*2
segments = Segments("##:##:##", "00:00:00", drawer, scale, xoffset, yoffset)

pygame.time.set_timer(pygame.USEREVENT, 1000)

while True:
    event = pygame.event.wait()
    if event.type == pygame.QUIT:
        break
    else:
        surface.lock()
        segments.values = time.strftime("%H:%M:%S")
        surface.unlock()
        pygame.display.flip()
Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

Sonntag 2. Januar 2005, 07:20

Hi Dookie!

Wegen der Fehlermeldung hattest Du recht, lag an der gtk-Version.
Zu dem pygame-Beispiel: sollte es nicht

Code: Alles auswählen

while scale:
    width, height = Elem.calc_size("##:##:##",scale)
heissen?

Gruß, mawe
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Sonntag 2. Januar 2005, 13:40

Hi mawe,

ich hatte oben meinen SevenSegs-Code dahingehend ergänzt, daß ich Segments eine staticmethode calc_size für die berechnung der Grösse des Displays verpasst habe. Erscheint mir sinnvoller das in Segments zu machen als in Elem, wobei die alte Version über Elem auch weiterhing funktioniert.


Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dienstag 4. Januar 2005, 10:21

Du hast nen Typo im ersten Code: indipend sollte es nicht independent lauten? Sorry, dass ich jetzt grad nichts sinnvolleres beizusteuern habe, ich bin gerade an einem Computer ohne Python Interpreter (kann man das noch Computer nennen?) :(
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Montag 17. Januar 2005, 14:10

Oha, ich habe mir jetzt die GTK+ Version etwas unter die Lupe genommen: super, da kann man richtig was zum Drawing Area lernen! (Außerdem schaut es gut aus) Werde ich noch weiterverfolgen, vielleicht schaffe ich ja etwas Turtle ähnliches.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Antworten