Workshop: Gui Entwicklung in tkinter

Fragen zu Tkinter.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

stefanxfg hat geschrieben:Hallo Alfons, ich denke Blackjack hat recht. Das heißt nicht, dass der GUIDesigner nicht gut ist. Du musst das eben anfangen, dass was bei dir oder einem anderen Editor automatisch passiert.
Ja stimmt, ich habe mir das unter windows noch gar nicht angeschaut. Sondern nur in Linux. Unter Windows wird anscheinend cp1252 gschgrieben. Da paßt natürlich der Kommentar nicht.

Da muß ich, wie Blackjack bereits angemerkt hatte, bei den paar Stellen, wo ich speichere, dafür sorgen, dass da in UTF-8 geschrieben wird. Und gut wäre, wenn die Script-Files auch alle utf-8 wären, obwohl das in Python3 nichts ausmacht, solange man nicht editieren will. Naja ist nur eine Kleinigkeit. Aber man merkt es eben nicht, wenn man nur in Linux gearbeitet hat.

Das mache ich gleich.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Man findet of das, was hier in der ersten Zeile steht:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
Sieht aus, wie ein Linux Verzeichnis. Hat das eine Bedeutung?
BlackJack

@Alfons Mittelmeyer: Dadurch weiss Linux wie es die Datei ausführen soll. Wenn man die Datei mit ``chmod`` als ausführbar kennzeichnet, dann wird das nach dem ``#!`` genommen und der Dateiname hinten angehängt und dann das ganze ausgeführt. Also wenn das in einer Datei mit dem Namen ``hallo.py`` steht, dann würde bei der Eingabe von ``./hallo.py`` in dem Verzeichnis in einer Shell ``/usr/bin/env python hallo.py`` ausgeführt. Dadurch kann man in Linux jede Textdatei als Programm ausführen und sagen welches Programm die Textdatei interpretieren soll. Das Programm muss dann nur diese erste Zeile ignorieren, entweder explizit, oder weil das ``#`` sowieso als Kennzeichen für eine Kommentarzeile behandelt wird.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

BlackJack hat geschrieben:@Melewo: Doch, ziemlich sicher verwechselst Du ANSI und ASCII. Denn das was Du schreibst gilt für ASCII, aber nicht für ANSI. ASCII ist von den fast allen Zeichenkodierungen die wir hier im Westen benutzen eine Untermenge. Unter anderem ist ASCII auch von ANSI und von UTF-8 und von allen ISO-8859-*-Kodierungen und vielen Windows-Codepages eine Untermenge. ANSI ist dagegen weder von UTF-8 noch von den ISO-8859-*-Kodierungen eine Untermenge. Du kannst also nicht als ANSI speichern und problemlos als UTF-8 oder ISO-8859-irgendwas öffnen. Das geht aber mit ASCII.
Gut, dann dürfte es am Editor liegen, dass der solange in einer Datei nur Zeichen entsprechend ASCII enthalten sind, diese mit ANSI anzeigt, obwohl eigentlich ASCII zutreffend sein sollte. Jedenfalls gab es noch nie Probleme damit, weil ein Parser oder Interpreter die enthaltenen Codes richtig abarbeitet und allenfalls die Ausgabe oder Speicherung in einer DB von betroffen sein könnte. Bei einer Verbindung zur Datenbank muss ich nur einen Charset setzen, bei Regex war es auch mit Umlauten so.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

stefanxfg hat geschrieben:Hallo Alfons, ich denke Blackjack hat recht. Das heißt nicht, dass der GUIDesigner nicht gut ist. Du musst das eben anfangen, dass was bei dir oder einem anderen Editor automatisch passiert.
Habe jetzt utf-8 Codierung bei allen read und write Dateioperationen implementiert. Ist schon auf GitHub. Damit dürften die Probleme unter Windows beseitigt sein.
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Der GuiDesigner legt ja für jeden Container eine neue Klasse an. Wenn ich nun aus dem call_code eine Funktion, sagen wir über eine Buttonbetätigung aus einer Toolbbar, anspreche, möchte ich Hauptframes ein- und ausblenden.
Aber ehrlich gesagt bin ich zu faul um jeden Frame für alle Klassen anprechbar zu machen. Wäre das dann besser über Eventbroker zu bewerkstelligen?

