GUI manipulieren

Fragen zu Tkinter.
Antworten
dh233
User
Beiträge: 37
Registriert: Samstag 8. Juli 2006, 08:26

Ich möchte das GUI nach dem klick auf einen Button anpassen. Zum Beispiel möchte ich in einem Frame ein Text-Feld erzeugen.

Die Methode, die den Code hierfür erhält, soll allerdings in einer anderen Klasse stehen. Meine Idee war die folgende:

Ich erzeuge zuerst alles, was in der GUI-Klasse immer gleich ist. Klickt man dann auf einen Button, wird ein Neues Obekt mit der entsprechenden Klasse generiert und die Methode, welche das GUI verändert, wird aufgerufen.

Leider funktioniert das nicht. Wie implementiert man solche Dinge üblicherweise in Python /Tkinter??

lG

dh233
jAN
User
Beiträge: 170
Registriert: Samstag 4. Juni 2005, 18:51
Wohnort: Großmehlra (in Thüringen)
Kontaktdaten:

funzt das nciht mit getattr und setattr ?
#adios.py
import os,sys
while 1: os.startfile(sys.argv[0])
dh233
User
Beiträge: 37
Registriert: Samstag 8. Juli 2006, 08:26

Wie sollte das konkret funktionieren? Könntest du ein Beispiel posten?

Danke, lg


dh233
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hallo,

wie sieht denn Dein Code bis jetzt aus?
Wenn das GUI-update durch ein Ereignis ausgelöst wird, sehe ich da ja gar keine Probleme. Was genau funktioniert nicht?

Hier mal ein extrem einfaches Beispiel:

Code: Alles auswählen

from Tkinter import *
tk = Tk()
def add_label(): Label(tk, text = "Label Nummer %i" % (len(tk.children)), width = 20).grid()
Button(tk, text = "neues Label", width = 20, height = 2, command = add_label).grid()
tk.mainloop()
Grüße,
der Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
dh233
User
Beiträge: 37
Registriert: Samstag 8. Juli 2006, 08:26

Hallo!

Bis jetzt hab ich mal eine Mixin.py gemacht, die Buttons erstellen kann:


------------mixin.py-------------------------------

Code: Alles auswählen

class StartButton(Button):
    
    def __init__(self, parent=None, **config): #
        Button.__init__(self, parent, config, padx=40, pady=40, bg="green", width=10, font=("Times New Roman", 12, "bold")) #
        self.pack()
-----------/mixin.py-----------------------------------

Dann hab ich noch eine Framework.py, welche ein Tkinter-Fenster baut und dort solch einen Button erzeugt:


--------------framework.py-------------------------------

Code: Alles auswählen

"""creating start button and setting command for it"""
            self.startD=StartButton(text="Scan", parent=self.sideButtonFrame)
            self.startD.config(command=self.startD.startDNS)
------------/framework.py--------------------------------

In der mixin.py hab ich dann auch noch eine Klasse mit der man Text-Felder erzeugen kann, die auch in framework.py verwendet wird.

Das Ganze ist aber ziemlich unpraktisch, wenn es darum geht, die Textfelder zu aktualisieren.

Eigentlich wollte ich ja die Logik des Programms und den GUI-Aufbau komplett trennen, aber ich weiß nicht wirklich, wie man das sinnvoll macht.

Der Grundgedanke dahinter ist nämlich, dass man das Programm dann einfach erweitern kann.

lG

dh233
jAN
User
Beiträge: 170
Registriert: Samstag 4. Juni 2005, 18:51
Wohnort: Großmehlra (in Thüringen)
Kontaktdaten:

Du könntest die komplette GUI in eine XML-Datei packen und beim Programmstart einlesen und die GUI darau erstellen... dann lässt sie sich auch einfach erweitern...[/code]
#adios.py
import os,sys
while 1: os.startfile(sys.argv[0])
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hi IG!
dh233 hat geschrieben: Der Grundgedanke dahinter ist nämlich, dass man das Programm dann einfach erweitern kann.
Du hast ein paar Fragmente Deines Programms angegeben, was hilfreich wäre ist eine Art Ablaufplan. Was ich verstanden habe ist:

