【问题标题】:Python - How to return the data from a threadPython - 如何从线程返回数据
【发布时间】:2019-06-11 07:35:05
【问题描述】:

我目前正在开发一个用户输入数据并单击按钮以运行绘制画布的函数的 GUI。

绘制画布的函数运行大约需要 1 分钟,在这几秒钟内,GUI 会冻结。

因此,在这几秒钟内,我想显示一个要求用户等待的页面(例如,带有一点动画 gif)。

这是我正在使用的代码(我用 time.sleep 替换了绘制图表的代码):

import tkinter as tk
from tkinter import ttk
import threading
from PIL import Image, ImageTk 

TITTLE_FONT = ("Verdana", 30)
LARGE_FONT= ("Verdana", 16)
NORM_FONT = ("Helvetica", 10)
SMALL_FONT = ("Helvetica", 8)
style.use("ggplot")


class GUI(tk.Tk):

    def __init__(self, *args, **kwargs):


        tk.Tk.__init__(self, *args, **kwargs)
        self.shared_data = {
            "x1": tk.StringVar(),
            "x2": tk.StringVar(),
            "x3": tk.StringVar()}


        tk.Tk.wm_title(self, "Titre")

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand = True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        #Menu
        menubar = tk.Menu(container)
        filemenu = tk.Menu(menubar, tearoff=0)
        filemenu.add_command(label="Exit", command=self.destroy)
        menubar.add_cascade(label="File", menu=filemenu)

        tk.Tk.config(self, menu=menubar)

        self.frames = {}

        for F in (StartPage, PageGraph, WaitingPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")
        self.show_frame(StartPage)

    def show_frame(self, cont):

        frame = self.frames[cont]
        frame.tkraise()

    def get_page(self, page_class):

        return self.frames[page_class]


class WaitingPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller=controller
        label1 = tk.Label(self, text="Calculs en cours", font=TITTLE_FONT)
        label1.pack()
        label2 = tk.Label(self, text="Veuillez Patienter", font=LARGE_FONT)
        label2.pack()


class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent)
        self.controller=controller
        label = tk.Label(self, text="Page d'acceuil", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        #---------- Boutton pour switcher sur la "Page du graph"

        button = ttk.Button(self, text="Page du Graph",
                            command=lambda: controller.show_frame(PageGraph))
        button.pack()

        label1 = ttk.Label(self, text="X1", font=NORM_FONT)
        label1.pack(pady=10,padx=10)
        self.entry1 = tk.Entry(self, textvariable=self.controller.shared_data["x1"])
        self.entry1.pack(pady=10,padx=10)

        label2 = ttk.Label(self, text="X2", font=NORM_FONT)
        label2.pack(pady=10,padx=10)
        self.entry2 = tk.Entry(self, textvariable=self.controller.shared_data["x2"])
        self.entry2.pack(pady=10,padx=10)

        label3 = ttk.Label(self, text="X3", font=NORM_FONT)
        label3.pack(pady=10,padx=10)
        self.entry3 = tk.Entry(self, textvariable=self.controller.shared_data["x3"])
        self.entry3.pack(pady=10,padx=10)

        button2 = ttk.Button(self, text="Valider", command=self.do_button)
        button2.pack()




    def do_button(self):

        page = self.controller.get_page(PageGraph)
        page.Graph()        




class PageGraph(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller=controller

        label = tk.Label(self, text="Graph Page", font=LARGE_FONT)
        label.pack(pady=10,padx=10)


        button1 = ttk.Button(self, text="Back to Home",command=lambda: controller.show_frame(StartPage))
        button1.pack()

        # ---------- Création du canvas vide

        self.f = Figure()
        self.a = self.f.add_subplot(111)

        self.canvas = FigureCanvasTkAgg(self.f, self)
        self.canvas.draw()
        self.canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)

        self.toolbar = NavigationToolbar2Tk(self.canvas, self)
        self.toolbar.update()
        self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)



    def Graph(self):


        self.controller.show_frame(WaitingPage)

        def real_Graph() :

            time.sleep(5)

            x1 = float(self.controller.shared_data["x1"].get())
            x2 = float(self.controller.shared_data["x2"].get())
            x3 = float(self.controller.shared_data["x3"].get())

            xAxis = [float(x1),float(x2),float(x3)]
            yAxis = [float(x1),float(x2),float(x3)]

            return (xAxis , yAxis)


        threadGraph = threading.Thread(target=real_Graph)
        threadGraph.start()

        #############################################
        ######### Retrieve xAxis and yAxis ?#########
        #############################################

        self.toolbar.update()
        self.a.clear() 
        self.a.bar(xAxis,yAxis)
        self.canvas.draw()

        self.controller.show_frame(PageGraph)




app = GUI()
app.geometry("1280x720")
app.mainloop()

在 Graph 函数中:想法是在另一个线程中运行长计算,然后检索数据以绘制图形。

因此我的问题是:如何检索在另一个线程中运行的函数返回的内容(此处为 xAxis 和 yAxis)? (这里是函数real_Graph)

【问题讨论】:

    标签: python multithreading user-interface matplotlib tkinter


    【解决方案1】:

    由于不能在其他线程更新图,可以使用tkinter.after(...)定期检查主线程是否更新图:

    def check_progress(self):
        if self.done:
            # calculation done, update graph
            self.a.clear() 
            self.a.bar(self.xAxis, self.yAxis)
            self.canvas.draw()
            # show the graph
            self.tkraise()
        else:
            # calculation not done, schedule next check
            self.after(500, self.check_progress)
    
    def real_Graph(self):
        self.done = False # set calculation not done
        time.sleep(5)
        x1 = float(self.controller.shared_data["x1"].get())
        x2 = float(self.controller.shared_data["x2"].get())
        x3 = float(self.controller.shared_data["x3"].get())
        self.xAxis = [float(x1),float(x2),float(x3)]
        self.yAxis = [float(x1),float(x2),float(x3)]
        self.done = True # set calculation done
    
    def Graph(self):
        self.controller.show_frame(WaitingPage)
        # create other thread to do calculation
        threadGraph = threading.Thread(target=self.real_Graph)
        threadGraph.start()
        # start the progress check
        self.check_progress()
    

    【讨论】:

    • 哦,太好了!非常感谢您的解决方案:)
    【解决方案2】:

    我希望这会对你有所帮助。您需要更改这部分:

    threadGraph = threading.Thread(target=real_Graph)
    threadGraph.start()
    

    Threads = []
    threadGraph = threading.Thread(target=real_Graph)
    Threads.append(threadGraph)
    threadGraph.start()
    for t in Threads:
        print(t.join())
    

    【讨论】:

    • 感谢有关线程的信息!我尝试了您的代码,但是当我输入数据(x1,x2,x3)时,我的应用程序崩溃了。我认为我对 tkinter/threads 有更深层次的问题或误解。
    猜你喜欢
    • 1970-01-01
    • 2021-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-10
    • 2011-10-17
    • 2018-04-15
    相关资源
    最近更新 更多