Zur Verdeutlichung:
Buttonbetätigung aus Toolbar => liegt in einer Datei
Input in einem Hauptframe => liegt in anderer Datei
Output in einem Hauptframe => nochaml andere Datei
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

stefanxfg hat geschrieben:Aber ehrlich gesagt bin ich zu faul um jeden Frame für alle Klassen anprechbar zu machen. Wäre das dann besser über Eventbroker zu bewerkstelligen?
Das wäre auch völlig falsch. Da würdest Du ja den Code verflechten. Früher gab es mal im Auto ein Kabelgewirr. Heute erfolgt alles ordentlich über gemeinsame Busse.

Da fangen wir einmal an mit einem Schalter. Der Schalter sollte eine funktionelle Einheit sein, und überall verwendet werden können. Um natürlich etwas bewirken zu können, muss er angeschlossen werden.

Das ist die GUI für den Schalter (inklusive App):

Code: Alles auswählen

# -*- coding: utf-8 -*-

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

#import DynTkInter as tk # for GuiDesigner

import switch_code

# Application definition ============================

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.minsize(800, 100)
        # widget definitions ===================================
        self.switch = Switch(self,name='#0_switch')
        self.switch.pack(fill='both', expand=1)

class Switch(tk.Frame):

    def __init__(self,master,**kwargs):
        tk.Frame.__init__(self,master,**kwargs)
        self.myclass = 'Switch'
        # widget definitions ===================================
        self.canvas = Canvas(self,name='#1_canvas')
        self.canvas.place(x=0,y=0)

        # call Code ===================================
        switch_code.switch_init(self)

class Canvas(tk.Canvas):

    def __init__(self,master,**kwargs):
        tk.Canvas.__init__(self,master,**kwargs)
        self.config(height='100', relief='raised', highlightbackground='#e8e9ee', insertwidth='0', bg='#615e58', bd='10', selectborderwidth='0', highlightthickness='10', width='100', highlightcolor='lightgray')
        self.start_width = 100
        # widget definitions ===================================
        coords = (30,30,110,110)
        item = self.create_oval(*coords)
        self.itemconfig(item,width = '0.0',fill = '#ff7431',tags = 'led')


if __name__ == '__main__':
    #Application().mainloop('guidesigner/Guidesigner.py') # for GuiDesigner
    Application().mainloop()
Und das ist der Code:

switch_code.py

Code: Alles auswählen

def switch_init(self):

    # for connection =========================
    self.eventbroker = None
    self.message = None
    # ========================================

    # bindings ===============================
    
    def width_height(event):
        frame_height = event.height

        bd = frame_height/14
        canvas_height = frame_height-4*bd
        scale_factor = canvas_height/self.canvas_height_before
        self.canvas_height_before = canvas_height
        self.canvas.config(width = canvas_height,height = canvas_height,bd = bd,highlightthickness=bd)
        self.canvas.scale('led',0,0,scale_factor,scale_factor)

    def switch(event):
        self.on = not self.on
        self.canvas.itemconfig('led',fill = 'lightgreen' if self.on else '#ff7431')
        self.canvas['relief'] = 'sunken' if self.on else'raised'
        if self.eventbroker:
            self.eventbroker.publish(self.message,self.on)

    self.canvas_height_before = 100
    self.bind('<Configure>',width_height,'+')
    self.on = False
    self.canvas.bind('<Button-1>',switch)
Dieser Switch Schalter ist überall verwendbar für eine horizontale Toolbar, da er sich der Höhe anpasst und außerdem nicht selber auf irgendetwas außerhalb zugreift, außer man schließt ihn an.

Hierfür sind in Zeile 4 und 5 zwei Attribute vorgesehen, über die man ihn anschließen kann.

