Workshop: Gui Entwicklung in tkinter

Fragen zu Tkinter.
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