当前位置: 首页 > 知识库问答 >
问题:

Python3-后台线程中的键盘中断在用户将鼠标悬停在GUI窗口上之前未被主线程检测到

充子航
2023-03-14

我已经编写了一个基于Python3Tkinter的GUI应用程序,它在后台启动一个工作线程。在工作线程完成后,它等待两秒钟(这是为了避免可能的争用情况),然后发送键盘中断,告诉主线程它可以关闭。

预期行为:运行程序启动GUI窗口,向控制台打印一些文本,然后程序自动关闭。

实际行为:它不是自动关闭,而是在用户将鼠标悬停在图形用户界面窗口区域上或按下键盘上的键后才关闭!除此之外,该程序运行时没有报告任何错误。

有人知道为什么会发生这种情况,以及如何解决吗?我已经尝试将KeyboardInterrupt包装成一个单独的函数,然后通过一个timer对象调用它,但这会导致相同的行为。

我已经能够在运行Python3.5的两台不同的Linux机器上重现这个问题。2.及3.6。分别为6。

#! /usr/bin/env python3

import os
import threading
import _thread as thread
import time
import tkinter as tk
import tkinter.scrolledtext as ScrolledText

class myGUI(tk.Frame):

    # This class defines the graphical user interface 

    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.root = parent
        self.build_gui()

    def build_gui(self):                    
        # Build GUI
        self.root.title('TEST')
        self.root.option_add('*tearOff', 'FALSE')
        self.grid(column=0, row=0, sticky='ew')
        self.grid_columnconfigure(0, weight=1, uniform='a')

        # Add text widget to display logging info
        st = ScrolledText.ScrolledText(self, state='disabled')
        st.configure(font='TkFixedFont')
        st.grid(column=0, row=1, sticky='w', columnspan=4)

def worker():
    """Skeleton worker function, runs in separate thread (see below)"""  

    # Print some text to console
    print("Working!")
    # Wait 2 seconds to avoid race condition
    time.sleep(2)
    # This triggers a KeyboardInterrupt in the main thread
    thread.interrupt_main()

def main():
    try:
        root = tk.Tk()
        myGUI(root)
        t1 = threading.Thread(target=worker, args=[])
        t1.start()
        root.mainloop()
        t1.join()
    except KeyboardInterrupt:
        # Close program if subthread issues KeyboardInterrupt
        os._exit(0)

main()

(此处链接到上述脚本)

共有1个答案

魏彦
2023-03-14

root。mainloop()<代码>t1。join()在您的代码中实际上从未执行过。

由于阻止等待转发的硬件中断,为了解除阻塞,你必须提供它们,例如像你看到的那样悬停在窗口上。只有到那时,解释器才会检测到挂起的KeyboardInter0009。这就是Python中信号处理的工作原理。

解决一般问题可能意味着找到方法,通过外部注入解除阻塞所需的内容来解除阻塞I/O调用,或者一开始就不使用阻塞调用。

对于您的具体设置,您可以使用未处理的SIGTERM终止整个过程,但当然,这样做非常非常难看,在这里也没有必要。如果您只是想寻找一种方法来超时窗口,您可以使用tkinter来超时。蒂克。在method(此处和此处显示)之后,或者您摆脱了mainloop,自己运行循环(此处)。

后者可能看起来像:

def main():
    root = tk.Tk()
    myGUI(root)

    t1 = threading.Thread(target=worker, args=[])
    t1.start()

    while True:
        try:
            root.update_idletasks()
            root.update()
            time.sleep(0.1)
        except KeyboardInterrupt:
            print('got interrupt')
            break

    t1.join()
 类似资料:
  • 问题内容: 我正在Xcode 9 beta,iOS 11中使用Google Maps。 我收到如下错误输出到日志: 主线程检查器:在后台线程上调用的UI API:-[UIApplication applicationState] PID:4442,TID:837820,线程名称:com.google.Maps.LabelingBehavior,队列名称:com.apple.root.default

  • 问题内容: 我有一张图片: 然后,我将其显示在屏幕上: 如何检测鼠标是否在触摸图像? 问题答案: 使用获得描述你的边界,然后使用来检查,如果鼠标光标这里面。 例:

  • 问题内容: 之前已针对Android,ObjectiveC和C++解决了此问题,但显然不适用于Python。如何可靠地确定当前线程是否为主线程?我可以想到一些方法,但没有一种方法能让我真正满意,因为考虑到与存在的方法相比,它可能是如此简单。 主线程是这样实例化的: 所以一个人可以做 但是这个名字是固定的吗?我见过的其他代码检查了线程名称中是否包含任何代码。 存储启动线程 我可以在程序启动时即在没有

  • 所以一个人可以做 但这个名字固定了吗?我看到的其他代码检查了是否包含在线程名称的任何地方。 我可以在程序启动时存储对起始线程的引用,也就是在没有其他线程的时候。这是绝对可靠的,但对于这样一个简单的查询来说太麻烦了? 有没有更简洁的方法?