尝试修复tkinter GUI冻结(使用线程) [英] Trying to fix tkinter GUI freeze-ups (using threads)

查看:75
本文介绍了尝试修复tkinter GUI冻结(使用线程)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Python 3.x报表创建器,该报表创建器受I/O约束(由于SQL,而不是python),因此在报表创建过程中主窗口将锁定" 分钟创建.

I have a Python 3.x report creator that is so I/O bound (due to SQL, not python) that the main window will "lock up" for minutes while the reports are being created.

所有需要做的就是能够在GUI处于锁定状态时使用标准的窗口动作(移动,调整大小/最小化,关闭等)(GUI上的所有其他内容都可以保持冻结"状态,直到所有报告都已冻结).完成的).

All that is needed is the ability to use the standard window actions (move, resize/minimize, close, etc.) while the GUI is locked-up (everything else on the GUI can stay "frozen" until all reports have finished).

添加了20181129:换句话说,tkinter必须仅控制应用程序窗口的内容,并将所有标准(外部)窗口控件的处理留给操作系统.如果可以的话,我的问题就消失了,并且我不需要全部使用线程/子进程(冻结成为可以接受的行为,类似于禁用执行报告"按钮).

Added 20181129 : In other words, tkinter must only control the CONTENTS of the application window and leave handling of all standard (external) window controls to the O/S. If I can do that my problem disappears and I don't need to use threads/subprocesses all (the freezeups become acceptable behaviour similar in effect to disabling the "Do Reports" button).

执行此操作的最简单/最简单的方法(=对现有代码的最小干扰)是什么?理想情况下,该方法应与Python> = 3.2.2一起使用,并且应采用跨平台的方式(即至少可以在Windows和Linux上运行).

What is the easiest/simplest way (= minimum disturbance to existing code) of doing this - ideally in a way that works with Python >= 3.2.2 and in a cross-platform way (i.e. works on at least Windows & linux).

下面的所有内容都是支持性信息,它们更详细地说明了此问题,尝试的方法以及遇到的一些细微问题.

Everything below is supporting information that explains the issue in greater detail, approaches tried, and some subtle issues encountered.

注意事项:

  • 用户选择他们的报告,然后在主窗口上按创建报告"按钮(当实际工作开始并发生冻结时).完成所有报告后,报告创建代码将显示一个(顶级)完成"窗口.关闭此窗口将启用主窗口中的所有内容,从而允许用户退出程序或创建更多报告.

添加了20181129:显然,我可以以随机的间隔(相隔几秒钟)移动窗口.

Added 20181129: At apparently random intervals (several seconds apart) I can move the window.

除了显示完成"窗口外,报告创建代码不以任何方式涉及GUI或tkinter.

Except for displaying the "Done" window, the report creation code does not involve the GUI or tkinter in any way.

由报表创建代码生成的某些数据必须出现在完成"窗口中.

Some data produced by the report creation code must appear on the "Done" window.

没有理由并行化"报表创建,尤其是因为同一台SQL Server&数据库用于创建所有报告.

There's no reason to "parallelize" report creation especially since the same SQL server & database is used to create all reports.

以防影响解决方案:创建每个报告时,我最终需要在GUI上显示报告名称(现在显示在控制台上).

In case it affects the solution : I'll eventually need to display the report names (now shown on the console) on the GUI as each report gets created.

第一次使用python进行线程处理/子处理,但同时熟悉其他语言.

First time doing threading/subprocessing with python but am familiar with both from other languages.

添加了20181129:开发环境是Win 10上使用Eclipse Oxygen(pydev插件)的64位Python 3.6.4.应用程序必须至少可移植到linux.

Added 20181129 : Development environment is 64 bit Python 3.6.4 on Win 10 using Eclipse Oxygen (pydev plugin). Application must be portable to at least linux.

最简单的答案似乎是使用线程.仅需要一个附加线程(用于创建报告的那个).受影响的行:

The simplest answer seems to be to use threads. Only one additional thread is needed (the one that creates the reports). The affected line:

DoChosenReports()  # creates all reports (and the "Done" window)

更改为:

from threading import Thread

CreateReportsThread = Thread( target = DoChosenReports )
CreateReportsThread.start()
CreateReportsThread.join()  # 20181130: line omitted in original post, comment out to unfreeze GUI 

成功创建报告,并在创建报告时将其名称显示在控制台上.
但是,GUI保持冻结状态,"Done"窗口(现在由新线程调用)从不出现.这使用户陷入困境,无法执行任何操作, 想知道发生了什么(如果有的话)(这就是为什么我想在创建文件名时在GUI上显示它们的原因).

successfully produces the reports with their names being displayed on the console as they get created.
However, the GUI remains frozen and the "Done" window (now invoked by the new thread) never appears. This leaves the user in limbo, unable to do anything and wondering what, if anything, has happened (which is why I want to display the filenames on the GUI as they get created).

顺便说一句,在完成报告之后,报告创建线程必须在显示完成"窗口之前(或之后)悄悄地自杀.

BTW, After the reports are done the report creation thread must quietly commit suicide before (or after) the Done window is shown.

我也尝试使用

from multiprocessing import Process

ReportCreationProcess = Process( target = DoChosenReports )
ReportCreationProcess.start()

但这与主程序"if(__name__ =='__main__):'"测试相抵触.

but that fell afoul of the main programs "if (__name__ == '__main__) :' " test.

添加了20181129:刚刚发现了"waitvariable"通用小部件方法(请参见

Added 20181129 : Just discovered the "waitvariable" universal widget method (see http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/universal.html). Basic idea is to launch the create report code as an do-forever thread (daemon?) controlled by this method (with execution controlled by the "Do reports" button on the GUI).

根据网络研究,我知道所有tkinter动作都应在主(父)线程内进行, 表示我必须将完成"窗口移至该线程.
我还需要该窗口来显示它从子"线程接收的一些数据(三个字符串).我正在考虑使用使用应用程序级全局变量作为信号量(仅由create report线程写入,而仅由主程序读取)以传递数据.我知道使用两个以上的线程可能会有风险,但为我的简单情况做更多的事情(例如使用队列?)似乎是过大了.

From web research I know that all tkinter actions should be made from within the main (parent) thread, meaning that I must move the "Done" window to that thread.
I also need that window to display some data (three strings) it receives from the "child" thread. I'm thinking of using use application-level globals as semaphores (only written to by the create report thread and only read by the main program) to pass the data. I'm aware that this can be risky with more than two threads but doing anything more (e.g. using queues?) for my simple situation seems like overkill.

总结:让用户出于任何原因冻结窗口的同时,最简单的方法是允许用户操纵(移动,调整大小,最小化等)该应用程序的主窗口.换句话说,O/S,而不是tkinter,必须控制主窗口的框架(外部).
答案需要以跨平台的方式(至少在Windows和Linux上)在python 3.2.2+上工作

To summarize: What's the easiest way to allow the user to manipulate (move, resize, minimize, etc.) an application's main window while the window is frozen for any reason. In other words, the O/S, not tkinter, must control the frame (outside) of the main window.
The answer needs to work on python 3.2.2+ in a cross-platform way (on at least Windows & linux)

推荐答案

您将需要两个函数:第一个封装程序的长期运行工作,第二个创建处理第一个函数的线程.如果您需要线程在用户仍在运行时关闭程序时立即停止线程(不建议),请使用daemon标志或查看

You'll need two functions: the first encapsulates your program's long-running work, and the second creates a thread that handles the first function. If you need the thread to stop immediately if the user closes the program while the thread is still running (not recommended), use the daemon flag or look into Event objects. If you don't want the user to be able to call the function again before it's finished, disable the button when it starts and then set the button back to normal at the end.

import threading
import tkinter as tk
import time

class App:
    def __init__(self, parent):
        self.button = tk.Button(parent, text='init', command=self.begin)
        self.button.pack()
    def func(self):
        '''long-running work'''
        self.button.config(text='func')
        time.sleep(1)
        self.button.config(text='continue')
        time.sleep(1)
        self.button.config(text='done')
        self.button.config(state=tk.NORMAL)
    def begin(self):
        '''start a thread and connect it to func'''
        self.button.config(state=tk.DISABLED)
        threading.Thread(target=self.func, daemon=True).start()

if __name__ == '__main__':
    root = tk.Tk()
    app = App(root)
    root.mainloop()

这篇关于尝试修复tkinter GUI冻结(使用线程)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