Problem mit TK und Threads

Fragen zu Tkinter.
Antworten
Zed01
User
Beiträge: 5
Registriert: Freitag 3. April 2009, 18:02

Ich habe ein Problem damit von einem Frame Fenster erstellen zu lassen, wenn das erstellen der Fenster über Buttons oder im Normalen Programmteil geschieht, läuft alles problemlos, sobald aber ein Fenster durch die den Thread creator erstellt wird friert die komplette Oberfläche ein. Wenn man das Erstellen der Fenster im Thread auskommentiert läuft alles problemlos und der Thread printed ein paar Zahlen in der Konsole.

Ein weiteres aber kleines Problem ist, dass die animation der Fenster stehen bleibt, sobald man während der Animation ein zweites Fenster öffnet, da das öffnen der Fenster später aber automatisch ablaufen soll, sobald das Programm Daten abruft (durch den Thread) ist das nicht so schlimm, ein Tipp wäre hier aber auch schön :D

ganz oben muss die Auflösung eingetragen sein, sonst seht ihr das evtl nicht. Kann man garantiert automatisch machen aber ich wollte jetzt erstmal das Problem lösen bevor ich mich an andere Sachen mach^^

Code: Alles auswählen

WINDOWWIDTH = 1680
WINDOWHEIGHT = 1050
FRAMEWIDTH = 200
FRAMEHEIGHT = 60


from Tkinter import Tk, Button, Frame, Label, Toplevel
from time import sleep

from threading import Thread
#import thread


class Notify():
    def __init__(self, author, text):
        self.author = author
        self.text = text
        self.posx = WINDOWWIDTH-FRAMEWIDTH
        self.posy = WINDOWHEIGHT-FRAMEHEIGHT+130
        self.wantedpos = WINDOWHEIGHT-FRAMEHEIGHT
        self.root = Toplevel()

        self.root.overrideredirect(1)
        self.root.geometry("+"+str(self.posx)+"+"+str(self.posy))
        self.frame = Frame(self.root, width=FRAMEWIDTH, height=FRAMEHEIGHT,
                           borderwidth=5, relief="raised")
        self.frame.pack_propagate(False)
        self.frame.pack()
        self.label = Label(self.frame, text = self.author + "\n" + self.text)
        self.label.pack(side="left", anchor = "nw")

        self.root.update()

        self.moveup()

        #self.root.mainloop()

    def moveup(self):
        self.wantedpos -= notifier.getwindows() * FRAMEHEIGHT + 30
        while self.posy > self.wantedpos:
            self.root.geometry("+"+str(self.posx)+"+"+str(self.posy))
            self.root.update()
            self.posy -=3
            sleep(0.01)

        self.root.bind("<Button-1>",self.movedown)

    def movedown(self, event=None):
        self.root.unbind("<Button-1>")
        while self.posy < (WINDOWHEIGHT+FRAMEHEIGHT):
            self.posy +=3
            self.root.geometry("+"+str(self.posx)+"+"+str(self.posy))
            self.root.update()
            sleep(0.01)
        notifier.decreasewindows()
        self.root.destroy()

class Creater(Thread):
    def run(self):
        while 1:
            sleep(3)
            print 1
            #-------------------- Problem: -----------------
            notifier.startnotify("Hobbybash0r", "blabala")
            
            #button.config(text="3") # verändern von einträgen geht
            print 2

class Notifier:
    def __init__(self):
        self.notifierlist = []
        self.windows = 0

    def getwindows(self):
        return self.windows

    def decreasewindows(self):
        self.windows -= 1

    def startnotify(self, author, text):
        self.notifierlist.append(Notify(author, text))
        self.windows +=1



notifier = Notifier()


main = Tk()
button = Button(main, text="1", command=lambda:notifier.startnotify("Hobbybash0r", "blabala"))
button.pack(fill="x")

button2 = Button(main, text="2", command=lambda:notifier.startnotify("Hobbybash0r", "blubebe"))
button2.pack(fill="x")

creater = Creater().start()

main.mainloop()
Zuletzt geändert von Zed01 am Freitag 3. April 2009, 18:51, insgesamt 3-mal geändert.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Von Threads aus auf GUIs zugreiffen ist etwas problematisch. Benutze einfach die Suchfunktion mit den Stickworten "Threads Tk" und da werden sich passende Antworten finden. Das Problem gab es hier schon öfter.

