Tkinter Fenster Klassenzugriff

Fragen zu Tkinter.
Antworten
Pixilated
User
Beiträge: 16
Registriert: Samstag 27. Juni 2015, 14:09

Code: Alles auswählen

from Tkinter import *

class _Test1:
    def __init__(self, master):
        self.initComplete = 0
        frame = Frame(master, width=300, height=300)
        frame.pack()
        self.master = master
        self.Button_1 = Button(self.master,text="Button_1 text", width="15")
        self.Entry_1 = Entry(self.master,width="15")


class _Test2:
    def __init__(self, master):
        self.initComplete = 0
        frame = Frame(master, width=300, height=300)
        frame.pack()
        self.master = master
        self.Button_1 = Button(self.master,text="Button_1 text", width="15")
        self.Entry_1 = Entry(self.master,width="15")
        


def main():
    root = Tk()
    app = _Test1(root)
    root.mainloop()

def main2():
    root = Tk()
    app = _Test2(root)
    root.mainloop()

if __name__ == '__main__':
    main()
Ich habe zwei Fenster mit den Classen Test1 und Test2 über Tkinter erstellt. Nun versuche ich den in Fenster Main1(Class Test1) in das eingabefeld eingegebenen Wert in das Fenster Main2(class Test2) zu übertragen.
Ist es generell möglich auf tkinter Widgets (die mit einer Klasse erstellt wurden) zuzugreifen ?

Danke für die Hilfe !!!
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Das ist keine Frage von Tkinter, sondern tatsaechlich ein einfaches Python-Problem.

Die jeweiligen Objekte muessen sich kennen, um auf Attribute zuzugreifen. Das kannst du erreichen in dem du `Test2` zb das entsprechende Exemplar von `Test1` uebergibst und dann auf dessen Attribute zugreifst.
Pixilated
User
Beiträge: 16
Registriert: Samstag 27. Juni 2015, 14:09

