使用QThreads的正确方法是什么?

时间:2013-12-23 10:35:50

标签: python multithreading pyside qthread

我正在尝试使用PySide在Python中构建一个具有多个窗口的应用程序。每个窗口执行一些函数,一个函数的执行不应该阻止其他窗口执行自己的函数,这就是我需要使用多线程的原因。但是我不知道该怎么做。我正在尝试使用新方法来使用线程(正确的),但我在网上找到的所有例子都是用C ++编写的,我不懂C ++,这就是我寻求帮助的原因。

为简化起见,我构建了两个模块,一个名为test_main,另一个名为test_wdw

这是test_main的代码:

import sys
import test_wdw
from PySide import QtGui
from PySide import QtCore

class Main_Window(QtGui.QMainWindow):
    def __init__(self):
        super(Main_Window,self).__init__()
        self.initUI()

    def initUI(self):
        self.statusBar()

        self.new_window=QtGui.QAction("&Window alpha",self)
        self.new_window.triggered.connect(self.open_window)

        self.menu_bar=self.menuBar()
        self.menu1=self.menu_bar.addMenu('&Menu 1')
        self.menu1.addAction(self.new_window)

        # Creates a QMdiArea to manage all windows
        self.wmanager=QtGui.QMdiArea()
        self.setCentralWidget(self.wmanager)

        # Shows the main window
        self.showMaximized()

    def open_window(self):
        test_wdw.launch_window()
        test_wdw.window_alpha=self.wmanager.addSubWindow(test_wdw.window)
        # Shows the new window
        test_wdw.window_alpha.show()

def main():
    app=QtGui.QApplication(sys.argv)
    main_wdw=Main_Window()
    sys.exit(app.exec_())

if __name__=="__main__":
    main()

这是test_wdw的代码:

from PySide import QtGui
from PySide import QtCore

def launch_window():
    global window

    window=QtGui.QWidget()
    window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
    window.setWindowTitle('Window 1')

    window.grid=QtGui.QGridLayout()
    window.label=QtGui.QLabel("Hello")
    window.grid.addWidget(window.label,0,0)

    window.setLayout(window.grid)
    window.setFixedSize(window.sizeHint())

class running_operation(QtCore.QObject):
    # Function I would like to run in a separate thread
    def run_operation(self):
        while True:
            print("hi")


myThread=QtCore.QThread()
operations = running_operation()
operations.moveToThread(myThread)
myThread.start()

我的问题是我不知道从哪里开始。我希望一旦启动新窗口,它就会在新线程中启动run_operation()函数。

我尝试将operations.run_operation()添加到test_wdw文件的末尾,但发生的事情是run_operation()在运行应用程序后立即开始执行,并且根本没有显示任何GUI。

在launch_window函数中添加operations.run_operation()只会导致GUI崩溃。

这让我意识到run_operation函数没有像预期的那样在单独的线程中运行。

我继续阅读文档,我相信我需要创建信号和插槽才能运行。我知道如何使用信号和插槽将QObject连接到我希望它们在触发时执行的函数但是我不明白信号和插槽与QThread和我有什么关系无法达到预期的结果。

我想解释一下QThread上下文中信号和插槽的作用以及如何使用它们,我希望对我发布的代码进行更正,以便按预期运行。< / p>

其他信息:

  • 操作系统:Windows 7 64位
  • Python版本:3.3
  • PySide版本:1.2.1

1 个答案:

答案 0 :(得分:2)

经过多次试验和错误以及一些C ++教程的解码后,我最终找到了自己的解决方案。

test_main的代码保持不变。

import sys
import test_wdw
from PySide import QtGui
from PySide import QtCore

class Main_Window(QtGui.QMainWindow):
    def __init__(self):
        super(Main_Window,self).__init__()
        self.initUI()

    def initUI(self):
        self.statusBar()

        self.new_window=QtGui.QAction("&Window alpha",self)
        self.new_window.triggered.connect(self.open_window)

        self.menu_bar=self.menuBar()
        self.menu1=self.menu_bar.addMenu('&Menu 1')
        self.menu1.addAction(self.new_window)

        # Creates a QMdiArea to manage all windows
        self.wmanager=QtGui.QMdiArea()
        self.setCentralWidget(self.wmanager)

        # Shows the main window
        self.showMaximized()

    def open_window(self):
        test_wdw.launch_window()
        test_wdw.window_alpha=self.wmanager.addSubWindow(test_wdw.window)
        # Shows the new window
        test_wdw.window_alpha.show()

def main():
    app=QtGui.QApplication(sys.argv)
    main_wdw=Main_Window()
    sys.exit(app.exec_())

if __name__=="__main__":
    main()

test_wdw的正确代码是:

from PySide import QtGui
from PySide import QtCore

def launch_window():
    global window
    # Creates a new window
    window=QtGui.QWidget()
    window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
    window.setWindowTitle('Window 1')
    # Creates a layout for the window and populates it with a Qlabel
    window.grid=QtGui.QGridLayout()
    window.label=QtGui.QLabel("Hello")
    window.grid.addWidget(window.label,0,0)
    # Sets the layout to the window and sets the size of the window
    window.setLayout(window.grid)
    window.setFixedSize(window.sizeHint())
    # Starts the thread
    myThread.start()

class running_operation(QtCore.QObject):

    # Creates a QtCore Signal for when the operation is over (if you want something to happen when the function ends processing)
    finish_operation=QtCore.Signal()
    # Wraps the function to run inside a QtCore Slot (MANDATORY !)
    @QtCore.Slot()
    # Function I wanted to run in a separate thread ! NOW IT RUNS !
    def run_operation(self):
        global counter
        counter=0
        # Setting a timer that fires every second and calling at the same time the function print_counter()
        global timer
        timer=QtCore.QTimer(self)
        timer.timeout.connect(self.print_counter)
        timer.start(1000)

    def print_counter(self):
        global counter
        global timer
        # A random function to execute just for testing purposes, the counter keeps counting up every second.
        if counter <= 3 :
            counter += 1
            print(counter)
        elif counter == 4 :
        # At seconds, we emit the finish_operation signal.
            counter += 1
            self.finish_operation.emit()
        # At 5 seconds and more, the counter keeps counting up again every second (just to check if the thread actually exited)
        else:
            counter += 1
            print(counter)


# Creates a new thread
myThread=QtCore.QThread()
# Creates the object "operations"
operations = running_operation()
# Moves the object to the thread
operations.moveToThread(myThread)
# Connects the QThread.started signal function to the run_operation() function.
myThread.started.connect(operations.run_operation)
# Connects the finish_operation signal to quitting the thread.
operations.finish_operation.connect(myThread.quit)

我在原始帖子(问题)中添加的所有内容都在代码注释中进行了解释。

我可以告诉函数在一个单独的线程中启动,因为GUI没有挂断。

我可以告诉线程正确退出,因为4秒后​​,程序不会输出任何内容。如果线程没有正确退出,并且由于我构造函数print_counter()的方式,程序将在第二个4秒停止打印到输出,然后它将再次开始打印到输出在第5秒。

我希望有些人会觉得这很有用,可以在此基础上继续发展。