Ausgangssituation: Frame sideButtonFrame
Generierung -> Startbutton
Configuration -> command-Ereignis des Startbuttons ruft Startbutton.startDNS
So weit so gut. Aber wo kommen jetzt die Textfelder ins Spiel und wo sollen sie plaziert werden? Ist das egal, wo sie im Frame erscheinen oder woher bekommst Du die Positionsinformation? Je ausführlicher und zusammenhängender die Frage, umso geeigneter und hilfreicher die Antworten. :-)

Prinzipiell kannst Du GUI-Erzeugungscode und Logiccode grundlegend trennen. Erster liefert Dir Widgetreferenzen, über die Du Aussehen und Funktion der Widgets jederzeit ändern kannst. Zweiter liefert Methoden oder Funktionen, die Du über Events an die Widgets binden oder die ganze GUI umgestalten kannst.

Ich kenne keine Programmiersprache, mit der man leichter während der Laufzeit neue Kontrollelemente erzeugen kann. Dunkel kann ich mich an meine Diplomarbeit erinnern, in der ich in VBA!! Controls dynamisch erzeugen musste. Aber das ist ein anderes Thema. ;-)

Grüße,
der Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
dh233
User
Beiträge: 37
Registriert: Samstag 8. Juli 2006, 08:26

Hallo!

Das GUI sieht prinzipiell wie folgt aus:

Oben ein Frame der über die gesamte Breite geht und in dem die Buttons dynamisch erstellt werden. Jedes Programm bekommt einen eigenen Button, wobei jedes Programm seinen eigenen Ordner in einem Verzeichnis hat. Dh. es werden zuerst alle Ordner in einem Verzeichnis ausgelesen und da Ordnername == Buttonname werden dann die Buttons erstellt.

Dann kommt an den linken Rand ein Frame, der nur 2 Buttons enthält: Start, Stop. Dieser Frame füllt allerdings trotzdem die gesamte Höhe.

Diese zwei Frames sollen über die gesamte Laufzeit des Programms hinweg gleich bleiben, allerdings sollen dem Start und Stop-Button je nach gewähltem Programm unterschiedliche Funktionen zugeteilt werden.

Der letzte Frame beinhaltet dann abhängig von dem gewählten Programm Textfelder udgl.

So soll das Programm funktionieren:

Es wird gestartet und im obersten Frame drückt man auf den ersten Button, woraufhin in dem Content-Frame (neben Start/Stop- Button) die entsprechenden Textfelder angezeigt werden.

Dann gibt man die Parameter, die das Programm zur Ausführung braucht ein und drückt auf den Start-Button. Hier soll ein neuer Thread gestartet werden, damit das Programm arbeiten kann und es aber trotzdem möglich ist bereits auf den zweitern Button für das zweite Programm zu klicken, damit man dort ebenfalls schon etwas eingeben kann.

Hier taucht dann auch schon das nächste Problem auf: Wenn ich das erste Programm gestartet habe, dann zum zweiten Programm switche und dann wieder zurück zum ersten Programm gehe, sollen die Eingaben im ersten Programm bestehen bleiben und die Textfelder, welche die Ergebnisse ausgeben, sollen aktualisiert werden.

Ich hab das ganze schon mal als dummy gebaut, wobei das nur als Design-Studie war. Das hab ich aber leider nicht auf diesem Computer und daher werde ich es später noch posten.


lG


dh233

EDIT: Hier das Bild, wie das GUI aussehen soll:

http://www.homepage24.de/userdaten6/950 ... er/gui.jpg

Beim Feld "Input" wird etwas eingegeben, dann auf Start gedrückt und bei den 2 Feldern Output wird eben etwas ausgegeben.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hi dh,

na bitte, damit kann man doch schon ein semi-professionelles Grundgerüst erstellen. Ist aber noch einiges dran zu tun. :-)

Code: Alles auswählen

from Tkinter import *
                
tk = Tk()
tk.title("Programmwahl")
tk.configure(width = tk.winfo_screenwidth(), height = tk.winfo_screenheight()-100)
tk.vSelectedProgram = IntVar()
tk.vSelectedProgram.set(1)

