在后台运行函数 - 如何实现?Python和PyQT

1 投票
1 回答
4995 浏览
提问于 2025-04-17 04:47

我有一个比较大的应用程序,是用Python写的,前端用的是PyQT。整个应用程序都在一个类里,放在一个文件中。

下面是一个示例代码:

class Application(QMainWindow):
  def __init__(self):
    super(etc...)

    self.connect(self.mainBtn, SIGNAL("clicked()"), self.do_stuff)

  def do_stuff(self):
    <checking some parameters>
    else:
      do_some_other_long_stuff()


  def do_some_other_long_stuff(self):
     500 lines of code of stuff doing

但是,这里有个问题:当我点击mainBtn时,一切都很顺利,除了界面会有点卡住——在这个函数执行期间,我什么都不能做(因为它是个网页抓取工具,所以需要花费不少时间)。等到函数do_some_other_long_stuff结束后,一切又恢复正常。这真的很让人烦。

有没有办法让do_some_other_stuff这个过程在“后台”运行呢?我查了一下QThreads,似乎可以做到这一点,但这就意味着我得重写几乎所有的代码,把程序的一半放到另一个类里,还得改所有的变量名(因为要从GUI类中获取变量,然后放到工作类中)。

1 个回答

2

这段内容是关于如何在不同线程中处理图形用户界面(GUI)的,类似的问题还有很多,比如如何处理不同线程的GUI如何在Python中跟踪线程进度而不冻结PyQt GUI等。

你的do_stuff()函数需要启动一个计算线程,然后返回。多线程就是在一个程序里同时运行多个任务的意思——简单来说,如果有事情在“后台”进行,那就是在另一个线程上运行。不过,你不需要把函数拆分到不同的类里才能使用线程,只要确保计算函数不和GUI有任何交互,同时主线程也不要调用计算线程使用的任何函数。

编辑于10月23日:这里有个简单的例子,展示如何在一个类里运行多个线程——其实语言或线程库并没有要求每个线程都用不同的类。这个例子可能使用了单独的类来处理,以便展示良好的模块化编程。

from tkinter import *
import threading

class MyApp:
    def __init__(self, root):
        self.root = root
        self.timer_evt = threading.Event()
        cf = Frame(root, borderwidth=1, relief="raised")
        cf.pack()
        Button(cf, text="Run", command=self.Run).pack(fill=X)
        Button(cf, text="Pause", command=self.Pause).pack(fill=X)
        Button(cf, text="Kill", command=self.Kill).pack(fill=X)

    def process_stuff(self):        # processing threads
        while self.go:
            print("Spam... ")
            self.timer_evt.wait()
            self.timer_evt.clear()

    def Run(self):                  # start another thread
        self.go = 1
        threading.Thread(target=self.process_stuff, name="_proc").start()
        self.root.after(0, self.tick)

    def Pause(self):
        self.go = 0

    def Kill(self):                 # wake threads up so they can die
        self.go = 0
        self.timer_evt.set()

    def tick(self):
        if self.go:
            self.timer_evt.set()    # unblock processing threads
            self.root.after(1000, self.tick)

def main():
    root = Tk()
    root.title("ProcessingThread")
    app = MyApp(root)
    root.mainloop()

main()

撰写回答