Dann vergisst du am besten auch gleich, dass es "global" gibt. Die *-Importe solltest du auch sein lassen, auch wenn diese in Tk-Beispielen üblich sind. Konstanten solltest du außerdem vollständig in Großbuchstaben schreiben. Die __init__-Methode deiner Creater-Klasse ist überflüssig.
Das Leben ist wie ein Tennisball.
Zed01
User
Beiträge: 5
Registriert: Freitag 3. April 2009, 18:02

schonmal danke für die Antwort, ich habe deine Tipps schonmal befolgt und oben den Code aktualisiert. Leider finde ich nicht wirklich eine Lösung für das Erstellen von Fenstern durch einen Thread, das Beeinflussen existierender Fenster oder ihrer Widgets ist kein Problem. Nur durch ein Erstellen eines neuen Fensters hängt sich Tk scheinbar auf. Eine Idee wär, dass die Fenster nicht neu erstellt werden sondern nur aus dem sichtbaren Bereich herausfahren und dann recycled werden sobald sie wieder gebraucht werden, schöner fänd ich es aber mit neu erstellten Fenstern, weil so nicht ein ganzer Haufen Fenster irgendwo rumfliegt der eh nicht gebraucht wird ;) hat jemand einen Tipp wie man das Lösen könnte?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Der Trick ist die after-Methode. Ich hab kurz mal gesucht und wuf hat ein Beispiel gemacht, welches dir weiterhelfen sollte: http://www.python-forum.de/viewtopic.ph ... 4aba835f23.

Die von after aufgerufene Methode muss jetzt nur Elemente aus einer Queue auswerten (ohne warten, dass Elemente in dieser liegen), welche durch die Threads gefüllt werden. Die Reaktion könnte dann zum Beispiel sein, dass bei jedem Objekt in der Queue ein neues Fenster erstellt wird.

Es gibt übrigens ein Modul Queue, welches auch threadsicher ist, da brauchst du dicht nicht mehr drum kümmern.


Was mir gerade noch aufgefallen ist: decreasewindows => get_decrease_windows. Das gleiche noch ein paar anderen Methoden. So simple get- und set-Methoden wie getwindow darfst (solltest) du unter Python am besten gleich weglassen und direkt auf die Attribute zugreifen.
Das Leben ist wie ein Tennisball.
Zed01
User
Beiträge: 5
Registriert: Freitag 3. April 2009, 18:02

danke :D

jetzt scheint es zu gehen.

Code: Alles auswählen

    def after_loop(self):
        item = self.newsqueue.get()
        self.startnotify(item[0],item[1])
        self.after_object = main.after(500, self.after_loop)
als Methode im Notifier

Code: Alles auswählen

notifier.newsqueue.put(("Hobbybash0r","blubbb")
im Thread

das einzige Problem ist jetzt noch, dass die Animationen abgebrochen werden sobald ein anderes Fenster kommt oder gehen soll.

edit: es geht mehr oder weniger, jetzt hängt das Programm ab und zu, guck ich mir morgen nochmal genauer an^^
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Verwendest du Queue aus dem queue-Modul? Dann solltest du in deinem Fall besser "get_nowait()" statt "get()" verwenden, da get blockiert und damit auch die after_loop-Methode, wenn keine Elemente in der Schlange sind. Das sollte das hängen deines Programms dann hoffentlich lösen. Zu den Animationen hab ich spontan keine Ideen, kenne mich mit Tkinter aber auch nicht sonderlich aus.
Das Leben ist wie ein Tennisball.
Zed01
User
Beiträge: 5
Registriert: Freitag 3. April 2009, 18:02

ah, jetzt gehts, danke. Das mit den Animationen werd ich dann so lösen, dass der Notifier erst dann ein neues Fenster losschickt, wenn die vorherigen fertig sind. Oder ich guck mal ob man das irgendwie anders machen kann

Edit: Animationsproblem auch gelöst, kein sleep verwenden sondern mit after die position mit einer Rekursiven Funktion erneut aufrufen
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Rekursion in Python? Ist in aller Regel eine eher schlechte Idee.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten