GUI durch Berechnungen blockiert - Modul threading

Fragen zu Tkinter.
Antworten
Benutzeravatar
pysq
User
Beiträge: 31
Registriert: Samstag 29. November 2008, 17:48

Hallo,
ich arbeite mich gerade in das Modul threading ein, da ich es später für eine Client-Server-Architektur brauche.
Ich habe ein Beispielprogramm geschrieben, das noch nicht wie gewünscht funktioniert: Trotz threading wird die GUI blockiert, wenn im Hintergrund ein Thread ausgeführt wird. Dadurch wird es nicht möglich, einen neuen Thread zu starten.

Zum einfachen Beispielprogramm: Es handelt sich um einen Primzahlentest, der eine Zahl als Eingabe erwartet.

Code: Alles auswählen

import threading 
from Tkinter import *

class MyThread(threading.Thread): 
    def __init__(self, number): 
        threading.Thread.__init__(self) 
        self.number = number
 
    def run(self): 
        i=2
        prim=True
        while i*i < self.number: 
            if self.number%i==0:
                prim=False
                break
            i+=1
        print self.number, prim

def execute():
    global all_threads
    eingabe = e.get()
    e.delete(0,999)
    if eingabe == "ende": 
        global root
        root.destroy()
        for t in all_threads: 
            t.join()
        return
    thread = MyThread(int(eingabe)) 
    all_threads.append(thread)
    thread.start()

all_threads=[]
root=Tk()
e=Entry(root, width=10)
e.pack()
b=Button(root, text='Teste Zahl!', command = execute)
b.pack()
Kann mir bitte jemand helfen?

lg pysq
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Wie wäre es mit

Code: Alles auswählen

root.mainloop()
am Ende? :wink:
MfG
HWK
Benutzeravatar
pysq
User
Beiträge: 31
Registriert: Samstag 29. November 2008, 17:48

danke. habe nach schwerwiegenderen Fehlern gesucht :D
mfg pysq
Benutzeravatar
pysq
User
Beiträge: 31
Registriert: Samstag 29. November 2008, 17:48

Hi!
Ich bin nun schon weitergekommen mit threading, stehe nun aber vor folgendem Problem:
Ich habe einen Clienten und einen Server.
Die run Methode der Klasse Client testet permanent auf empfangene Nachrichten. Der Server sendet genau 2 davon, die Nachrichten sind jeweils Methodennamen von Client, die aber parameterlos sind.
Die erste Methode ist "CreateLabel". Sie erstellt ein Tkinter Label.
Die zweite Methode ist "DestroyLabel". Sie soll das Label wieder zerstören.
Doch das Label verschwindet leider nicht aus der GUI.
Weiß jemand, wo der Fehler liegt? danke wieder im Voraus! ;)

client.py, vereinfacht

Code: Alles auswählen

import threading, socket
import Tkinter as tk

class Client(threading.Thread, tk.Tk):

    def __init__(self, sock, ausschalter):
        threading.Thread.__init__(self)
        tk.Tk.__init__(self)
        
        self.sock=sock
        self.ausschalter=ausschalter
        self.canvas=tk.Canvas(self)
        self.canvas.pack()

    def run(self): 
        while True:  
            rcvmsg = self.sock.recv(1024)
            if rcvmsg: 
                # rcvmsg ist hier immer ein Methodenname von Test,
                # der parameterlos ist
                dargs = {}
                if hasattr(self, rcvmsg):
                    getattr(self, rcvmsg)(**dargs)

    def CreateLabel(self):
        print 'func create'
        self.l = tk.Label(self.canvas, text = 'Label', relief = 'groove')
        self.canvas.create_window(50, 50, window = self.l)

    def DestroyLabel(self):
        print 'func destroy'
        self.l.destroy()
        self.canvas.update()
        self.update()

ip = "127.0.0.1"
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, 50000))

ausschalter = threading.Event()
test = Client(sock, ausschalter)
test.start() # beginnt permanent auf Nachrichten zu testen
tk.mainloop()
server.py, vereinfacht

Code: Alles auswählen

import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.bind(("", 50000)) 
s.listen(1)

try: 
    while True:
        komm, addr = s.accept()
        print 'Client connected'
        komm.send("CreateLabel")
        time.sleep(1)
        komm.send("DestroyLabel")
        print 'ready'
finally: 
    s.close()
mfg pysq :]
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo pysq !

Falls du das Modul threading wegen der blockierten Gui verwendest, ist es auch moeglich diese Verhalten mit s.setblocking(0) zu verhindern. So kannst du auch die after Methode verwenden.

gruss frank
Benutzeravatar
pysq
User
Beiträge: 31
Registriert: Samstag 29. November 2008, 17:48

Hallo!
Ich verwende threading, um im Hintergrund einer GUI auf Nachrichten von einem Server zu warten. Der Nutzer kann also noch verschiedene andere Operationen in der GUI durchführen.
Konkret handelt es sich um ein Kartenspiel (ähnlich Skat), dass ich in Form eines Netzwerkspiels implementieren will. Das Spiel selbst soll auf dem Server laufen.
Der Client erhält vom Server jedes mal, wenn eine Karte gelegt wird ein Nachricht, das der Kartenstapel geupdatet werden soll (vgl. "CreateLabel" - erstellt eine Karte).
Wenn ein Kartenstich vollständig ist, erhält der Client den Befehl, den Kartenstapel wieder zu leeren vgl. "DestroyLabel"). Letzteres funktioniert nicht richtig.

s.setblocking(0) selbst ändert an dem Problem noch nichts. Was ist die after Methode? Ich habe sie in den Module Docs/ Python Manuals nicht gefunden.

mfg pysq
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

pysq hat geschrieben:Was ist die after Methode? Ich habe sie in den Module Docs/ Python Manuals nicht gefunden.
after
Benutzeravatar
pysq
User
Beiträge: 31
Registriert: Samstag 29. November 2008, 17:48

danke! es funktioniert soweit bei parameterlosen Funktionen! : )
Eines noch:
Ich will nun mit der after Methode eine Funktion mit Parametern aufrufen. Die Parameter liegen in einen Dictionary vor.
Wie kann ich alle Argumente des Dictionaries an die mit after per callback aufgerufene Funktion übergeben?

mfg pysq
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

pysq hat geschrieben:Ich will nun mit der after Methode eine Funktion mit Parametern aufrufen. Die Parameter liegen in einen Dictionary vor.
Wie kann ich alle Argumente des Dictionaries an die mit after per callback aufgerufene Funktion übergeben?
Variante 1:
Das Dictionary als Attribut an eine Instanz binden, die auch im Event-Handler verfügbar ist (so eine Art "global" Ersatz)

Variante 2:
Einen lambda-Ausdruck bauen. Im Zusammenhang mit Callbacks:
http://www.ferg.org/thinking_in_tkinter ... grams.html
Speziell die Programme tt078.py und tt079.py mit den entsprechenden Erläuterungen.
Benutzeravatar
pysq
User
Beiträge: 31
Registriert: Samstag 29. November 2008, 17:48

jetzt funktioniert alles! nochmal thx :]
Antworten