tkinter gui在其他2个线程打开时停止响应

时间:2018-02-07 10:59:05

标签: python multithreading tkinter pyhook

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()除了重现bug之外什么都不做。

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

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

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

非常感谢阅读。

1 个答案:

答案 0 :(得分:0)

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

该类充当会话监视程序并使用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)
相关问题