【问题标题】:Creating a loading screen in tkinter在 tkinter 中创建加载屏幕
【发布时间】:2021-04-14 17:46:50
【问题描述】:

有没有办法在 tkinter 中创建加载屏幕?

代码如下:

from tkinter import *

root = Tk()

text = Text(root , width = 65 , height = 20 , font = "consolas 14")
text.pack()

text.insert('1.0' , "hello\n"*5000000)

mainloop()

这里,主窗口需要一些时间才能弹出,所以我想创建一个临时窗口,告诉用户一些进程正在进行中。

为此,我做了这样的事情:

from tkinter import *

root = Tk()

temp_win = Toplevel()
message_label = Label(temp_win , text = "Loading.. Please wait")
message_label.pack()

text = Text(root , width = 65 , height = 20 , font = "consolas 14")
text.pack()

text.insert('1.0' , "hello\n"*5000000)

mainloop()

但是这里,这两个窗口只有在调用mainloop函数时才会弹出,这是没有用的。

我的目标是在主窗口弹出之前显示临时窗口。

有没有办法在 tkinter 中实现这一点?

如果有人能帮助我,那就太好了。

【问题讨论】:

  • 为什么你的应用程序很慢?
  • @DaniyalWarraich:我让它变慢了,只是为了演示。正如您在text.insert('1.0' , "hello\n"*5000000) 这一行中看到的那样,我在文本小部件中插入了很多行,这使得应用程序变慢了。
  • 主窗口弹出需要时间?
  • 试试下面的解决方案,它确实使用threading,但不直接与主tkinter通信。

标签: python tkinter


【解决方案1】:

tkinter 在主代码中,但为启动屏幕创建一个函数,如下所示:

import threading
from tkinter import *

started = False
def pop():
    new = Tk()

    l = Label(new,text='Loading')
    l.pack()

    new.protocol('WM_DELETE_WINDOW',lambda: new.destroy() if started else False) # Close the window only if main window has shown up
    new.mainloop()

threading.Thread(target=pop).start() # Use this before the heavy process starts

root = Tk()

text = Text(root , width = 65 , height = 20 , font = "consolas 14")
text.pack()

text.insert('1.0' , "hello\n"*5000000) # If you want to see real delay use time.sleep(5)

started = True
root.mainloop()

这样Tk 的前两个实例之间没有直接关系,使用Toplevel 将需要先创建Tk 实例。

标志started只是用来保证在主窗口弹出之前不能关闭闪屏。

【讨论】:

  • 在此代码中,new.mainloop() 等待所有窗口都关闭。这意味着即使它位于不同的线程中,它也可以触发事件。唯一不应该引起问题的解决方案是移动线程中的所有非 GUI 内容,并将其他所有内容留在主线程中。
  • @TheLizzard 这意味着new.mainloop() 之后的内容不会被执行?
  • 正确。 <any tkinter.Tk>.mainloop() 是一个阻塞调用,直到所有 tkinter.Tk 窗口都关闭。试试看 - 在new.mainloop() 之后添加print("!"),看看它是否被执行。
  • @TheLizzard 在我关闭启动画面后它确实被执行了。
  • 我的错,我忘记了只有从事件/.after 脚本调用它时才会发生这种情况。所以你的代码应该没问题。
【解决方案2】:

tkinter 不允许您从多个线程调用它(它可能会崩溃而没有任何错误)。这意味着除了主线程/创建Tk() 的线程之外,您不能在线程中执行任何 GUI 操作。还有some bugs_tkinter 如何与tcl 通信。请记住,这就是我所拥有的:

from time import sleep
import tkinter as tk
from threading import Thread

def non_gui_stuff():
    # We are going to do some work
    sleep(3)


t = Thread(target=non_gui_stuff, daemon=True)
t.start()

root = tk.Tk()
# Hide the main window until the non-GUI stuff is done
root.withdraw()

# Create the loading screen
loading_screen = tk.Toplevel(root)
loading_label = tk.Label(loading_screen, text="Loading")
loading_label.pack()

# While the thread is alive
while t.is_alive():
    # Update the root so it will keep responding
    root.update()
print("Non-GUI thread is done")

loading_screen.destroy()
# Show the main window
root.deiconify()
root.focus_force()

root.mainloop()

【讨论】:

    【解决方案3】:

    我相信你可以通过线程来做到这一点。

    import threading
    from tkinter import *
    
    root = Tk()
    
    def loading_screen():
        temp_win = Toplevel()
        message_label = Label(temp_win , text = "Loading.. Please wait")
        message_label.pack()
    
    def main():
        text = Text(root , width = 65 , height = 20 , font = "consolas 14")
        text.pack()
    
        text.insert('1.0' , "hello\n"*5000000)
    
    a = threading.Thread(target=loading_screen)
    b = threading.Thread(target=main)
    a.start()
    b.start()
    mainloop()
    

    【讨论】:

    • 这个问题回答了这个问题。您是否还考虑过在主线程中创建Tk() 的实例并在两个不同的线程中构造子线程,而在进程完成之前不会显示子线程?这不是解决方案,而是以不同的方式解决相同的问题。
    • 这在我的电脑上运行良好。当我运行代码时,即使另一个窗口正在加载,加载屏幕也会弹出文本
    • 同时在多个线程中使用 tkinter 可能会使 tkinter 崩溃,所以请不要这样做
    • 好的,谢谢@TheLizzard 向我解释这一点
    • @CoolCloud 好吧...这很复杂,但我认为它仍然可能崩溃,因为mainloop()(顺便说一句root.mainloop() 将具有相同的效果)等待所有窗口关闭,这意味着事件将从主线程调用。这与创建Tk() 的线程不同。有时 tcl 会奇怪地处理像 this 这样的事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-26
    • 2016-04-16
    • 1970-01-01
    相关资源
    最近更新 更多