Multithreading updating with multiple windows in pyqt5

Tags: , , , ,



I’m trying to have a timer going that I can use to update values in multiple windows with pyqt5. What I have so far, the main window opens with buttons to open the other windows, but when I press either button, it crashes.

from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget,  QLineEdit
from PyQt5.QtCore import QRunnable, pyqtSlot, QTimer, QThreadPool
import sys

timer = QTimer()
timer.setInterval(1000) 

timer.start()

class Worker(QRunnable):

    def __init__(self, fn, *args, **kwargs):
        super(Worker, self).__init__()

        self.fn = fn
        self.args = args
        self.kwargs = kwargs

    @pyqtSlot()
    def run(self):

        self.fn(*self.args, **self.kwargs)

class AnotherWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.x = 0
        layout = QVBoxLayout()
        self.label = QLabel("Another Window")
        layout.addWidget(self.label)
        self.line = QLineEdit(self)
        layout.addWidget(self.line)       
        self.setLayout(layout)
        timer.timeout.connect(self.update_line)
        
    def update_line(self):
        self.x += 1
        self.line.setText(str(self.x))

class AnotherWindow2(QWidget):

    def __init__(self):
        super().__init__()
        self.x = 0
        layout = QVBoxLayout()
        self.label = QLabel("Another Window2")
        layout.addWidget(self.label)
        self.line = QLineEdit(self)
        layout.addWidget(self.line)       
        self.setLayout(layout)
        timer.timeout.connect(self.update_line)
        
    def update_line(self):
        self.x += 3
        self.line.setText(str(self.x))
        
class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        l = QVBoxLayout()
        button1 = QPushButton("Push for Window 1")
        button1.clicked.connect(self.show_new_window)
        l.addWidget(button1)

        button2 = QPushButton("Push for Window 2")
        button2.clicked.connect(self.show_new_window2)
        l.addWidget(button2)

        w = QWidget()
        w.setLayout(l)
        self.setCentralWidget(w)

        self.threadpool = QThreadPool()
        print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())

    def show_new_window(self):
        worker = Worker(self.open_AnotherWindow)
        self.threadpool.start(worker)
        
    def open_AnotherWindow(self):
        self.w = AnotherWindow()
        self.w.show()

    def show_new_window2(self, checked):
        worker2 = Worker(self.open_AnotherWindow2)
        self.threadpool.start(worker2)
        
    def open_AnotherWindow2(self, checked):
        self.w2 = AnotherWindow2()
        self.w2.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

The number of threads is printed out. I have also tried putting

        self.timer = QTimer()
        self.timer.setInterval(1000) 
        self.timer.timeout.connect(self.update_line)
        self.timer.start()

in each AnotherWindow(2) init function, but that also crashes. Maybe the timer should be in the mainwindow somehow? Though the results from it aren’t displayed there. I have seen tutorials on multithreading, and multiwindows, but not on having a window for each thread with all accessing the same data.

Answer

Why is it necessary to use multi-threading? The threads are not the magic solution, and on the contrary if their disadvantages are not known then they bring more problems than solutions. In your case it is unnecessary as you don’t have any time consuming task that blocks the GUI. Bottom line: Use tools when necessary, not when you want.

On the other hand you are creating GUI from another thread which Qt prohibits since it is not thread-safe.

class AnotherWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.x = 0
        layout = QVBoxLayout()
        self.label = QLabel("Another Window")
        layout.addWidget(self.label)
        self.line = QLineEdit(self)
        layout.addWidget(self.line)
        self.setLayout(layout)
        timer = QTimer(self)
        timer.timeout.connect(self.update_line)
        timer.start()

    def update_line(self):
        self.x += 1
        self.line.setText(str(self.x))


class AnotherWindow2(QWidget):
    def __init__(self):
        super().__init__()
        self.x = 0
        layout = QVBoxLayout()
        self.label = QLabel("Another Window2")
        layout.addWidget(self.label)
        self.line = QLineEdit(self)
        layout.addWidget(self.line)
        self.setLayout(layout)
        timer = QTimer(self)
        timer.timeout.connect(self.update_line)
        timer.start()

    def update_line(self):
        self.x += 3
        self.line.setText(str(self.x))


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        l = QVBoxLayout()
        button1 = QPushButton("Push for Window 1")
        button1.clicked.connect(self.show_new_window)
        l.addWidget(button1)

        button2 = QPushButton("Push for Window 2")
        button2.clicked.connect(self.show_new_window2)
        l.addWidget(button2)

        w = QWidget()
        w.setLayout(l)
        self.setCentralWidget(w)

    def show_new_window(self):
        self.w = AnotherWindow()
        self.w.show()

    def show_new_window2(self):
        self.w2 = AnotherWindow2()
        self.w2.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()


Source: stackoverflow