Run multiple qthreads concurrently in Python

2019-08-16 05:27发布

问题:

In the attached code when you click start it creates a QSpinBox and starts counting to 20 in QThread, but if I click start again while it is counting, the first QSpinBox stops and a new one takes the focus, and both counters run in it, but I need all spins to run at the same time separately:

import sys
import time
from PySide.QtGui import *
from PySide.QtCore import *

class frmMain(QDialog):
    def __init__(self):
        QDialog.__init__(self)
        self.btStart = QPushButton('Start')
        self.btStop = QPushButton('Stop')
        self.counter = QSpinBox()
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.btStart)
        self.layout.addWidget(self.btStop)
        self.layout.addWidget(self.counter)
        self.setLayout(self.layout)
        self.btStart.clicked.connect(self.start_thread)
        self.btStop.clicked.connect(self.stop_thread)
        self.boxes = []

    def stop_thread(self):
        self.th.stop()

    def loopfunction(self, x):
        self.boxes[-1].setValue(x)

    def start_thread(self):
        self.th = thread(2)
        self.th.loop.connect(self.loopfunction)
        self.th.setTerminationEnabled(True)
        self.boxes.append(QSpinBox())
        self.layout.addWidget(self.boxes[-1])
        self.th.start()

class thread(QThread):
    loop = Signal(object)

    def __init__(self, x):
        QThread.__init__(self)
        self.x = x

    def run(self):
        for i in range(20):
            self.x = i
            self.loop.emit(self.x)
            time.sleep(0.5)

    def stop(self):
        self.stop()


app = QApplication(sys.argv)
win = frmMain()

win.show()
sys.exit(app.exec_())

回答1:

I made a few key changes:

  • kept a list of the separate threads as you were overwriting the same thread object,
  • fixed the recursive error when stopping a thread; used terminate instead,
  • thread keeps track of it's own index so it knows which spin box to update.

It's not really clear what you want to happen when you press stop, or press start after stopping, but this code should more or less work for you:

import sys
import time
from PySide.QtGui import *
from PySide.QtCore import *

class frmMain(QDialog):
    def __init__(self):
        QDialog.__init__(self)
        self.btStart = QPushButton('Start')
        self.btStop = QPushButton('Stop')
        #self.counter = QSpinBox()
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.btStart)
        self.layout.addWidget(self.btStop)
        #self.layout.addWidget(self.counter)
        self.setLayout(self.layout)
        self.btStart.clicked.connect(self.start_thread)
        self.btStop.clicked.connect(self.stop_thread)
        self.boxes = []
        self.threads = []

    def stop_thread(self):
        for th in self.threads:
            th.terminate()

    def loopfunction(self, n, index):
        self.boxes[index].setValue(n)

    def start_thread(self):
        index = len(self.threads)
        th = thread(index)
        th.loop.connect(self.loopfunction)
        th.setTerminationEnabled(True)
        th.start()
        self.threads.append(th)        
        self.boxes.append(QSpinBox())
        self.layout.addWidget(self.boxes[index])        


class thread(QThread):
    loop = Signal(int, int)

    def __init__(self, index):
        QThread.__init__(self)
        self.index = index

    def run(self):
        for n in range(20):
            self.loop.emit(n, self.index)
            time.sleep(0.5)


app = QApplication(sys.argv)
win = frmMain()

win.show()
sys.exit(app.exec_())