WCK/Tkinter 3000: Tkinter-Widgets selber schreiben

Gute Links und Tutorials könnt ihr hier posten.
Antworten
schlangenbeschwörer
User
Beiträge: 419
Registriert: Sonntag 3. September 2006, 15:11
Wohnort: in den weiten von NRW
Kontaktdaten:

Hallo!
Ich hab mich jetzt mal etwas mit dem WCK von effbot beschäftigt und möchte es hier mal mit einem Beispielscript vorstellen.
Als einfaches Beispiel hab ich ein RasterFrame geschrieben, mit dem man 2d-Arrays anzeigen kann. An einem Anwendungsbeispiel, das den tieferen Sinn dieses Widgets zeigt, arbeite ich noch.

Code: Alles auswählen

from WCK import Widget, EventMixin

class RasterFrame(EventMixin, Widget):
    """Widget zum darstellen und ändern von Arrays.
werte = {0:("white", "black"), i: ("color", bd-color")}: Zuordnung von Farben zu den vorkommenden Werten
array = [[1,2,0,0,3],[1,2,0,1],[i,i,i,i],[...]]: 2d-Array (Liste), das anfangs dargestellt wird
editierbar = True: Bool, ob das array manuell (per Mausklick) geändert werden kann"""

    ui_option_werte = {0 : ("white", "black"), 1:("black", "white")}
    ui_option_array = [[1,0],[0,1]]
    ui_option_editierbar = True

    ui_option_height = 200
    ui_option_width = 200
    
    ui_doublebuffer = 1
    
#    def __init__(self, master, **options):
#        self.ui_init(master, options)

    def ui_handle_config(self):
        self.werte = self.ui_option_werte
        self.array = self.ui_option_array
        self.editierbar = self.ui_option_editierbar
        self.height, self.width = self.ui_option_height, self.ui_option_width
        return int(self.ui_option_width), int(self.ui_option_height)

    def ui_handle_resize(self, width, height):
        self.width, self.height = width, height

    def ui_handle_clear(self, draw, x0, y0, x1, y1):
        pass
    
    def ui_handle_repair(self, draw, x0, y0, x1, y1):
        xeinheit = self.width / len(self.array[0])
        yeinheit = self.height / len(self.array)
        for i1, x in enumerate(self.array):
            for i2, y in enumerate(x):
                x1_, y1_ = i1 * xeinheit, i2 * yeinheit
                color = self.werte[y]                
                draw.rectangle((x1_, y1_, x1_ + xeinheit, y1_ + yeinheit),self.ui_pen(color[1]),\
                               self.ui_brush(color[0]))

    def update(self, x, y, wert):
        "Aktuallisiert zum zeitsparen nur ein Feld."
        xeinheit = self.width / len(self.array[0])
        yeinheit = self.height / len(self.array)
        x1, y1 = x * xeinheit, y * yeinheit
        x2, y2 = x1 + xeinheit, y1 + yeinheit
        self.array[x][y] = wert
        color = self.werte[wert]
        self.ui_draw.rectangle((x1,y1,x2,y2), self.ui_pen(color[1]), self.ui_brush(color[0]))

    def onclick(self, event):
        """Bindet Mausklicks: Je nach Taste nimmt das Feld
         die nächste oder vorherige Farbe an. Dazu wird 
         entsprechend das array geändert."""
        if self.editierbar:
            x, y = event.x, event.y
            xeinheit = self.width /len(self.array[0])
            yeinheit = self.height /len(self.array)
            i1, i2 = x / xeinheit, y / yeinheit
            keys = sorted(self.werte.keys())
            wert = self.array[i1][i2]
            index = keys.index(wert)
            if event.num == 1:
                if (index - 1) < 0:
                    index = len(keys) 
                self.update(i1, i2, keys[index - 1])
            elif event.num == 3:
                if (index + 1) > len(keys) - 1:
                    index = -1
                self.update(i1, i2, keys[index + 1])
Zunächst muss man sich bei effbot das WCK-Packet runterladen und installieren. Ich hab das einfach mit der Windows-.exe gemacht.
Im scripts-Ordner der Pythoninstallation findet man einige Beispielscripte, die zeigen, "was alles geht".