@stefanxfg: Frage, soll dieser Schalter in der Toolbar den Button beim Toggleframe ersetzten? Wenn Ja, dann könnte man ihn einmal anschließen.
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Hallo Alfons, nein. Der CHeckbutton im ToggleFrame ist nur für das Einklappen der SubFrames verantwortlich. Demnach kann er sogar lokal gesetzt werden.
Der Switch soll von der Toolbar aus gesteuert werden. Demnach von einer andere Datei aus.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@stefanxfg: Die Logik mit eventbroker ist doch ganz einfach.

Zuvor habe ich nochmals mir das Modul communication angeschaut. Es ist doch überflüssig, hier noch dein Modul zu importieren. Da sollte man doch auch gleich den event broker mit reinmachen:

communication.py:

Code: Alles auswählen

import sys
def output(param):
    sys.stdout.write(param+'\n')

class EventBroker():
    def __init__(self):
        self._dictionary_ = {}
 
    def subscribe(self,message_id,callback):
        if message_id in self._dictionary_:
            output('EventBroker: callback already defined for message id: {}\nThe callback before will be overwritten'.format(message_id))

        self._dictionary_[message_id] = callback
 
    def publish(self,message_id,*args,**kwargs):
        if message_id not in self._dictionary_:
            output('EventBroker: no callback defined for message: {},*{},**{}'.format(message_id,args,kwargs))
        else:
            self._dictionary_[message_id](*args,**kwargs)


eventbroker = EventBroker()
publish = eventbroker.publish
subscribe = eventbroker.subscribe
Dann denke ich mir. dass zuerst die Eingabe angezeigt werden soll und da ist der Schalder noch auf aus. Gedrückter Schalter heißt also 'AUSGABE_EIN'. Deshalb habe ich die Message mal so genannt. DAnn habe ich gesehen, dass der Code Aufruf bei der Eingage groß geschrieben war und 'InputCode' heißt. Dann wird das wohl eine Klasse sein und für die Ausgabe wird es wohl dann 'OutpuCode' heißen?

Jedenfalls wäre das dann die Lösung:

testcode.py

Code: Alles auswählen

from communication import eventbroker, publish, subscribe

class InputCode:

    def __init__(self,container):
        self.container = container
        subscribe('AUSGABE_EIN',self.lift_eingabe)

    def lift_eingabe(self,ausgabe_ein):
        if not ausgabe_ein:
            self.container.lift()

class OutputCode:

    def __init__(self,container):
        self.container = container
        subscribe('AUSGABE_EIN',self.lift_ausgabe)

    def lift_ausgabe(self,ausgabe_ein):
        if ausgabe_ein:
            self.container.lift()
Ach so, Du hattest mir mitgeteilt, bei Dir hätte lift nicht funktioniert. Du mußt für Eingabe und Ausgabe dieselbe Grid Zelle nehmen und keine andere Zeile oder Spalte, sonst kann es natürlich nicht funktionieren.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Das wäre dann ein sichtbares Beispiel für Deine InputGUI. Natürlich toggelt da jetzt nichts.

AppInput.py

Code: Alles auswählen

# -*- coding: utf-8 -*-

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

#import DynTkInter as tk # for GuiDesigner


#============= imports call Code ===================
import testcode

# Application definition ============================

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.minsize(300, 300)
        # widget definitions ===================================
        self.InputContentGUI = InputGUI(self,name='#0_InputContentGUI')
        self.InputContentGUI.pack(expand=1, fill='both')

class InputGUI(tk.Frame):

    def __init__(self,master,**kwargs):
        tk.Frame.__init__(self,master,**kwargs)
        self.myclass = 'InputGUI'
        self.call_code = 'testcode.InputCode'
        self.config(relief='solid', bd=4)
        # widget definitions ===================================
        self.sub_frameA = tk.Frame(self,name='#1_sub_frameA',height=40, bg='#ffffd3')
        self.sub_frameB = tk.Frame(self,name='#2_sub_frameB',height=40, bg='#e4f3fc')
        self.sub_frameC = tk.Frame(self,name='#3_sub_frameC',relief='sunken', width=50, bg='#cbeed8')
        self.sub_frameA.pack(fill='x')
        self.sub_frameB.pack(fill='x')
        self.sub_frameC.pack(expand=1, fill='both', side='left')
        # call Code ===================================
        testcode.InputCode(self)

