【问题标题】:Python ProgressBar & GUI frozen while calculation for plot is going onPython ProgressBar 和 GUI 在进行绘图计算时冻结
【发布时间】:2023-01-05 23:41:12
【问题描述】:

有人可以帮我解决 python 中的线程问题并让进度条正常工作吗?

即使研究给出了很多结果,我也无法让它发挥作用。

我以前从未做过线程,我不知道把东西放在哪里正确。

出于测试目的,我准备了一个带有按钮和进度条的简单 GUI:

单击按钮后,将弹出一个简单的 3d 图。

这样的绘图可能需要一些计算时间,而用户需要等待,我希望 GUI 不被冻结并且进度条动画化。

目前 GUI 冻结,直到绘图显示出来。然后进度条开始动画。

我已经阅读了很多关于线程并将计算和 gui 放到不同的线程? 但我只是不想让它工作。 有人能够向我解释更多,指导我解决类似的问题或文档吗? 或者,万一很快就解决了,超越简单的代码并按应有的方式更正它?

在此先感谢您提供任何帮助。

到目前为止的 Python 脚本:

from time import sleep
from tkinter import EW
import ttkbootstrap as ttk
import numpy as np
import matplotlib.pyplot as plt

def main():

    def plot_sample():

        sleep(5) # simulate calculation times
        x = np.outer(np.linspace(-2, 2, 30), np.ones(30))
        y = x.copy().T # transpose
        z = np.cos(x ** 2 + y ** 2)

        fig = plt.figure()
        ax = plt.axes(projection='3d')

        ax.plot_surface(x, y, z,cmap='viridis', edgecolor='none')
        ax.set_title('Surface plot')
        plt.show()

    def progressbar_start():
        progressbar.grid(row=1, column=0, sticky=EW, padx=10, pady=10) # let progressbar appear in GUI
        progressbar.start(interval=10)

    def progressbar_stop():
        progressbar.stop()
        progressbar.grid_forget() # hide progressbar when not needed

    def button_clicked():
        progressbar_start()  # start progressbar before computation begins
        plot_sample() # plotting
        progressbar_stop()  # stop progressbar after plot is done



    # GUI
    # window size and settings
    root = ttk.Window()

    # Basic settings for the main window
    root.title('Test progressbar')
    root.minsize(300, 200)
    root.resizable(True, True)
    root.configure(bg='white')

    # grid settings for the main window in which LabelFrames are sitting in
    root.columnconfigure(0, weight=1)
    root.rowconfigure(0, weight=1)
    root.rowconfigure(1, weight=1)

    # Button fto show 3d-plot
    button_calc_3dplot = ttk.Button(root, text='Calculate 3D Plot', command=button_clicked)
    button_calc_3dplot.grid(row=0, column=0, padx=5, pady=5)

    progressbar = ttk.Progressbar(style='success-striped', mode='indeterminate')


    # end of GUI
    root.mainloop()


if __name__ == "__main__":
    main()

【问题讨论】:

    标签: python python-3.x tkinter


    【解决方案1】:

    您应该创建一个单独的线程来运行函数 plot_sample 并更新进度条。为此,您需要导入线程模块。

    import threading
    
    def main():
        def plot_sample():
            # your code to create the plot
            ...
    
        def progressbar_start():
            progressbar.grid(row=1, column=0, sticky=EW, padx=10, pady=10)
            progressbar.start(interval=10)
    
        def progressbar_stop():
            progressbar.stop()
            progressbar.grid_forget()
    
        def button_clicked():
            progressbar_start()
            # create a new thread to run the plot_sample function
            t = threading.Thread(target=plot_sample)
            # start the thread
            t.start()
            # stop the progress bar after the thread finishes
            t.join()
            progressbar_stop()
    

    现在,当单击按钮时,进度条将开始动画并且 plot_sample 函数将在单独的线程中运行。这将允许 GUI 在创建绘图时保持响应。

    【讨论】:

    • 嘿 Athrv,谢谢你的回答。我试过了,但它并没有改变行为。我可以看到 matplotlib 被放置在另一个线程中(IDE 发出警告),但最后我仍然有一个冻结的 GUI,并且在制作情节时根本没有进度条:/
    【解决方案2】:

    我有一个解决方案。主要问题是,您不能在线程中运行 plt.show() 函数。由此可见,您不能同时运行窗口和 plt.show 函数。

    from time import sleep
    from tkinter import EW
    import ttkbootstrap as ttk
    import numpy as np
    import matplotlib.pyplot as plt
    from threading import Thread
    
    x, y, z = None, None, None
    
    
    def main():
        def calculate_xyz():
            sleep(5)  # simulate calculation times
            global x, y, z
            x = np.outer(np.linspace(-2, 2, 30), np.ones(30))
            y = x.copy().T  # transpose
            z = np.cos(x ** 2 + y ** 2)
    
        def progressbar_start():
            progressbar.grid(row=1, column=0, sticky=EW, padx=10, pady=10)  # let progressbar appear in GUI
        progressbar.start(interval=10)
    
        def progressbar_stop():
            progressbar.stop()
            progressbar.grid_forget()  # hide progressbar when not needed
    
        def button_clicked():
            progressbar_start()  # start progressbar before computation begins
            calculate_xyz_thread = Thread(target=calculate_xyz)
            calculate_xyz_thread.start()  # calculate x, y and z
            root.after(500, wait_for_plotting)  # wait for plotting and stop animation after finishing
    
        def wait_for_plotting():
            if x is None or y is None or z is None:
                root.after(500, wait_for_plotting)  # run self again
                return  # wait for calculation
            # calculation is finished
            progressbar_stop()  # stop progressbar "after" plot is done
            ax = plt.axes(projection='3d')
            ax.plot_surface(x, y, z, cmap='viridis', edgecolor='none')
            ax.set_title('Surface plot')
            root.after(500, plot_3d)
    
        def plot_3d():
            plt.show()
    
        # GUI
        # window size and settings
        root = ttk.Window()
    
        # Basic settings for the main window
        root.title('Test progressbar')
        root.minsize(300, 200)
        root.resizable(True, True)
        root.configure(bg='white')
    
        # grid settings for the main window in which LabelFrames are sitting in
        root.columnconfigure(0, weight=1)
        root.rowconfigure(0, weight=1)
        root.rowconfigure(1, weight=1)
    
        # Button fto show 3d-plot
        button_calc_3dplot = ttk.Button(root, text='Calculate 3D Plot', command=button_clicked)
        button_calc_3dplot.grid(row=0, column=0, padx=5, pady=5)
    
        progressbar = ttk.Progressbar(style='success-striped', mode='indeterminate')
    
        # end of GUI
        root.mainloop()
    
    
    if __name__ == "__main__":
        main()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-16
      • 2015-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-19
      相关资源
      最近更新 更多