Danke für die Antwort nur habe ich leider nichts weiter zu dem Problem gefunden wie genau eine solche Übergabe aussieht :(
BlackJack

@Pixilated: Übergeben meint normalerweise als Argument beim Aufruf übergeben.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ich haette ja gerne dein Beispiel angepasst, aber leider passt es so gar nicht zu deinem Problem. Ich versuchs mal trotzdem:

Code: Alles auswählen

class Test1(object):
    def __init__(self, master):
        self.master = master
        self.Entry_1 = Entry(self.master,width="15")


class Test2(object):
    def __init__(self, master, other):
        self.other = other
        self.master = master

    def report_on_other(self):
        print self.other.Entry_1.get()


def main():
    root = Tk()
    app = Test1(root)
    t2 = Test2(root, app)
    root.mainloop()

if __name__ == '__main__':
    main()
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Pixilated Das Problem ist einfach zu lösen durch einen Eventbroker. Hier ein Beispiel:

Code: Alles auswählen

from simple_event_broker import EventBroker

eventbroker = EventBroker()

def mycallback(message):
    print(message)

eventbroker.subscribe("MYMESSAGE",mycallback)

eventbroker.publish("MYMESSAGE","So you may access all, what you like")
Ein Subscriber registriert sich für eine Nachricht mittels einer Message ID (hier "MYMESSAGE").
Ein Publisher, der den Subscriber gar nicht kennt, schickt eine Nachricht mit dieser Message ID.

Hier eine abgespeckte Version meines EventBrokers (nicht für auch andere Threads):

Code: Alles auswählen

import queue

class EventBroker:
 
    def __init__(self):
        self.trigger = self.do_work # intern trigger at start is direct call of 'do_work'.
        self._initialize() # initialize the event broker
 
     # publish within same thread
    def publish(self,msgid,msgdata=None):
        self._Queue.put((msgid,msgdata))
        self.trigger()

    # subscribe a callback for an event
    def subscribe(self,msgid,callback,with_id=False):
        if msgid not in self._Dictionary: self._Dictionary[msgid] = {}
        self._Dictionary[msgid][callback] = with_id

    # unsubscribe a callback for an event
    def unsubscribe(self,msgid,callback):
        if msgid in self._Dictionary:
            self._Dictionary[msgid].pop(callback,None)
            if len(self._Dictionary[msgid]) == 0:
                del self._Dictionary[msgid]

    # process all events in the queues and block other calls
    def do_work(self,*args):
        if self._running: return
        self._running = True
        while self.work(): pass
        self._running = False

    # process one event in the queues 
    def work(self,*args):

        # get message from Queue
        if not self._Queue.empty(): data = self._Queue.get()
        else: return False

        # look up message id and registered callbacks for it and call callback
        msgid = data[0]
        msgdata = data[1]
        if msgid in self._Dictionary:
            callbacks_of_msgid = dict(self._Dictionary[msgid]).items() # # items contain callback function and with_id and we need a copy (via dict), because the entry could change via subscribe or unsubsribe
            for callback,with_id in callbacks_of_msgid:
                if with_id: callback((msgid,msgdata))
                else: callback(msgdata)
        return True

    def _initialize(self):
        self._running = False # regulates calls of method 'do_work'- start value False enables call of method 'work'
        self._Queue = queue.Queue() # Queue for publishing
        self._Dictionary = {} # contains registered message ids and registered callback functions for these message ids
        self.subscribe("EXECUTE_MESSAGE",lambda message: message()) # very useful callback function: executes messages, which are lambda expressions
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: ich will jetzt nicht den nächsten Thread für den OP kaputt machen, aber ich glaube, Dein EventBroker geht an dem Problem vorbei: synchroner Zugriff auf den Inhalt eines Widgets. Oder der OP hat sich unklar ausgedrückt und will eigentlich etwas ganz anderes.
Pixilated
User
Beiträge: 16
Registriert: Samstag 27. Juni 2015, 14:09

Vielen Dank für die Hilfe !

cofi hat das Problem erkannt. Leider ist es mir bisher nicht gelungen die Lösung erfolgreich umzusetzen.

Hier mal mein ganzes Programm worum es eigentlich geht, das mit dem entry hatte ich zum besseren Verständnis angesprochen.

Ich habe ein Hauptfenster über das ein weiteres Fenster (Profil) geöffnet werden kann.
In diesem Fenster kann über eine Liste ein eintrag gewählt werden der dann durch betätigen der Schaltfläche Übernehmen in das Label2.text übernommen wird.

Über weitere Vorschläge bin ich sehr dankbar weil ich ehrlich gesagt nicht weiter weiss....



Code: Alles auswählen

from Tkinter import *
from tkFileDialog   import askopenfilename
import ConfigParser


config = ConfigParser.RawConfigParser()

class _Startmenue(object):
    def __init__(self, master,other):
        global v
        global profil
        self.initComplete = 0
        frame = Frame(master, width=533, height=243)
        frame.pack()
        self.master = master
        self.other = other
        self.x, self.y, self.w, self.h = -1,-1,-1,-1
        self.master.bind('<Enter>', self.bindConfigure)
        

        self.master.title("Startmenue")

        self.Button_1 = Button(self.master,text="Laden", width="15")
        self.Button_1.place(x=264, y=24, width=118, height=29)
        self.Button_1.bind("<ButtonRelease-1>", self.Button_1_Click)

        self.Button_2 = Button(self.master,text="Laden", width="15")
        self.Button_2.place(x=348, y=132, width=71, height=33)
        self.Button_2.bind("<ButtonRelease-1>", self.Button_2_Click)

        self.Button_4 = Button(self.master,text="Starten", width="15")
        self.Button_4.place(x=12, y=192, width=118, height=36)
        self.Button_4.bind("<ButtonRelease-1>", self.Button_4_Click)

        self.Button_5 = Button(self.master,text="Hilfe", width="15")
        self.Button_5.place(x=240, y=192, width=84, height=37)
        self.Button_5.bind("<ButtonRelease-1>", self.Button_5_Click)

        self.Button_6 = Button(self.master,text="Info", width="15")
        self.Button_6.place(x=336, y=192, width=83, height=37)
        self.Button_6.bind("<ButtonRelease-1>", self.Button_6_Click)

        self.Button_7 = Button(self.master,text="Exit", width="15")
        self.Button_7.place(x=432, y=192, width=87, height=36)
        self.Button_7.bind("<ButtonRelease-1>", self.Button_7_Click)

        self.Button_8 = Button(self.master,text="Entfernen", width="15")
        self.Button_8.place(x=444, y=132, width=73, height=33)
        self.Button_8.bind("<ButtonRelease-1>", self.Button_7_Click)

        self.Checkbutton_1 = Checkbutton(self.master,text="Modellnamen verwenden", width="15")
        self.Checkbutton_1.place(x=252, y=60, width=175, height=30)
        self.Checkbutton_1_StringVar = StringVar()
        self.Checkbutton_1.configure(variable=self.Checkbutton_1_StringVar, onvalue="yes", offvalue="no")
        self.Checkbutton_1_StringVar_traceName = self.Checkbutton_1_StringVar.trace_variable("w", self.Checkbutton_1_StringVar_Callback)

        self.Entry_1 = Entry(self.master,width="15")
        self.Entry_1.place(x=72, y=60, width=153, height=22)
        self.Entry_1_StringVar = StringVar()
        self.Entry_1.configure(textvariable=self.Entry_1_StringVar)
        self.Entry_1_StringVar.set("Entry_1 text")
        self.Entry_1_StringVar_traceName = self.Entry_1_StringVar.trace_variable("w", self.Entry_1_StringVar_Callback)

        self.Label_1 = Label(self.master,text="Profil:", width="15")
        self.Label_1.place(x=12, y=24, width=33, height=22)
        v = StringVar()

        self.Label_2 = Label(self.master,text= " < ? > ", anchor="center", width="15")
        self.Label_2.place(x=84, y=24, width=116, height=22)

        self.Label_3 = Label(self.master,text="Protokoll:", justify="center", relief="flat", width="15")
        self.Label_3.place(x=0, y=60, width=71, height=23)

        self.Label_4 = Label(self.master,text="Modell:", width="15")
        self.Label_4.place(x=0, y=132, width=73, height=28)

        self.Labelframe_1 = LabelFrame(self.master,text="", height="-26", width="537")
        self.Labelframe_1.place(x=12, y=228)


        lbframe = Frame( self.master )
        self.Listbox_1_frame = lbframe
        scrollbar = Scrollbar(lbframe, orient=VERTICAL)
        self.Listbox_1 = Listbox(lbframe, width="15", selectmode="extended", relief="sunken", yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.Listbox_1.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.Listbox_1.pack(side=LEFT, fill=BOTH, expand=1)

        self.Listbox_1_frame.place(x=72, y=120, width=251, height=48)
        self.Listbox_1.bind("<ButtonRelease-1>", self.Listbox_1_Click)
        self.master.resizable(0,0) # Linux may not respect this
     
    def bindConfigure(self, event):
        if not self.initComplete:
            self.master.bind("<Configure>", self.Master_Configure)
            self.initComplete = 1
            
    def report_on_other(self):
        print "uuzbzbuz"

        
    def Master_Configure(self, event):
        pass
     
        if event.widget != self.master:
            if self.w != -1:
                return
        x = int(self.master.winfo_x())
        y = int(self.master.winfo_y())
        w = int(self.master.winfo_width())
        h = int(self.master.winfo_height())
        if (self.x, self.y, self.w, self.h) == (-1,-1,-1,-1):
            self.x, self.y, self.w, self.h = x,y,w,h


        if self.w!=w or self.h!=h:
            print "Master reconfigured... make resize adjustments"
            self.w=w
            self.h=h
    
    def Button_1_Click(self, event): #click method for component ID=3
        pass

        menue()
    
        print "executed method Button_1_Click"
        print(self)

    
    def Button_2_Click(self, event): #click method for component ID=7
        pass
        print(self)
        
        name = askopenfilename(filetypes=[("CATIA Modelle","*.CATPart")])
        if name  != "":
            
            self.Listbox_1.insert(END, name)
        else:
            print"bitte eingabe tätogen"
            
        print "executed method Button_2_Click"

    def Button_4_Click(self, event): #click method for component ID=15
        pass
  
        try:
            cs=self.Listbox_1.curselection()
            self.Listbox_1.delete(cs)
        except:
            print ""
                
      
        print "executed method Button_4_Click"

   
    def Button_5_Click(self, event): #click method for component ID=16
        pass
    
        # replace, delete, or comment-out the following
        print "executed method Button_5_Click"

    def Button_6_Click(self, event): #click method for component ID=17
        pass
     
        print "executed method Button_6_Click"

    def Button_8_Click(self, event): #click method for component ID=18
        pass
        
       
        print "executed method Button_8_Click"

  
    def Button_7_Click(self, event): 
        pass
        
        print "executed method Button_7_Click"

    def Listbox_1_Click(self, event): 
        pass
     
        print "executed method Listbox_1_Click"
        print "current selection(s) =",self.Listbox_1.curselection()
        labelL = []
        for i in self.Listbox_1.curselection():
            labelL.append( self.Listbox_1.get(i))
        print "current label(s) =",labelL
   

    def Checkbutton_1_StringVar_Callback(self, varName, index, mode):
        pass

 
        print "Checkbutton_1_StringVar_Callback varName, index, mode",varName, index, mode
        print "    new StringVar value =",self.Checkbutton_1_StringVar.get()


  
    def Entry_1_StringVar_Callback(self, varName, index, mode):
        pass
 
        print "Entry_1_StringVar_Callback varName, index, mode",varName, index, mode
        print "    new StringVar value =",self.Entry_1_StringVar.get()



class _Profilwahl(object):
    def __init__(self, master):
        global profil
        self.initComplete = 0
        frame = Frame(master, width=439, height=209)
        frame.pack()
        self.master = master
        self.x, self.y, self.w, self.h = -1,-1,-1,-1
       
        self.master.bind('<Enter>', self.bindConfigure)
        

        self.master.title("Profilwahl")

        self.Button_1 = Button(self.master,text="Uebernehmen", width="15")
        self.Button_1.place(x=12, y=168, width=116, height=31)
        self.Button_1.bind("<ButtonRelease-1>", self.Button_1_Click)

        self.Button_2 = Button(self.master,text="Neu", width="15")
        self.Button_2.place(x=288, y=12, width=101, height=35)
        self.Button_2.bind("<ButtonRelease-1>", self.Button_2_Click)

        self.Button_3 = Button(self.master,text="Loeschen", width="15")
        self.Button_3.place(x=288, y=60, width=102, height=36)
        self.Button_3.bind("<ButtonRelease-1>", self.Button_3_Click)

        self.Button_4 = Button(self.master,text="Laden", width="15")
        self.Button_4.place(x=288, y=108, width=103, height=36)
        self.Button_4.bind("<ButtonRelease-1>", self.Button_4_Click)

        self.Button_5 = Button(self.master,text="Zurueck", width="15")
        self.Button_5.place(x=312, y=168, width=117, height=32)
        self.Button_5.bind("<ButtonRelease-1>", self.Button_5_Click)


        lbframe = Frame( self.master )
        self.Listbox_1_frame = lbframe
        scrollbar = Scrollbar(lbframe, orient=VERTICAL)
        self.Listbox_1 = Listbox(lbframe, width="15", selectmode="SINGLE ", yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.Listbox_1.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.Listbox_1.pack(side=LEFT, fill=BOTH, expand=1)

        self.Listbox_1_frame.place(x=12, y=12, width=254, height=140)
        self.Listbox_1.bind("<ButtonRelease-1>", self.Listbox_1_Click)
        config.read('config.cfg')
        for section in config.sections():
            
            self.Listbox_1.insert(END,section)
        self.master.resizable(0,0) # Linux may not respect this
        
  


    def bindConfigure(self, event):
        if not self.initComplete:
            self.master.bind("<Configure>", self.Master_Configure)
            self.initComplete = 1


    def Master_Configure(self, event):
        pass
        # replace, delete, or comment-out the following
        if event.widget != self.master:
            if self.w != -1:
                return
        x = int(self.master.winfo_x())
        y = int(self.master.winfo_y())
        w = int(self.master.winfo_width())
        h = int(self.master.winfo_height())
        if (self.x, self.y, self.w, self.h) == (-1,-1,-1,-1):
            self.x, self.y, self.w, self.h = x,y,w,h


        if self.w!=w or self.h!=h:
            print "Master reconfigured... make resize adjustments"
            self.w=w
            self.h=h
   
    def Button_1_Click(self, event): #click method for component ID=2

        
        
      
        print "executed method Button_1_Click"


    def Button_3_Click(self, event): #click method for component ID=4
        pass
        for i in self.Listbox_1.curselection():
            
            gewaehlt =( self.Listbox_1.get(i))
        config.remove_section(gewaehlt)
        with open('config.cfg', 'wb') as configfile:
            config.write(configfile)
        print (self.Listbox_1.curselection)

        
    def Button_2_Click(self, event): 
        pass
               print "executed method Button_2_Click"

  



    def Button_4_Click(self, event): #click method for component ID=5
        pass
    
        print "executed method Button_4_Click"

    
    def Button_5_Click(self, event): #click method for component ID=6
        pass
       
        print "executed method Button_5_Click"

    def Listbox_1_Click(self, event): #click method for component ID=1
        pass
     
        print "executed method Listbox_1_Click"
        print "current selection(s) =",self.Listbox_1.curselection()
        
        for i in self.Listbox_1.curselection():
            gewaehlt =( self.Listbox_1.get(i))
     


def main():
    root = Tk()
    app2 = _Startmenue(root,app)
    root.mainloop()
    
def menue():
    root = Tk()
    global app
    app = _Profilwahl(root)
    root.mainloop()

if __name__ == '__main__':
    main()
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Puh, da liegt einiges im Argen. Das schlimmste ist, dass du keine brauchbaren Namen benutzt:

Code: Alles auswählen

self.Button_1 = Button(self.master,text="Laden", width="15")
self.Button_1.place(x=264, y=24, width=118, height=29)
self.Button_1.bind("<ButtonRelease-1>", self.Button_1_Click)
Sowohl `Button_1` als auch die entsprechende Methode brauchen bessere Namen, zb `load_button` und `load`. Und das gilt auch fuer die ganzen weiteren Namen.

Im Hinblick auf Tkinter benutzt du `Tk` falsch. Davon darf es nur eine Instanz geben und erst recht nur eine `mainloop`.
`menue` solltest du ganz streichen und stattdessen das Profil direkt im Startmenue aufrufen. Damit hast du dann auch das Exemplar und kannst dessen Attribute abfragen.

Die ganzen `global` sind auch unnoetig.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Pixilated Das ist völlig verkehrt, nämlich zweimal Tk und mainloop. Das darf es nur einmal geben. Wenn Du ein zweites Fenster brauchst, dann nimm dafür ein Toplevel.

Also, das letzte Beispiel ist unübersichtlich. Daher bleiben wir beim ersten Beispiel. Da ist ein Button und ein Eingabefeld aber einen command oder event habe ich da nicht gesehen.

Ich würde erwarten, dass bei Buttondruck oder auch bei Return im Eingabefeld der Wert daraus entnommen wird und an das andere Fenster übergeben wird. Das könnte man mit dem Eventbroker machen:

Code: Alles auswählen

def _init_(self,master):
   ...
    self.Entry_1 = Entry(self.master,width="15")
    self.Entry_1.bind('<Return>,self.send_entry)

def send_entry(self,event):
    eventbroker.publish("ENTRY_1",self.Entry_1.get())
Aber normalerweise sollte es doch auch nicht schwer sein, Dir eine Referenz auf ein Toplevel zu verschaffen. Für Buttons würde ich außerdem den command nehmen und nicht den ButtonRelease. Dafür ist der command ja da. Wenn allerdings das zweite Fenster unabhängig und ganz beliebig irgendwo sein könnte, dann macht der Eventbroker viel Sinn.
Antworten