Um eine eigene Widgetklasse zu schreiben, muss man zunächst von WCK.Widget erben. Das ist die Standartklasse, die alles nötige wie zB. die Geometriemanager usw. enthält.
Dann werden die Attribute als Klassenattribute der Form ui_option_optionname = defaultwert festgelegt. Eine __init__-Methode ist nicht umbedingt nötig.
Als letztes muss die "ui_handle_repair"-Methode definiert werden. Diese ist sozusagen das Zentrum des Widgets, da es hier erzeugt wird. Als Argumente werden "draw" und 4 Punkte übergeben.
Die 4 Punkte beschreiben die Ecken des Widgets (nw und se). Über "draw" kann man direkt auf die Widgetfläche Zeichnen und schreiben, ähnlich wie beim Tkinter.Canvas. Immer, wenn das Widget neu aufgebaut wird, also bei Größenänderung, etc. wird diese Methode aufgerufen.
Zuvor wird das Widget jedoch "geleert". Dazu wird die Methode "ui_handle_clear" aufgerufen, die normalerweise die Widgetfläche mit der Hintergrundfarbe füllt. Wenn man in der repair-Methode aber eh alles neu "malt", kann man durch deaktivierung der clear-Methode etwas Zeit sparen.
Mit weiteren Methoden wie den im Bsp. genutzten kann man interne Events abfangen und verarbeiten.

Richtige Events, also Mausklicks usw. kann man mit verschiedenen Klassen handeln. Recht einfach ist es, die Widgetklasse auch von "EventMixin" erben zu lassen, und die entsprechenden Methoden, die bei Events aufgerufen werden, zu ändern.

Hiermit kann man erstmal sehen, wie die Klasse von oben "aussieht":

Code: Alles auswählen

def maintest(what):
    t = tk.Tk()
    a1 = [[0,1,0,1],
            [1,0,1,0],
            [0,1,0,1],
            [1,0,1,0]]
    w1 = {0 : ("white", "black"), 1:("black", "white")}
    a2 = [[1,2,3],
             [2,3,1],
             [3,1,2]]
    w2 = {1: ("blue", "white"), 2: ("red", "white"), 3:("green", "white")}
    if what == "schachbrett":
        r = RasterFrame(t, werte = w1 , array = a1)
    elif what == "farbenfroh":
        r = RasterFrame(t, werte = w2 , array = a2)
    r.pack(expand = 1, fill = "both")
    r.bind("<Any-Button>", eventhandler)
    t.mainloop()
def eventhandler(event):
    for line in event.widget.array:
        print line
    print "-" * 6
        
if __name__ == "__main__":
# eins auswählen:
#    maintest("schachbrett")
    maintest("farbenfroh")    
Erster Eindruck:
Mit dem WCK kann man echt viel machen, was man, wenn überhaupt, sonst nur aufwändig aus bereits bestehenden Widgets machen kann.
Ein sehr großes Problem ist jedoch, das es erst recht wenig Doku gibt, und es so etwas schwer fällt, alle Möglichkeiten kennenzulernen.
Es kommt aber auf jeden Fall was professionelles bei raus.

LG, jj
Benutzeravatar
Mr_Snede
User
Beiträge: 387
Registriert: Sonntag 8. Februar 2004, 16:02
Wohnort: D-Dorf, Bo

Super Sache sowas. Habe es selbst nie geschafft mich damit auseinander zu setzen. Leider scheiterte eine spontane Installation vom WCK bei mir auf debian. Vielleicht setze ich mich ja mal mit etwas mehr zeit dran...
schlangenbeschwörer
User
Beiträge: 419
Registriert: Sonntag 3. September 2006, 15:11
Wohnort: in den weiten von NRW
Kontaktdaten:

ja, ich finde, es lohnt sich auf jeden Fall. Leider ist es noch so neu und unverbreitet, das es keine große Doku und nicht viele andere Leute die es nutzen (und die man fragen könnte) gibt. Außerdem muss ja auch jeder, der das Script laufen lassen will, erstmal das Kit installieren, was manche vlt. auch abhält. Am besten wär es ja, wenn man mit dem WCK die Widgets schreiben könnte und sie dann irgedwie so kompilieren könnte, dass sie bei jedem Laufen, also nur die Standardsachen brauchen, aber das geht wohl nicht...
Wie ich jedoch schon gesagt hab, kann man die WCK-Widgets allgemeiner formulieren, sodass bei organisierter Verbreitung dieser das vorgandene Widgetpacket immer weiter wächst, sodass man nur das entsprechende suchen muss und dann nimmt, ohne was dran machen zu müssen (oder nur ganz wenig).
Antworten