if __name__ == '__main__':
    #Application().mainloop('guidesigner/Guidesigner.py') # for GuiDesigner
    Application().mainloop()
Für die OutputGUI hast Du kein eigenes Modul spendiert. Warum nicht, machen wir es doch schön symmetrisch.

Dann ist das:

AppOutput.py

Code: Alles auswählen

# -*- coding: utf-8 -*-

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

#import DynTkInter as tk # for GuiDesigner


#============= imports call Code ===================
import testcode

# Application definition ============================

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.minsize(400, 400)
        # widget definitions ===================================
        self.OutputContentGUI = OutputGUI(self,name='#0_OutputContentGUI')
        self.OutputContentGUI.pack(fill='both', expand=1)

class OutputGUI(tk.Frame):

    def __init__(self,master,**kwargs):
        tk.Frame.__init__(self,master,**kwargs)
        self.myclass = 'OutputGUI'
        self.call_code = 'testcode.OutputCode'
        self.config(bd=4, bg='#ffeb00', relief='solid')
        # call Code ===================================
        testcode.OutputCode(self)

if __name__ == '__main__':
    #Application().mainloop('guidesigner/Guidesigner.py') # for GuiDesigner
    Application().mainloop()
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Der Toolbar Code ist sehr einfach:

testcode.py

Code: Alles auswählen

from communication import eventbroker, publish, subscribe

class InputCode:

    def __init__(self,container):
        self.container = container
        subscribe('AUSGABE_EIN',self.lift_eingabe)

    def lift_eingabe(self,ausgabe_ein):
        if not ausgabe_ein:
            self.container.lift()

class OutputCode:

    def __init__(self,container):
        self.container = container
        subscribe('AUSGABE_EIN',self.lift_ausgabe)

    def lift_ausgabe(self,ausgabe_ein):
        if ausgabe_ein:
            self.container.lift()

def ToolbarCode(self):
    self.schalter1.connect(eventbroker,'AUSGABE_EIN',False)
Es braucht hier nur der Schalter verbunden werden.

Nachdem der led_switch fertig ist:

led_switch.py

Code: Alles auswählen

# -*- coding: utf-8 -*-

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

#import DynTkInter as tk # for GuiDesigner

# Application definition ============================

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.minsize(800, 100)
        # widget definitions ===================================
        self.switch = Switch(self,name='#0_switch')
        self.switch.pack(fill='both', expand=1)

class Switch(tk.Frame):

    def __init__(self,master,**kwargs):
        tk.Frame.__init__(self,master,**kwargs)
        self.myclass = 'Switch'
        # widget definitions ===================================
        self.canvas = Canvas(self,name='#1_canvas')
        self.canvas.place(x=0,y=0)

        # fitting canvas to frame height and enable switching =====
        self.canvas_height_before = 100
        self.bind('<Configure>',self.width_height,'+')
        self.on = False
        self.eventbroker = None
        self.message = None
        self.canvas.bind('<Button-1>',self.switch)

    # for connection and state at beginning ============
    def connect(self,eventbroker,message,on = False):
        self.on = on
        self.eventbroker = eventbroker
        self.message = message
        self.switch_update()
    
    def width_height(self,event):
        frame_height = event.height
        frame_width = event.width
        frame_size = min(event.height,event.width)
        bd = frame_size/14
        canvas_height = frame_size-4*bd
        scale_factor = canvas_height/self.canvas_height_before
        self.canvas_height_before = canvas_height
        self.canvas.config(width = canvas_height,height = canvas_height,bd = bd,highlightthickness=bd)
        self.canvas.scale('led',0,0,scale_factor,scale_factor)
        x = (frame_width - frame_size)/2
        y = (frame_height - frame_size)/2
        self.canvas.place(x = x, y = y)

    # show and signal on status =========================
    def switch_update(self):
        self.canvas.itemconfig('led',fill = 'lightgreen' if self.on else '#ff7431')
        self.canvas['relief'] = 'sunken' if self.on else'raised'
        if self.eventbroker:
            self.eventbroker.publish(self.message,self.on)

    def switch(self,event):
        self.on = not self.on
        self.switch_update()