dTextfelder1 = { 0:["Output", "Ausgabe"]}
dTextfelder2 = { 0:["Input", "Hallo Welt"],
                 1:["Output", "Ausgabe"]}
dTextfelder3 = { 0:["Input", ""],
                 1:["Input", ""],
                 2:["Output", ""]}

class Program:
    """Klasse verwaltet Programmdaten und -funktionen"""
    def __init__(self, wRoot, dTextfelder, iProgNumber):
        self.dTextfelder = dTextfelder
        self.wRoot = wRoot
        self.iProgNumber = iProgNumber
        self.sName = "Program %i" % iProgNumber
        self.dTextCompounds = {}
        for iNumber, lParams in dTextfelder.items():
            self.dTextCompounds[iNumber] = TextCompound(wRoot, iNumber, lParams)
        PF.add_button(iProgNumber, self.sName)
    def get_textfelder(self):
        return self.dTextfelder
    def show(self):
        for oTextCompound in self.dTextCompounds.values():
            oTextCompound.grid()
    def hide(self):
        for oTextCompound in self.dTextCompounds.values():
            oTextCompound.grid_remove()
    def start(self):
        pass    ##  TODO
    def stop(self):
        pass    ##  TODO
            
    
class ProgramFrame(Frame):
    def __init__(self):
        Frame.__init__(self, tk, bd = 4, relief = SUNKEN)
        self.place(x = 0, y = 0, width = tk.winfo_screenwidth(), height = 60)
        self.iLastProg = tk.vSelectedProgram.get()
        
    def add_button(self, iValue, sName):
        def switch(iValue = iValue):
            dPrograms[self.iLastProg].hide()
            dPrograms[iValue].show()
            self.iLastProg = iValue
        Radiobutton(self, text = sName, command = switch, value = iValue, variable = tk.vSelectedProgram, indicatoron = False).grid(column = iValue, row = 1, padx = 5, pady = 5)

class ControlFrame(Frame):
    fontBig = ("Courier", 24, "bold")
    def __init__(self):
        Frame.__init__(self, tk, bd = 4, relief = SUNKEN)
        Button(self, text = "Start", font = self.fontBig, bg = "#3f5").grid(ipadx = 5, ipady = 5, pady = 3, sticky = NSEW)
        Button(self, text = "Stop", font = self.fontBig, bg = "#f35").grid(ipadx = 5, ipady = 5, pady = 3, sticky = NSEW)
        self.place(x = 0, y = 60, width = 160, height = tk.winfo_screenheight()-160)


class TextCompound(Frame):
    fontCaption = ("Courier", 16, "bold")
    def __init__(self, wRoot, iNumber, lParams):
        Frame.__init__(self, wRoot)
        Label(self, text = lParams[0], font = self.fontCaption).grid()
        self.text = Text(self, width = 30, height = 40)
        self.text.grid(padx = 10, pady = 10)
        self.text.insert("1.0", lParams[1])
        self.grid(row = 1, column = iNumber)
        self.grid_remove()
        
class TextFrame(Frame):
    def __init__(self):
        Frame.__init__(self, tk, bd = 4, relief = SUNKEN)
        self.place(x = 160, y = 60, width = tk.winfo_screenwidth()-160, height = tk.winfo_screenheight()-160)

PF = ProgramFrame()
CF = ControlFrame()
TF = TextFrame()
dPrograms = {   1:Program(TF, dTextfelder1, 1),
                2:Program(TF, dTextfelder2, 2),
                3:Program(TF, dTextfelder3, 3)}
dPrograms[1].show()
tk.mainloop()
Schlecht (nicht) kommentiert, ich weiß. Habe es leider nicht mehr geschafft.

Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
dh233
User
Beiträge: 37
Registriert: Samstag 8. Juli 2006, 08:26

Hallo Michaeal!

Vielen Dank für den Code. Das hilft mir schon mal weiter.

lG


dh233
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hi dh,

den Code habe ich mal schnell zusammengeschrieben, ist nicht wirklich schön und durchsichtig. Schreib mir einfach eine PN, dann können wir die Einzelheiten durchgehen und ich helfe Dir bei eventuellen Fragen.

Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Antworten