Why does some widgets don't update on Qt5?

361 views Asked by At

I am trying to create a PyQt5 application, where I have used certain labels for displaying status variables. To update them, I have implemented custom pyqtSignal manually. However, on debugging I find that the value of GUI QLabel have changed but the values don't get reflected on the main window.

Some answers suggested calling QApplication().processEvents() occasionally. However, this instantaneously crashes the application and also freezes the application.

Here's a sample code (all required libraries are imported, it's just the part creating problem, the actual code is huge):

from multiprocessing import Process

def sub(signal):
    i = 0
    while (True):
        if (i % 5 == 0):
            signal.update(i)

class CustomSignal(QObject):
    signal = pyqtSignal(int)
    def update(value):
        self.signal.emit(value)

class MainApp(QWidget):
    def __init__(self):
        super().__init__()
        self.label = QLabel("0");
        self.customSignal = CustomSignal()
        self.subp = Process(target=sub, args=(customSignal,))
        self.subp.start()
        self.customSignal.signal.connect(self.updateValue)

    def updateValue(self, value):
        print("old value", self.label.text())
        self.label.setText(str(value))
        print("new value", self.label.text())

The output of the print statements is as expected. However, the text in label does not change.

The update function in CustomSignal is called by some thread. I've applied the same method to update progress bar which works fine.

Is there any other fix for this, other than processEvents()?

The OS is Ubuntu 16.04.

2

There are 2 answers

0
Himanshu Shekhar On

The key problem lies in the very concept behind the code.

Processes have their own address space, and don't share data with another processes, unless some inter-process communication algorithm is used. Perhaps, multithreading module was used instead of threading module to bring concurrency to avoid Python's GIL and speedup the program. However, subprocess has cannot access the data of parent process.

I have tested two solutions to this case, and they seem to work.

  • threading module: No matter threading in Python is inefficient due to GIL, but it's still sufficient to some extent for basic concurrency demands. Note the difference between concurrency and speedup.

  • QThread: Since you are using PyQt, there's isn't any issue in using QThread, which is a better option because it takes concurrency to multiple cores taking advantage of operating system's system call, rather than Python in the middle.

0
Dominic McLoughlin On

Try adding

self.label.repaint()

immediately after updating the text, like this:

self.label.setText(str(value))
self.label.repaint()