class Canvas(tk.Canvas):

    def __init__(self,master,**kwargs):
        tk.Canvas.__init__(self,master,**kwargs)
        self.config(height='100', relief='raised', highlightbackground='#e8e9ee', insertwidth='0', bg='#615e58', bd='10', selectborderwidth='0', highlightthickness='10', width='100', highlightcolor='lightgray')
        self.start_width = 100
        # widget definitions ===================================
        coords = (30,30,110,110)
        item = self.create_oval(*coords)
        self.itemconfig(item,width = '0.0',fill = '#ff7431',tags = 'led')

if __name__ == '__main__':
    #Application().mainloop('guidesigner/Guidesigner.py') # for GuiDesigner
    Application().mainloop()
Kann er leicht in die Toolbar eingebunden werden:

AppToolbar.py

Code: Alles auswählen

# -*- coding: utf-8 -*-

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

#import DynTkInter as tk # for GuiDesigner


#============= imports baseclass ===================
import led_switch

#============= imports call Code ===================
import testcode

# Application definition ============================

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.minsize(400, 100)
        # widget definitions ===================================
        self.ToolbarGUI = Toolbar(self,name='#0_ToolbarGUI')
        self.ToolbarGUI.pack(fill='both', expand=1)

class Toolbar(tk.Frame):

    def __init__(self,master,**kwargs):
        tk.Frame.__init__(self,master,**kwargs)
        self.myclass = 'Toolbar'
        self.call_code = 'testcode.ToolbarCode'
        # widget definitions ===================================
        self.schalter1 = Schalter1(self,name='#1_schalter1')
        self.schalter1.place(relwidth='0.2', height='-20', x='10', y='10', relheight='1')
        # call Code ===================================
        testcode.ToolbarCode(self)

class Schalter1(led_switch.Switch):

    def __init__(self,master,**kwargs):
        led_switch.Switch.__init__(self,master,**kwargs)
        self.myclass = 'Schalter1'
        self.baseclass = 'led_switch.Switch'

if __name__ == '__main__':
    #Application().mainloop('guidesigner/Guidesigner.py') # for GuiDesigner
    Application().mainloop()
Der Schalter tut schon, man sieht es am Konsolen Output. Dann bräuchte man nur noch alles zusammenbauen
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Oh, es gibt da noch ein Problem. Dieser Eventbroker ist nicht für mehre Empfänger ausgerüstet. Da müßte man ihn zuvor aufrüsten. Ich wollte ihn einfach halten und dabei vermeiden, dass etwa bei Widgets Löschen und wieder aufbauen immer mehr Callbacks entstehen. Bei meinem eigenen Eventbroker ist die destroy Methode aller Containerwidgets überschrieben, so dass bei destroy alle Message Abonnements für sie gelöscht werden. Aber das geht hier zu weit.

Entweder man bleibt bei diesem Eventbroker und nimmt einen Verteiler, der eine Message in mehrere andere umschaufelt, aber besser ist, man rüstet dieses beim Eventbroker nach.
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Also zunächst einmal Danke. Ich habe erst einmal meinen Button im Toolbar mit einem Command ausgestattet und in dann auf Publish gesetzt. Der Rest läuft, wie du bereits geschrieben hast.

Ich glaube mich zu erinnern, dass beim Eventbroker durchaus mehrmalige Betätigung auch schon zu Fehlern geführt hatte.

