从pyqt线程内启动多个线程

时间:2014-04-30 13:12:06

标签: python multithreading pyqt pyside

我目前有一个脚本,将文件夹(数千个文件)读入一个列表,然后将其拆分为4个子列表。然后我为每个列表运行一个线程。这在python脚本中很容易实现。

thread1fileList, thread2fileList, thread3fileList, thread4fileList = load.sortFiles(fileList)
threads = [threading.Thread(target=load.threadLoad, args=(fileList, ))
for fileList in (thread1fileList, thread2fileList, thread3fileList, thread4fileList)]
for thread in threads:
    thread.start()
for thread in threads:
    thread.join()

但是我现在正在使用Pyside将此代码移动到GUI。

我已经能够创建一个读取文件夹的线程(以确保GUI仍然响应)但我无法从Qt.Thread中启动4个新线程来处理文件列表。

这是我的核心代码

self.unzipThread = UnzipThread()
self.unzipThread.start()
self.unzipThread.finished.connect(self.finishUp()) #finsihUp provides the final page of the wizard


class UnzipThread(QtCore.QThread):
    def __init__(self):
        QtCore.QThread.__init__(self)

    def run(self):
        for dirname, dirnames, filenames in os.walk(directorypath):
            for filename in filenames:
                if filename.endswith(".zip"):
                   fileList.append(filename)
                   count = count +1

如果我尝试在run函数中添加启动线程,则会导致错误,指出不允许这样做。

我还能做到这一点吗?

由于

EDIT ##

完整示例

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
from PySide import QtGui, QtCore
import os

class Example(QtGui.QWidget):

def __init__(self):
    super(Example, self).__init__()

    self.initUI()

def initUI(self):

    runButton = QtGui.QPushButton("Run")
    runButton.clicked.connect(self.unzip)
    exitButton = QtGui.QPushButton("Exit")
    exitButton.clicked.connect(QtCore.QCoreApplication.instance().quit)
    hbox = QtGui.QHBoxLayout()
    hbox.addWidget(runButton)
    hbox.addStretch(1)
    hbox.addWidget(exitButton)
    titleBox = QtGui.QVBoxLayout()
    titleLabel = QtGui.QLabel(self.tr("Find Zip File"))

    searchBox = QtGui.QHBoxLayout()
    self.fileLabel = QtGui.QLabel(self.tr("Location: "))
    self.fileLineEdit = QtGui.QLineEdit()
    self.fileDialogBtn =  QtGui.QPushButton("Browse")
    self.fileDialogBtn.clicked.connect(self.openDirectoryDialog)
    searchBox.addWidget(self.fileLabel)
    searchBox.addWidget(self.fileLineEdit)
    searchBox.addWidget(self.fileDialogBtn)

    titleBox.addWidget(titleLabel)
    titleBox.addStretch(1)
    titleBox.addLayout(searchBox)
    titleBox.addStretch(1)
    titleBox.addLayout(hbox)

    self.setLayout(titleBox)

    self.resize(500, 300)
    self.center()
    self.setWindowTitle('Example')
    self.show()

def unzip(self):
    self.unzipThread = UnzipThread(self.filePath)
    self.unzipThread.start()
    self.unzipThread.finished.connect(self.finishUp)

def openDirectoryDialog(self):
    flags = QtGui.QFileDialog.DontResolveSymlinks | QtGui.QFileDialog.ShowDirsOnly
    directory = QtGui.QFileDialog.getExistingDirectory(self, "Open Directory", os.getcwd(),flags)
    self.fileLineEdit.setText(directory)
    self.filePath = self.fileLineEdit.text()

def center(self):
    qr = self.frameGeometry()
    cp = QtGui.QDesktopWidget().availableGeometry().center()
    qr.moveCenter(cp)
    self.move(qr.topLeft())

def closeEvent(self, event):
    reply = QtGui.QMessageBox.question(self, 'Message',
        "Are you sure to quit?", QtGui.QMessageBox.Yes |
        QtGui.QMessageBox.No, QtGui.QMessageBox.No)

    if reply == QtGui.QMessageBox.Yes:
        event.accept()
    else:
        event.ignore()

