简体   繁体   English

Python Tkinter 睡眠/未按预期工作后

[英]Python Tkinter sleep/after not working as expected

So what I want to do is that there will be a function from script B which will be called inside the script A. So that function will be displaying some output on the console via print, but what I want is that the output should also be displayed on a widget, and the script A is the one where all my GUI operations happen. So what I want to do is that there will be a function from script B which will be called inside the script A. So that function will be displaying some output on the console via print, but what I want is that the output should also be显示在小部件上,脚本 A 是我所有 GUI 操作发生的地方。

So what I did was that I passed a function X as a argument to the function Y in script B, thus, when that output was printed in the function Y, I simply referenced that into a variable and passed that variable into the function X of script A... which I passed as argument. So what I did was that I passed a function X as a argument to the function Y in script B, thus, when that output was printed in the function Y, I simply referenced that into a variable and passed that variable into the function X of脚本 A... 我作为参数传递。 Thus, that function simply inserted the output on the text widget.因此,function 只是在文本小部件上插入了 output。

But the problem is that the function Y was called after a button click, and due to some reason the button freezes for some time and then displays the final output all at once.但问题是 function Y 在单击按钮后被调用,由于某种原因,按钮冻结了一段时间,然后立即显示最终的 output。 But this is not what I want.但这不是我想要的。 I want that the output to be displayed at the same time as it is displayed in the console, one after another, that way.我希望 output 在控制台中显示的同时显示,一个接一个,这样。 But it seems like that the button widget resumes when the entire function passed in the command argument has finished running.但是,当命令参数中传递的整个 function 完成运行时,按钮小部件似乎会恢复。

To solve this I tried using sleep and after functions but they don't seem to be helping me that much.为了解决这个问题,我尝试使用 sleep 和 after 函数,但它们似乎对我没有太大帮助。 Thus, I have tried to recreate my problem in a more simpler way and I have tried it doing it via sleep and after, but none of em seems to work for me.因此,我试图以更简单的方式重新创建我的问题,并且我已经尝试通过 sleep 和之后的方式来解决它,但似乎没有一个对我有用。

So the codes are below, though they don't exactly match my problem but I hope they are able to explain my question more clearly.所以代码如下,虽然它们不完全符合我的问题,但我希望他们能够更清楚地解释我的问题。

So say we have two scripts A and B假设我们有两个脚本 A 和 B

In the script A -在脚本 A -

from time import sleep

# will output a number every 1 second on the console
def Show(number, Function):    
    while(number < 5):
        sleep(1)            # Wait specified time
        number += 1         # Some random operation, here incrementing the number by 1

        print(number)       # On console
        Function(number)    # On widget

In the script B -在脚本 B -

import A
import tkinter as tk

number = 0

root = tk.Tk()

# Function which will be passed as an argument
def Print(number):
    label = tk.Label(root, text=number)
    label.pack()

# Will be used for the after method [ OPTIONAL ]
def Delay(number, Print):
    root.after(5000, test.Show(number, Print))

# Below I recommend to comment either one of the button initializations, in order to test each ways

# Using sleep method
button = tk.Button(root, text='Start', command=lambda: A.Show(number, Print))

                             #OR

# Using after method
button = tk.Button(root, text='Start', command=lambda: Delay(number, Print))


button.pack()

tk.mainloop()

So my point is that, I want to show the numbers on any widget ( in my real problem it is a text widget ) at the same time as it is actually happening, ie displaying on the console.所以我的意思是,我想在任何小部件上显示数字(在我真正的问题中,它是一个文本小部件),同时它实际上正在发生,即在控制台上显示。

UPDATE: This is not the actual problem, it is just a simplified version of my actual problem.更新:这不是实际问题,它只是我实际问题的简化版本。 So don't assume that I am trying to make this code over complicated.所以不要假设我试图让这段代码变得复杂。 As I am training a classifier with a trained NN so the output is printed every iteration on the console.由于我正在使用经过训练的 NN 训练分类器,因此每次迭代都会在控制台上打印 output。 So what I want to achieve is that the output be printed on the text widget at the same time as well during the ongoing loop.所以我想要实现的是 output 在正在进行的循环过程中同时打印在文本小部件上。

UPDATE 2: It is finally working as I wanted it to be.更新 2:它终于按我想要的方式工作了。 The answer is to use threading as described by Mike:D答案是使用 Mike:D 所描述的线程

sleep() and tkinter do not get along. sleep()tkinter不来。 Nor does while . while也没有。 The problem with sleep and while is that they block the main loop until they are complete and thus nothing updates in your GUI until they have finished. sleep 和 while 的问题是它们会阻塞主循环,直到它们完成,因此在它们完成之前你的 GUI 中没有任何更新。 That said I think you are making this code more complex then it needs to be.也就是说,我认为您正在使此代码变得比它需要的更复杂。 You have 2 functions for something that can simply be done in one and you are passing functions to functions.您有 2 个函数可以简单地在一个函数中完成,并且您正在将函数传递给函数。 Way more complex than need be.比需要的复杂得多。

You are also packing a new label every time print is called.每次调用 print 时,您还要包装一个新的 label。 Please try to keep to the PEP8 standard for naming your function.请尽量遵守 PEP8 标准来命名您的 function。 all_low_with_underscores for standard functions and variables. all_low_with_underscores 用于标准函数和变量。

The method after() is specifically designed to handle timed events within tkinter and is mainly used to replace sleep within the GUI. after()方法专门用于处理 tkinter 中的定时事件,主要用于替换 GUI 中的sleep

Here is your code simplified and using after() :这是您的代码简化并使用after()

import tkinter as tk


def delay_and_print():
    global number
    if number < 5:
        print(number)
        label.config(text=number)
        number += 1
        root.after(1000, delay_and_print)


root = tk.Tk()
number = 0
tk.Button(root, text='Start', command=delay_and_print).pack()
label = tk.Label(root, text='')
label.pack()
root.mainloop()

Here is an example using threading:下面是一个使用线程的例子:

import tkinter as tk
import threading
from time import sleep


def delay_and_print():
    global number
    if number < 100:
        print(number)
        label.config(text=number)
        number += 1
        sleep(2)
        delay_and_print()


def start_thread():
    thread = threading.Thread(target=delay_and_print)
    thread.start()


def do_something():
    print('Something')


root = tk.Tk()
number = 0
tk.Button(root, text='Start', command=start_thread).pack()
tk.Button(root, text='some other button to test if app is responsive while loop is going!', command=do_something).pack()
label = tk.Label(root, text='')
label.pack()
root.mainloop()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM