Results 1 to 6 of 6

Thread: QDialog and Multithreading within PYQT5

  1. #1
    Join Date
    Jul 2013
    Posts
    36
    Thanks
    14
    Thanked 8 Times in 8 Posts
    Qt products
    Qt5

    Default QDialog and Multithreading within PYQT5

    I am trying to update a QDialogs labels with a background process in two ways: Via a STATUS class and a Result function I post in the end. I think the QDialog prevents my functions in the back to be executed, as the console output is also only generated once I close the QDialog:

    Qt Code:
    1. from PyQt5 import QtCore, QtGui, QtWidgets
    2. from PyQt5.QtGui import QBrush, QColor
    3. from PyQt5.QtCore import QTimer, QThreadPool, QRunnable, QObject, Qt, pyqtSlot, pyqtSignal
    4. from datetime import datetime
    5. import traceback, sys
    6.  
    7. class AppWindow(QDialog):
    8. def __init__(self):
    9. super().__init__()
    10. self.ui = Ui_Dialog()
    11. self.threadpool = QThreadPool()
    12. self.ui.setupUi(self)
    13. self.results = 0
    14. self.STATI = [Status(ID=i, callback=self.postStatus) for i in range(2)]
    15. self.show()
    16. self.ui.Button.clicked.connect(self.Button_clkd)
    17.  
    18. def OkButton_clkd(self):
    19. self.ui.resultBox.close()
    20.  
    21. def postStatus(self, STATUS):
    22. lables_list_qdialog = self.ui.resultBox.findChildren(QtWidgets.QLabel)
    23. lables_list_qdialog[STATUS.id].setText("Slot "+ str(STATUS.id) +" job: "+ STATUS.status)
    24. self.ui.res_0_label.setText("postStatus working")
    25.  
    26. def postResult(self, job):
    27. self.results += 1
    28. lables_list_qdialog = self.ui.resultBox.findChildren(QtWidgets.QLabel)
    29. lables_list_qdialog[job["id"]].setText(job["result"]+ "--" +job["arg"])
    30. self.ui.res_1_label.setText("postResult working")
    31.  
    32. def workFunc(self, *argv):
    33. self.ui.resultWindow(self)
    34. self.total_jobs = 0
    35.  
    36. self.jobs = {0: {"id": 0, "time": datetime.now(), "result": None, "arg": "Hello", "func": argv[0].__name__},
    37. 1: {"id": 1, "time": datetime.now(), "result": None, "arg": "World", "func": argv[0].__name__}}
    38.  
    39. for job in self.jobs:
    40. self.total_jobs += 1
    41. self.STATI[job].reset()
    42. worker = Worker(argv[0], self.jobs[job], argv[1], self.STATI[job])
    43. worker.signals.result.connect(self.postResult)
    44. self.threadpool.start(worker)
    45.  
    46. def Button_clkd(self):
    47. self.workFunc(self.test, "testing parameter")
    48.  
    49. def test(self, job, arg, STATUS):
    50. STATUS.update("Test executing")
    51. secs = datetime.now() - job["time"]
    52. job["time"] = secs.seconds
    53. job["result"] = arg
    54. return job
    55.  
    56.  
    57. class Worker(QRunnable):
    58. def __init__(self, func, *args):
    59. super().__init__()
    60. self.func = func
    61. self.args = args
    62. self.signals = WorkerSignals()
    63.  
    64. @pyqtSlot()
    65. def run(self):
    66. try:
    67. result = self.func(*self.args)
    68. print(result)
    69. except:
    70. traceback.print_exc()
    71. exctype, value = sys.exc_info()[:2]
    72. self.signals.error.emit((exctype, value, traceback.format_exc()))
    73. else:
    74. self.signals.result.emit(result)
    75. finally:
    76. self.signals.finished.emit()
    77.  
    78.  
    79. class WorkerSignals(QObject):
    80. finished = pyqtSignal()
    81. error = pyqtSignal(tuple)
    82. result = pyqtSignal(object)
    83.  
    84.  
    85. class Status():
    86. def __init__(self, ID, callback=lambda callbackparameter: None):
    87. self.status = ""
    88. self.callback = callback
    89. self.id = ID
    90.  
    91. def update(self, callbackparameter):
    92. self.status = callbackparameter
    93. self.callback(self)
    94.  
    95. def reset(self):
    96. self.status = ""
    97.  
    98.  
    99. class Ui_Dialog(object):
    100. def setupUi(self, Dialog):
    101. Dialog.setObjectName("Dialog")
    102. Dialog.resize(200, 200)
    103. Dialog.setWindowTitle("test")
    104.  
    105. self.res_0_label = QtWidgets.QLabel(Dialog)
    106. self.res_0_label.setGeometry(QtCore.QRect(10, 100, 120, 40))
    107. self.res_0_label.setText("id 0 job: ...")
    108.  
    109. self.res_1_label = QtWidgets.QLabel(Dialog)
    110. self.res_1_label.setGeometry(QtCore.QRect(10, 120, 120, 40))
    111. self.res_1_label.setText("id 1 job: ...")
    112.  
    113. self.Button = QtWidgets.QPushButton(Dialog)
    114. self.Button.setGeometry(QtCore.QRect(40, 40, 140, 40))
    115. self.Button.setObjectName("Button")
    116. self.Button.setText("WORK!")
    117.  
    118.  
    119. def resultWindow(self, parent):
    120. self.resultBox = QDialog(parent)
    121. self.resultBox.installEventFilter(parent)
    122. self.resultBox.setWindowTitle("Please Wait")
    123. self.resultBox.setGeometry(300, 300, 360, 200)
    124.  
    125. self.OkButton = QtWidgets.QPushButton(self.resultBox)
    126. self.OkButton.setGeometry(QtCore.QRect(130, 160, 100, 40))
    127. self.OkButton.setText("Ok")
    128. self.OkButton.clicked.connect(parent.OkButton_clkd)
    129.  
    130. self.job_0_label = QtWidgets.QLabel(self.resultBox)
    131. self.job_0_label.setGeometry(QtCore.QRect(10, 10, 300, 40))
    132. self.job_0_label.setText("Slot 0 job: IDLE ...")
    133.  
    134. self.job_1_label = QtWidgets.QLabel(self.resultBox)
    135. self.job_1_label.setGeometry(QtCore.QRect(10, 30, 300, 40))
    136. self.job_1_label.setText("Slot 1 job: IDLE ...")
    137.  
    138. self.resultBox.exec_()
    139.  
    140.  
    141. if __name__ == "__main__":
    142. app = QApplication(sys.argv)
    143. w = AppWindow()
    144. w.show()
    145. sys.exit(app.exec_())
    To copy to clipboard, switch view to plain text mode 

    This snippet basically describes my pain: Once executed it should update the labels in the QDialog ResultBox. But it does not. Please enlighten me Also, how can I find all labels in my Ui_Dialog class that are inside the setupUi method? .findChildren does work for resultbox (see code) but I fail to apply the principle on the Ui_Dialog.

  2. #2
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,229
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QDialog and Multithreading within PYQT5

    QDialog::exec() starts its own event loop, independent of the main application's event loop. The loop is modal, meaning that events from other event loops are not processed until the dialog's loop exits. So your worker thread might be running, but the events it sends back to the parent process aren't handled until the dialog is closed.

    A call to the static method QCoreApplication::processEvents() is the usual remedy to avoid the app freezing during long compute processes. This might work here, but I haven't studied your code in enough detail to know where to place it. Possibly in the slot that handles the finished status from the worker thread.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  3. #3
    Join Date
    Jul 2013
    Posts
    36
    Thanks
    14
    Thanked 8 Times in 8 Posts
    Qt products
    Qt5

    Default Re: QDialog and Multithreading within PYQT5

    Thanks for you answer. So I can just add a line "QCoreApplication.processEvents()" after the QDialog is initiated (that would be line 34 here)?

    My second question is still puzzeling me: How do I get a list of all labels, buttons and so on for my Ui_Dialog? It dos not have a children/findchildren method.

  4. #4
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,229
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QDialog and Multithreading within PYQT5

    So I can just add a line "QCoreApplication.processEvents()" after the QDialog is initiated (that would be line 34 here)?
    Probably would have no effect there, since nothing has happened yet in your worker thread. I would try it at the end of "postResult()".

    How do I get a list of all labels, buttons and so on for my Ui_Dialog?
    If you created the dialog's .ui file using Qt Designer, then you (or Qt Designer) would have defined a variable for every QWidget in your dialog (QLabel, QLineEdit, etc.). Open the .ui file in Qt Designer and select any widget. At the top of the Property Editor panel (Execute the View->Property Editor menu command if you don't see it), there will be a QObject entry. Under that is the "objectName" property. This is the name assigned to the widget variable. You can change this to anything you want that is more meaningful ("statusLabel" instead of "label_4" for instance). After all, will you remember next week which label "label_4" refers to?

    Since you are adding the ui into your dialog class by composition (i.e. there is a "ui" member variable for the Ui_Dialog class), then you access the label as "self.ui.statusLabel".
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  5. #5
    Join Date
    Jul 2013
    Posts
    36
    Thanks
    14
    Thanked 8 Times in 8 Posts
    Qt products
    Qt5

    Default Re: QDialog and Multithreading within PYQT5

    First: Thanks for your time. And thanks for the hint with processEvents. That worked! Here I think I was not clear enough in what I wanted to know:
    Quote Originally Posted by d_stranz View Post
    Since you are adding the ui into your dialog class by composition (i.e. there is a "ui" member variable for the Ui_Dialog class), then you access the label as "self.ui.statusLabel".
    I want to get ALL labels of the Ui_Dialog class - something like
    Qt Code:
    1. Ui_Dialog.findChildren(QWidgets.QLabel)
    To copy to clipboard, switch view to plain text mode 
    if I wanted to find all QLabels ... but this only works for the resultWindow method. How does it work on the Ui_Dialog?
    Qt Code:
    1. dir(Ui_Dialog)
    To copy to clipboard, switch view to plain text mode 
    shows no method Children/findChildren.

  6. #6
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,229
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QDialog and Multithreading within PYQT5

    The documentation for QObject::findChildren() says that the first argument should be a QString that names the type (ie. class) of the widgets you want to retrieve.

    In C++, this is implemented using templates, as the documentation shows. I do not know how this is done in python.

    In addition, you are using the name of your class (Ui_Dialog), not the name of the variable that implements that class in your dialog. So at a minimum, your code should look something like this:

    Qt Code:
    1. self.ui.findChildren( QWidgets.QLabel )
    To copy to clipboard, switch view to plain text mode 

    assuming QWidgets.QLabel is the correct syntax for the argument. Perhaps it should be "QWidgets.QLabel" (ie. a quoted string) instead.

    Or maybe you don't need the "ui" variable at all:

    Qt Code:
    1. self.findChildren( QWidgets.QLabel )
    To copy to clipboard, switch view to plain text mode 

    where "self" is your Dialog class instance.
    Last edited by d_stranz; 22nd March 2020 at 16:52.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

Similar Threads

  1. Multithreading
    By havij000 in forum Newbie
    Replies: 22
    Last Post: 21st August 2013, 14:35
  2. Replies: 1
    Last Post: 26th March 2013, 03:04
  3. Replies: 9
    Last Post: 25th March 2011, 22:22
  4. QDialog.exec() exiting without calling QDialog::accept()
    By doggrant in forum Qt Programming
    Replies: 3
    Last Post: 2nd February 2011, 12:35
  5. regarding multithreading
    By mohanakrishnan in forum Qt Programming
    Replies: 19
    Last Post: 9th December 2009, 09:21

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.