def finishUp(self):#yet to code up to GUI log windows
    print "finished"

class UnzipThread(QtCore.QThread):
def __init__(self, filepath):
    QtCore.QThread.__init__(self)

    self.filePath = filepath

def run(self):
    fileList = []
    count = 0
    for dirname, dirnames, filenames in os.walk(self.filePath):
        for filename in filenames:
            if filename.endswith(".shp"):
                fileList.append(filename)
                count += 1
    print count

    list1 = []
    list2 = []

    for shpfile in fileList:
        if "line" in shpfile:
            list1.append(shpfile)
        elif "point" in shpfile:
            list2.append(shpfile)
        else:
            pass

    self.processThread1 = ProcessThread1(list1)
    self.processThread1.start()
    self.processThread1.finished.connect(self.threadCount)

    self.processThread2 = ProcessThread2(list2)
    self.processThread2.start()
    self.processThread2.finished.connect(self.threadCount)

    return

def threadCount(self):
    print "got here"

class ProcessThread1(QtCore.QThread):
def __init__(self, filelist):
    QtCore.QThread.__init__(self)

    self.filelist = filelist

def run(self):
    count = 0
    for textfile in self.filelist:
        count +=1

    print "thread 1 count %s" %(count)

    return

class ProcessThread2(QtCore.QThread):
def __init__(self, filelist):
    QtCore.QThread.__init__(self)

    self.filelist = filelist

def run(self):
    count = 0
    for textfile in self.filelist:
        count +=1

    print "thread 2 count %s" %(count)

    return

def main():

app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())


if __name__ == '__main__':
    main()

在剥离我的代码很多以提供此示例时,它似乎从我的QT.Thread内部运行一个线程但是初始线程认为它已经在运行的线程完成之前完成。

所以结果我看起来像这样

5635 完成线程1计数2858

线程2计算2777在哪里

这里

所以你可以看到评论“完成”出现在其他代码“线程数1”之前,这证明它认为它已经完成。

无论如何,我可以让他们进行交流吗?

感谢

编辑2

所以我现在已经改变了我的代码以使用QObjects和movetoThread但是如何正确地确定两个子线程都已完成。这是我的代码。我不得不使用线程计数器和While循环来确定两个子线程是否都已完成。必须有更好的方法??

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
from PySide import QtGui, QtCore
import os
from PySide.QtCore import Signal as pyqtSignal
import time