Wenn er nun nicht mehrere Empfänger bedienen kann, dann kann ich nicht einfach den Button im Toolbar drücken, die Eingaben im InputGUI ausblenden und die Ausgaben im OutputGUI einblenden, weil dass dann 2 Empfänger sind. Vielleicht hast du ja doch noch eine ruhige Minute und kannst ihn mal dazu aufrüsten. :wink:
Ich würde mich freuen.

Übrigens, warum exportiert der GUIDesigner mit Leerzeichen und nicht mit Tabs?


Viele Grüße Stefan
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Naja und jetzt geht mir ein Licht auf, was ich schon vorher falsch gemacht hatte. Ich hatte immer zur Hauptklasse gesendet und empfangen.

Also werde ich es nun richtig machen und jede einzelne Klasse mit Widget zum Sender machen. Also z.B. beim Toolbar gibt es das Fileframe, welche die Buttons "newfile" und "openfile" beinhaltet. Hierzu ein Sender und nicht aus der darüber befindlichen Klasse,welche als Container alle Untercontainer der einzelnen Toolbarleisten in sich herbergt.
Ob ich das beim Input als Empfänger genauso mache, weiß ich noch nicht. Liegt möglicherweise am Umstand, was man machen möchte.
BlackJack

@stefanxfg: Eingerückt wird mit vier Leerzeichen pro Ebene. Nicht mit Tabs. Siehe den Style Guide for Python Code.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

stefanxfg hat geschrieben:Ich glaube mich zu erinnern, dass beim Eventbroker durchaus mehrmalige Betätigung auch schon zu Fehlern geführt hatte.
Mehrmaliges publish sicher nicht. Aber wenn zwei Empfänger eines subscribe auf dieselbe Message machen, dann klaut sie der zweite vom ersten. Da gibt es aber eine Konsolenausgabe dafür. Der Grund nur einen Callback zu erlauben, ist dass man bei Widget Löschen und wieder neu subscribe zu machen, keine ungüpltige callbacks mehr übrig bleiben.
stefanxfg hat geschrieben:Übrigens, warum exportiert der GUIDesigner mit Leerzeichen und nicht mit Tabs?
Damit man den Code hier in das Forum stellen kann. Statt Tabs nimmt man hier vier Leerzeichen.
stefanxfg hat geschrieben:Wenn er nun nicht mehrere Empfänger bedienen kann, dann kann ich nicht einfach den Button im Toolbar drücken, die Eingaben im InputGUI ausblenden und die Ausgaben im OutputGUI einblenden, weil dass dann 2 Empfänger sind. Vielleicht hast du ja doch noch eine ruhige Minute und kannst ihn mal dazu aufrüsten. :wink:
Ich würde mich freuen.

Viele Grüße Stefan
Schon geschehen, aber noch nicht auf GitHub

Code: Alles auswählen

import sys
def output(param):
    sys.stdout.write(param+'\n')

class EventBroker():
    def __init__(self):
        self._dictionary_ = {}
 
    def subscribe(self,message_id,callback_or_alias):

        is_string = True

        try: # python2
            is_string = isinstance(callback_or_alias,basestring)
        except NameError: #python 3
            is_string = isinstance(callback_or_alias,str)
            
        if is_string:
            if message_id not in self._dictionary_:
                self._dictionary_[message_id] = set()
            self._dictionary_[message_id].add(callback_or_alias)
            
        else:
            if message_id in self._dictionary_:
                output('EventBroker: callback already defined for message id: {}\nThe callback before will be overwritten'.format(message_id))

            self._dictionary_[message_id] = callback_or_alias
 

    def publish(self,message_id,*args,**kwargs):
        if message_id not in self._dictionary_:
            output('EventBroker: no callback defined for message: {},*{},**{}'.format(message_id,args,kwargs))
        else:
            callback = self._dictionary_[message_id]
            if not isinstance(callback ,set):
                return callback(*args,**kwargs)
            for element in callback:
                if element in self._dictionary_:
                    callback = self._dictionary_[element]
                    if isinstance(callback,set):
                        output("EventBroker: for message id '{}' is alias '{}' allowed, but no furter aliases for this alias".format(message_id,element))
                    else:
                        callback(*args,**kwargs)


eventbroker = EventBroker()
publish = eventbroker.publish
subscribe = eventbroker.subscribe


if __name__ == '__main__':

# =========================== Test ==============================
# for aliases are only strings allowed

    def receiver_A():

        def got(info):
            output('receiver_A got info: ' + info)

        subscribe('BROADCAST','BROADCAST_A')
        subscribe('BROADCAST_A',got)

    def receiver_B():

        def got(info):
            output('receiver_B got info: ' + info)

        subscribe('BROADCAST','BROADCAST_B')
        subscribe('BROADCAST_B',got)

    receiver_A()
    receiver_B()

    publish('BROADCAST','NEWS')
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Nach Korrektur dieses Codes:

testcode.py

Code: Alles auswählen

from communication import eventbroker, publish, subscribe

class InputCode:

    def __init__(self,container):
        self.container = container

        subscribe('AUSGABE_EIN','LIFT_EINGABE')
        subscribe('LIFT_EINGABE',self.lift_eingabe)

    def lift_eingabe(self,ausgabe_ein):
        if not ausgabe_ein:
            self.container.lift()

class OutputCode:

    def __init__(self,container):
        self.container = container
        subscribe('AUSGABE_EIN','LIFT_AUSGABE')
        subscribe('LIFT_AUSGABE',self.lift_ausgabe)

    def lift_ausgabe(self,ausgabe_ein):
        if ausgabe_ein:
            self.container.lift()

def ToolbarCode(self):
    self.schalter1.connect(eventbroker,'AUSGABE_EIN',False)
wurden dann bei diesem Anfangsbeispiel: viewtopic.php?f=18&t=40677

mit dem GuiDesigner die Text Felder herausgelöscht und stattdessen Frames hineingesetzt und für diese die entsprechenden Eintragungen gemacht. Das ist dann das Resultat:

Code: Alles auswählen

# -*- coding: utf-8 -*-

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

#import DynTkInter as tk # for GuiDesigner


#============= imports baseclass ===================
import AppOutput
import AppToolbar
import AppInput

# === general grid table definition =================
def grid_general_rows(container,rows,**kwargs):
    for row in range(rows):
        container.rowconfigure(row,**kwargs)

def grid_general_cols(container,columns,**kwargs):
    for column in range(columns):
        container.columnconfigure(column,**kwargs)

# Application definition ============================

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.minsize(500, 400)
        # widget definitions ===================================
        self.frame_guinotes = GuiNotes(self,name='#0_frame_guinotes')
        self.frame_menunotes = FrameMenunotes(self,name='#1_frame_menunotes')
        self.frame_toolbarnotes = FrameToolbarnotes(self,name='#2_frame_toolbarnotes')
        self.label_PS = tk.Label(self,name='#3_label_PS',bd='4', anchor='w', pady='4', fg='#a90000', relief='solid', font='TkFixedFont 10 bold', text='Welches Layout soll man nehmen?\nUnd soll man einen GuiBuilder nehmen?\nWenn ja, welchen?', padx='4', justify='left')
        self.frame_menunotes.pack(fill='x')
        self.frame_toolbarnotes.pack(fill='x')
        self.frame_guinotes.pack(expand=1, fill='both')
        self.label_PS.pack(fill='x', pady=3)

class GuiNotes(tk.Frame):

    def __init__(self,master,**kwargs):
        tk.Frame.__init__(self,master,**kwargs)
        self.myclass = 'GuiNotes'
        self.config(bg='#ceff00', highlightthickness=7, highlightbackground='#a90000', highlightcolor='#a90000')
        # general grid definition ==============================
        grid_general_rows(self,1, minsize = 100, pad = 0, weight = 1)
        grid_general_cols(self,1, minsize = 100, pad = 0, weight = 1)
        # widget definitions ===================================
        self.InputContentGUI = Input(self,name='#4_InputContentGUI')
        self.InputContentGUI.grid(row=0, sticky='nesw')
        self.OutputGUI = Output(self,name='#5_OutputGUI')
        self.OutputGUI.grid(row=0, sticky='nesw')

