我是Python新手,目前正在開發一個小型應用程序供個人使用。我用我的gui使用tkinter。試圖與tkinter(tkinter是在主線程上)運行線程執行任務,而是它停止主線程
我想要做的是創建一個帶有標籤的Toplevel彈出窗口,根據登錄嘗試的方式更改文本。所以,當tk運行的主線程顯示帶有動態文本的彈出窗口時,我想要啓動一個線程來嘗試登錄至多5次,並通過設置名爲'logindata'的全局變量向主線程報告。
AuctioneerGUI和LoginThread類中的_login()方法實際上是唯一重要的事情,您可以忽略其餘部分,但它們可能認爲相關。
當按下登錄按鈕時調用_login()方法。所有這些都是嘗試登錄並設置logindata。同時主線程正在循環,直到它注意到LoginThread已經設置了變量,並且它收集了所有三個元素之後,纔會通過邏輯的其餘部分(這不是完全實現的,但與問題無關)
現在發生的情況是主線程在LoginThread啓動後暫停,並且只有在完成時纔會繼續。即使LoginThread應該在單獨的線程中運行,因此不會暫停主線程。所以彈出窗口只在LoginThread任務執行完畢後才顯示出來。我希望彈出窗口出現並顯示給用戶更新的標籤。我該怎麼做呢?
我確定問題是線程暫停主線程,因爲我使用打印確定了這一點。
另外我還有一個小問題。 popup.destroy()似乎沒有做任何事情。 TopLevel只停留在那裏。
對不起,我的文字牆,並提前感謝幫助我。我已經花費了比我應該嘗試幾種不同的東西更多的時間,但是我沒有設法讓它工作。
讓我知道如果有什麼不清楚,不介意有時效率低下或愚蠢的邏輯,我首先想在使它漂亮之前至少使其功能。
-Daan
global logindata
logindata = {"counter": -1, "status": -1, "success": -1}
class AuctioneerGUI:
def __init__(self):
root = Tk()
root.title("Path of Exile Auctioneer")
self._setupGUI(root)
self._loggingin = False
root.protocol("WM_DELETE_WINDOW", lambda: root.quit())
root.mainloop()
def _setupGUI(self, root):
frame = Frame(root)
email = StringVar()
pass_ = StringVar()
thread = StringVar()
email.set("email")
pass_.set("password")
thread.set("76300")
email_label = Label(frame, text="email")
self._email_box = Entry(frame, takefocus=True, width=50, textvariable=email)
self._email_box.focus_set()
pass_label = Label(frame, text="password")
self._pass_box = Entry(frame, takefocus=True, show="*", width=50, textvariable=pass_)
thread_label = Label(frame, text="thread id")
self._thread_box = Entry(frame, takefocus=True, width=10, textvariable=thread)
self._login_button = Button(frame, text="login", command=lambda: self._login(root), takefocus=True)
frame.pack()
email_label.pack()
self._email_box.pack()
pass_label.pack()
self._pass_box.pack()
thread_label.pack()
self._thread_box.pack()
self._login_button.pack()
def _login(self, root):
self._login_button.configure(command=None)
email = self._email_box.get()
pass_ = self._pass_box.get()
thread = self._thread_box.get()
# Check email validity
# no whitespaces, 1 @ sign 1 . after the @ sign
try:
thread = int(thread)
except ValueError:
return -1
#invalid thread
if not re.match(r"[^@][email protected][^@]+\.[^@]+", email) or not email.find(" ") == -1:
return -1
#invalid mail
self._sm = SessionManager(email, pass_, thread)
self._message = StringVar()
self._message.set("Attempt 1/5.")
popup = Toplevel(root)
popup.title("Logging in...")
message_label = Label(popup, text = self._message.get(), textvariable = self._message)
message_label.pack()
_thread = LoginThread(self._sm)
_thread.start()
loop = True
while loop:
counter = -1
success = -1
status = -1
while counter == -1:
counter = logindata["counter"]
print(counter)
while success == -1:
success = logindata["success"]
print(success)
while status == -1:
status = logindata["status"]
print(status)
if success:
self._message.set("Attempt {}/5. Success.".format(counter))
elif status == 200:
self._message.set("Attempt {}/5. Failed: wrong password.".format(counter))
else:
self._message.set("Attempt {}/5. Failed: connection error. {}".format(counter, status))
updatebar = not success
logindata["counter"] = -1
logindata["status"] = -1
logindata["success"] = -1
if counter == 5:
break
popup.destroy()
self._login_button["command"] = lambda: self._login(root)
self._setup_main_layout(root)
def _setup_main_layout(self, root):
pass
class LoginThread(threading.Thread):
def __init__(self, sessionmanager):
threading.Thread.__init__(self)
self._sm = sessionmanager
def run(self):
success = False
counter = 1
while not success:
if counter > 5:
break
data = self._sm.login()
status = data[1]
success = data[0]
logindata["counter"] = counter
logindata["success"] = success
logindata["status"] = status
counter += 1
print("done")
更新:
一些研究,我將通過創建從標籤是通過管道輸送到小部件,通過像在本例中,隊列通信繼承ThreadSafeLabel解決問題後:
http://effbot.org/zone/tkinter-threads.htm
理解它的工作原理很有用。您仍在投票處理完成情況,這仍然是浪費,但是您只會每100ms執行一次,而不是儘可能快。而且,更重要的是,你讓主要的'tkinter'事件循環在間隔100ms內運行,所以GUI不會凍結。這仍然不是理想的,但它是你用純tkinter做的最好的。 – abarnert 2013-02-19 02:25:15
是的,我意識到儘可能快地通過輪詢囤積所有cpu資源並不明智。我發佈的代碼片段幾乎是一個正在進行的混亂。我有一些java線程的經驗,但我從來沒有像tkinter一樣處理它。 – 2013-02-19 02:29:34
我可能會記住錯誤,但不是Swing基本相同 - 觸摸另一個線程中的任何Swing對象會導致異常或神祕崩潰等。除非我非常確定Swing有一個'runLaterOnDispatchThread'方法(和''BackgroundWorker'和'WorkerExecutor'),對吧? – abarnert 2013-02-19 18:48:48