class Example(QtGui.QWidget):

    def __init__(self):
        super(Example, self).__init__()

        self.initUI()

    def initUI(self):

        runButton = QtGui.QPushButton("Run")
        runButton.clicked.connect(self.unzip)
        exitButton = QtGui.QPushButton("Exit")
        exitButton.clicked.connect(QtCore.QCoreApplication.instance().quit)
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(runButton)
        hbox.addStretch(1)
        hbox.addWidget(exitButton)
        titleBox = QtGui.QVBoxLayout()
        titleLabel = QtGui.QLabel(self.tr("Find Zip File"))

        searchBox = QtGui.QHBoxLayout()
        self.fileLabel = QtGui.QLabel(self.tr("Location: "))
        self.fileLineEdit = QtGui.QLineEdit()
        self.fileDialogBtn =  QtGui.QPushButton("Browse")
        self.fileDialogBtn.clicked.connect(self.openDirectoryDialog)
        searchBox.addWidget(self.fileLabel)
        searchBox.addWidget(self.fileLineEdit)
        searchBox.addWidget(self.fileDialogBtn)

        titleBox.addWidget(titleLabel)
        titleBox.addStretch(1)
        titleBox.addLayout(searchBox)
        titleBox.addStretch(1)
        titleBox.addLayout(hbox)

        self.setLayout(titleBox)

        self.resize(500, 300)
        self.center()
        self.setWindowTitle('Example')
        self.show()

    def unzip(self):
        thread = self.thread = QtCore.QThread()
        worker = self.worker = Worker(self.filePath)
        worker.moveToThread(thread)
        worker.finished.connect(worker.deleteLater)
        worker.finished.connect(thread.quit)
        thread.started.connect(worker.run)
        thread.finished.connect(self.finishUp)
        thread.start()

    def openDirectoryDialog(self):
        flags = QtGui.QFileDialog.DontResolveSymlinks | QtGui.QFileDialog.ShowDirsOnly
        directory = QtGui.QFileDialog.getExistingDirectory(self, "Open Directory", os.getcwd(),flags)
        self.fileLineEdit.setText(directory)
        self.filePath = self.fileLineEdit.text()

    def center(self):
        qr = self.frameGeometry()
        cp = QtGui.QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def closeEvent(self, event):
        reply = QtGui.QMessageBox.question(self, 'Message',
            "Are you sure to quit?", QtGui.QMessageBox.Yes |
            QtGui.QMessageBox.No, QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

    def finishUp(self):#yet to code up to GUI log windows
        print "unzip thread finished"

class Worker(QtCore.QObject):
    finished = pyqtSignal()

    def __init__(self, filepath):
        QtCore.QObject.__init__(self)

        self.filePath = filepath

    def run(self):
        self.threadcount = 0
        print "finding files"
        fileList = []
        count = 0
        for dirname, dirnames, filenames in os.walk(self.filePath):
            for filename in filenames:
                if filename.endswith(".shp"):
                    fileList.append(filename)
                    count += 1
        print count

        self.list1 = []
        self.list2 = []

        for shpfile in fileList:
            if "line" in shpfile:
                self.list1.append(shpfile)
            elif "point" in shpfile:
                self.list2.append(shpfile)
            else:
                pass

        thread1 = self.thread1 = QtCore.QThread()
        worker1 = self.worker1 = Process1(self.list1)
        worker1.moveToThread(thread1)
        worker1.finished.connect(worker1.deleteLater)
        worker1.finished.connect(thread1.quit)
        thread1.started.connect(worker1.run)
        thread1.finished.connect(self.finishUp)
        thread1.start()


        thread2 = self.thread2 = QtCore.QThread()
        worker2 = self.worker2 = Process2(self.list2)
        worker2.moveToThread(thread2)
        worker2.finished.connect(worker2.deleteLater)
        worker2.finished.connect(thread2.quit)
        thread2.started.connect(worker2.run)
        thread2.finished.connect(self.finishUp)
        thread2.start()

    def finishUp(self):
        self.threadcount += 1
        while True:
            if self.threadcount != 2:
                break
            else:
                print "extra threads finished"
                self.finished.emit()
                break


class Process1(QtCore.QObject):
    finished = pyqtSignal()
    def __init__(self, filelist):
        QtCore.QObject.__init__(self)

        self.filelist = filelist

    def run(self):
        count = 0
        for textfile in self.filelist:
            count +=1

        print "thread 1 count %s" %(count)

        self.finished.emit()


class Process2(QtCore.QObject):
    finished = pyqtSignal()
    def __init__(self, filelist):
        QtCore.QObject.__init__(self)

        self.filelist = filelist

    def run(self):
        count = 0
        for textfile in self.filelist:
            count +=1

        time.sleep(15)

        print "thread 2 count %s" %(count)

        self.finished.emit()

def main():

    app = QtGui.QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

1 个答案:

答案 0 :(得分:1)

首先,注释“finished”出现在“thread count x”之前,因为UnzipThread确实在ProcessingThreads之前完成。这是预期的。

其次我不能说是否存在沟通问题但请务必阅读true usage of QThreads。由于信令问题,不建议对QThread进行子类化。最好使用worker对象并通过QObject:movetoThread()将它们移动到Threads。

如果仍有疑问,请在所有可能的位置打印QThread.currentThread(),并检查哪个代码实际在哪个线程中运行。