class Input(AppInput.InputGUI):

    def __init__(self,master,**kwargs):
        AppInput.InputGUI.__init__(self,master,**kwargs)
        self.myclass = 'Input'
        self.baseclass = 'AppInput.InputGUI'

class Output(AppOutput.OutputGUI):

    def __init__(self,master,**kwargs):
        AppOutput.OutputGUI.__init__(self,master,**kwargs)
        self.myclass = 'Output'
        self.baseclass = 'AppOutput.OutputGUI'

class FrameMenunotes(tk.Frame):

    def __init__(self,master,**kwargs):
        tk.Frame.__init__(self,master,**kwargs)
        self.config(highlightbackground='blue', highlightthickness=7, highlightcolor='blue')
        # widget definitions ===================================
        self.notes_menu = tk.Text(self,name='#6_notes_menu',font='TkFixedFont 12 bold', pady=8, width=1, fg='blue', padx=8, height=1)
        self.notes_menu.delete(1.0, tk.END)
        self.notes_menu.insert(tk.END,'Oben soll ein Menü sein')
        self.notes_menu.pack(expand=1, fill='both')

class FrameToolbarnotes(tk.Frame):

    def __init__(self,master,**kwargs):
        tk.Frame.__init__(self,master,**kwargs)
        self.config(relief='sunken', highlightbackground='#008900', highlightthickness=7, highlightcolor='#008900')
        # widget definitions ===================================
        self.ToolbarGUI = Toolbar(self,name='#7_ToolbarGUI')
        self.ToolbarGUI.pack(expand=1, fill='both')

class Toolbar(AppToolbar.Toolbar):

    def __init__(self,master,**kwargs):
        AppToolbar.Toolbar.__init__(self,master,**kwargs)
        self.myclass = 'Toolbar'
        self.baseclass = 'AppToolbar.Toolbar'
        self.config(height=60)

if __name__ == '__main__':
    #Application().mainloop('guidesigner/Guidesigner.py') # for GuiDesigner
    Application().mainloop()
Lediglich eine kleine Nachpesserung war nötig. In AppToolbar.py war das Padding mittels y und height etwas zu hoch eingestellt, wodurch dann der LED Switch zu klein wurde.

Code: Alles auswählen

        self.schalter1.place(relwidth='0.2', height='-10', x='10', y='5', relheight='1')
Hier war zuerst y = 10 und height = -20 und das war etwas zuviel. Wenn man einmal von meinem LED Switch absieht, war das bißchen in testcode.py der ganze zu schreibende Code.

Ist klar, dass man den ganzen generierten Code nicht händisch schreiben sollte, wenn man alles bequem und live erlebend mit dem GuiDesigner erledigen kann.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Noch eine wichtige Anmerkung:

wenn man statt den normalen tkinter Importen das importiert

Code: Alles auswählen

import DynTkInter as tk # for GuiDesigner
Und statt dem normalen mainloop dieses aufruft:

Code: Alles auswählen

Application().mainloop('guidesigner/Guidesigner.py') # for GuiDesigner
Dann startet der GuiDesigner mit zur Gui. Und da ist noch etwas wichtig. Wenn man das nur bei der Applikation macht und bei den anderen Modulen die normalen imports lässt, ist alles schön und funktionierend im GuiDesigner zu sehen. Bearbeiten und speichern kann man dann allerdings nur die Applikation. Denn diese ist dann in DynTkInter und für den GuiDesigner da. Das andere, obwohl sichtbar und funktionierend, nicht für den GuiDesigner existent.

Das ist dann sehr gut, wenn man nur die Applikation bearbeiten und speichern möchte, nicht aber die in Komponenten aufgeteilte Gui wieder zu einer zusammengefügt haben will.

Die Gui zusammenfügen kann man, wenn man auch für die Komponenten die Importe mit DynTkInter macht.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Der neue Eventbroker ist jetzt auf GitHub im Verzeichnis Utilities
Antworten