Python Tkinter 睡眠/未按预期工作后 [英] Python Tkinter sleep/after not working as expected

查看:24
本文介绍了Python Tkinter 睡眠/未按预期工作后的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我想要做的是脚本 B 中的一个函数将在脚本 A 中调用.因此该函数将通过打印在控制台上显示一些输出,但我想要的是输出也应该显示在一个小部件上,脚本 A 是我所有 GUI 操作发生的地方.

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.

所以我所做的是将函数 X 作为参数传递给脚本 B 中的函数 Y,因此,当该输出打印在函数 Y 中时,我只是将其引用到一个变量中并将该变量传递到脚本 A 的函数 X... 我作为参数传递.因此,该函数只是将输出插入到文本小部件上.

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. Thus, that function simply inserted the output on the text widget.

但问题是在单击按钮后调用了函数 Y,并且由于某种原因按钮冻结了一段时间,然后立即显示了最终输出.但这不是我想要的.我希望输出在控制台中显示的同时显示,一个接一个,这样.但是当命令参数中传递的整个函数完成运行时,按钮小部件似乎会恢复.

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. 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. But it seems like that the button widget resumes when the entire function passed in the command argument has finished running.

为了解决这个问题,我尝试使用 sleep 和 after 函数,但它们似乎对我帮助不大.因此,我试图以更简单的方式重新创建我的问题,并且我尝试通过睡眠和之后来解决它,但似乎没有一个对我有用.

To solve this I tried using sleep and after functions but they don't seem to be helping me that much. 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.

所以代码如下,虽然它们不完全符合我的问题,但我希望他们能够更清楚地解释我的问题.

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.

假设我们有两个脚本 A 和 B

So say we have two scripts A and B

在脚本 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

在脚本 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, i.e displaying on the console.

更新:这不是实际问题,它只是我实际问题的简化版本.所以不要假设我试图让这段代码变得过于复杂.由于我正在使用经过训练的 NN 训练分类器,因此每次迭代都会在控制台上打印输出.所以我想要实现的是在正在进行的循环中同时将输出打印在文本小部件上.

更新 2:它终于如我所愿.答案是使用 Mike 描述的线程:D

推荐答案

sleep()tkinter 不相处.while 也不行.sleep 和 while 的问题在于它们会阻塞主循环直到它们完成,因此在它们完成之前,您的 GUI 中没有任何更新.也就是说,我认为您正在使这段代码变得更加复杂.你有两个函数可以简单地完成一个函数,并且你正在将函数传递给函数.比需要的复杂得多.

sleep() and tkinter do not get along. Nor does 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. 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. Way more complex than need be.

每次调用 print 时,您都会打包一个新标签.请尽量遵守 PEP8 标准来命名您的函数.all_low_with_underscores 用于标准函数和变量.

You are also packing a new label every time print is called. Please try to keep to the PEP8 standard for naming your function. all_low_with_underscores for standard functions and variables.

方法 after() 专门用于处理 tkinter 中的定时事件,主要用于替换 GUI 中的 sleep.

The method after() is specifically designed to handle timed events within tkinter and is mainly used to replace sleep within the GUI.

这是您的代码简化并使用了after():

Here is your code simplified and using 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()

这篇关于Python Tkinter 睡眠/未按预期工作后的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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