【问题标题】:tkinter gui stops responding while 2 other threads opentkinter gui 在其他 2 个线程打开时停止响应
【发布时间】:2018-07-17 15:27:46
【问题描述】:
from pythoncom import PumpWaitingMessages 
import pyHook, threading 
import tkinter as tk

threadsRun = 1 
token = 0

def pas():
  while threadsRun:
    pass

def listen(startButton): 
    """Listens for keystrokes"""

    def OnKeyboardEvent(event):
        """A key was pressed"""
        global threadsRun
        if event.Key == "R":
            startButton.config(relief=tk.RAISED, state=tk.NORMAL, text="Start")
            threadsRun = 0
        return True

    hm = pyHook.HookManager()
    hm.KeyDown = OnKeyboardEvent
    hm.HookKeyboard()
    while threadsRun:
        PumpWaitingMessages()
    else:
        hm.UnhookKeyboard()

def simplegui():

    def threadmaker():
        """Starts threads running listen() and pas()"""
        startButton.config(relief=tk.SUNKEN, state=tk.DISABLED, text="r=stop listening")
        global token, threadsRun
        threadsRun = 1
        token += 1
        t1 = threading.Thread(target=pas, name='pas{}'.format(token))
        t2 = threading.Thread(target=listen, args=(startButton,), name='listen{}'.format(token))
        t1.start()
        t2.start()

    def destroy():
        """exit program"""
        global threadsRun
        threadsRun = 0
        root.destroy()

    startButton = tk.Button(root, text="Start", command=threadmaker, height=10, width=20)
    startButton.grid(row=1, column=0)

    quitButton = tk.Button(root, text="Quit", command=destroy, height=10, width=20)
    quitButton.grid(row=1, column=1)

root = tk.Tk() 
simplegui() 
root.mainloop()

代码说明:
simplegui() 创建两个线程运行
pas()
listen()
同时。

listen() 等待键盘按下(只有r 做任何事情:退出两个线程/函数)。
pas() 什么都不做,但需要重现错误。

问题描述:
单击开始后,按键盘上的任何按钮都会导致tkinter 停止响应。
大约 2/3 的时间 r 将按预期运行。

我正在使用 Spyder IDE (python 3.5)。

一些观察:
使用打印语句,程序将在崩溃前进入listen() 中的while threadsRun 循环,但没有到达OnKeyboardEvent() print 语句。
在按键之前可以等待很长时间,它可能会冻结。
按下启动后可以立即按下一个键,它可以按预期工作。
删除 t1 = ...t1.start() 行允许程序无错误地运行。
或者,删除所有 tkinter 代码允许程序无错误地运行。
一次混合一堆键会冻结它。
如果我在while threadsRun 循环中放置一个打印语句,r 将很少工作。
我在其他帖子中读过,tkinter 不是线程安全的,并且要使用队列。但我不明白怎么做。我也认为可能还有其他问题,因为它有时会起作用。 https://www.reddit.com/r/learnpython/comments/1v5v3r/tkinter_uis_toplevel_freezes_on_windows_machine/

非常感谢您的阅读。

【问题讨论】:

  • 感谢编辑和回答 R4PH43L。我无法实现类和队列,但它确实激励我学习好的类:)。我找到了这个线程:stackoverflow.com/questions/42101906/… 我能够在没有pumpwaitingmessages() 的情况下重建代码,所以它可能有两个消息泵(我假设mainloop() 中有一个),这导致了锁定。我的新代码现在按预期工作。

标签: python multithreading tkinter pyhook


【解决方案1】:

我设法用于线程和队列的一次尝试如下(用多个伪代码条目替换使用过的代码)

该类用作会话看门狗,并使用 sql 命令收集登录用户,然后使用线程进行位置检测 (geoip)

类 SessionWatchdog

import Tkinter as tk
import ttk
import Queue

import Locator

class SessionWatchdog(ttk.Frame):
    """
    Class to monitor active Sessions including Location in a threaded environment
    """
    __queue = None 
    __sql = None

    def __init__(self, *args, **kwargs):
        #...
        # Create the Queue
        self.__queue = Queue.Queue()

    def inqueue(self):
        """ Handle Input from watchdog worker Thread """

        if self.__queue.empty():
            return

        while self.__queue.qsize():
            """
                Use 
                try:
                    self.__queue.get()
                finally:
                    self.__queue.task_done()

                to retrieve data from the queue
            """
            pass

    def gather_data(self, queue):
        """
        Retrieve online users and locate them
        """
        if self.__sql:
            threads = []

            # gather data via sql
            # ....
            # ....
            for data in sql_result:
                thread = Locator(queue, data)
                threads.append(thread)
                thread.start()

            for thread in threads:
                thread.join()

填充队列的定位器:

类定位器


import threading
import Queue

class Locator(threading.Thread):
    """
    docstring
    """

    __base_url = "http://freegeoip.net/json/{}"

    def __init__(self, queue, user_information):
        threading.Thread.__init__(self)

        self.queue = queue
        self.user_information = user_information
    def run(self):
        """ add location information to data (self.user_information)
            to improve performance, we put the localization in single threads.
        """
        located_user = []
        # locate the user in a function, NOT method!
        self.queue.put(located_user, False)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-23
    • 2014-07-08
    • 1970-01-01
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多