Skip to content Skip to sidebar Skip to footer

Run Function In The Background And Update Ui

I am using PyQt to make a GUI for a project. Screenshot of GUI After inputting a number and submitting it, I need to execute the function that would run in a background, otherwise

Solution 1:

The GUI should not be updated from another thread since Qt creates a loop where the application lives, although python provides many alternatives for works with threads, often these tools do not handle the logic of Qt so they can generate problems. Qt provides classes that perform this type of tasks with QThread (low-level), but this time I will use QRunnable and QThreadPool, I have created a class that behaves the same as Process:

classProcessRunnable(QRunnable):def__init__(self, target, args):
        QRunnable.__init__(self)
        self.t = target
        self.args = args

    defrun(self):
        self.t(*self.args)

    defstart(self):
        QThreadPool.globalInstance().start(self)

Use:

self.p = ProcessRunnable(target=run, args=(user_input.displayText(), box))
self.p.start()

Also as I said before, you should not update the GUI directly from another thread, a solution is to use signals, or in this case, for simplicity, use QMetaObject.invokeMethod:

def run(user_input, log):
    text = ""if user_input == "":
        text = "Please enter a value\n"else:
        text = "Test"

    QMetaObject.invokeMethod(log,
                "append", Qt.QueuedConnection, 
                Q_ARG(str, text))

To be invoked correctly this must be a slot, for this we use a decorator:

classLogginOutput(QTextEdit):
    # ...    @pyqtSlot(str)defappend(self, text):
        self.moveCursor(QTextCursor.End)
        # ...

The complete and workable example is in the following code

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

classProcessRunnable(QRunnable):
    def__init__(self, target, args):
        QRunnable.__init__(self)
        self.t = target
        self.args = args

    defrun(self):
        self.t(*self.args)

    defstart(self):
        QThreadPool.globalInstance().start(self)

defrun(user_input, log):
    text = ""if user_input == "":
        text = "Please enter a value\n"else:
        text = "Test"

    QMetaObject.invokeMethod(log,
                "append", Qt.QueuedConnection, 
                Q_ARG(str, text))

classLogginOutput(QTextEdit):
    def__init__(self, parent=None):
        super(LogginOutput, self).__init__(parent)

        self.setReadOnly(True)
        self.setLineWrapMode(self.NoWrap)
        self.insertPlainText("")

    @pyqtSlot(str)defappend(self, text):
        self.moveCursor(QTextCursor.End)
        current = self.toPlainText()

        if current == "":
            self.insertPlainText(text)
        else:
            self.insertPlainText("\n" + text)

        sb = self.verticalScrollBar()
        sb.setValue(sb.maximum())

classApp(QWidget):
    def__init__(self):
        super().__init__()

        self.init_ui()

    definit_ui(self):
        label = QLabel('Amount')
        amount_input = QLineEdit()
        submit = QPushButton('Submit', self)
        box = LogginOutput(self)

        submit.clicked.connect(lambda: self.changeLabel(box, amount_input))

        grid = QGridLayout()
        grid.addWidget(label, 0, 0)
        grid.addWidget(amount_input, 1, 0)
        grid.addWidget(submit, 1, 1)
        grid.addWidget(box, 2, 0, 5, 2)

        self.setLayout(grid)
        self.resize(350, 250)
        self.setWindowTitle('GetMeStuff Bot v0.1')
        self.show()

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

    defchangeLabel(self, box, user_input):
        self.p = ProcessRunnable(target=run, args=(user_input.displayText(), box))
        self.p.start()
        user_input.clear()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    widget = App()
    sys.exit(app.exec_())

Solution 2:

A gui app will always need it's own blocking loop, so you are right in that you can go to either threads or process. However I believe that once you are in the Qt world you also have to use the provided tools for spawning.

Try PyQt5.QtCore.QProcess or PyQt5.QtCore.QThread.

I'm sure that you can find an example in the wild that suits you.

Post a Comment for "Run Function In The Background And Update Ui"