0

I have a function that is ran using the Worker method. Inside this function, there can be a condition that then requires the user to input a string and that string will be used in the function. I am able to get the QInputDialog to show up, but it freezes once I start typing something and the program crashes. I have an example of what I am doing right now below:

from PySide2.QtWidgets import QApplication, QInputDialog, QMainWindow, QPushButton
from PySide2.QtCore import *
import sys, os

class Worker_Class:
    class WorkerSignals():
        finished = Signal()
        newRecord = Signal(str)

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

            self.fn = fn
            self.args = args
            self.kwargs = kwargs
            self.signals = Worker_Class.WorkerSignals()
        
        @Slot()
        def run(self):
            try:
                self.fn(*self.args, **self.kwargs)
            except:
                pass

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("My App")

        button = QPushButton("Press Me!")

        self.setFixedSize(QSize(400, 300))

        self.setCentralWidget(button)

        self.threadpool = QThreadPool()

    def start_worker(self):
        worker = Worker_Class.Worker(get_dialog_value)
        self.threadpool.start(worker)

def get_dialog_value():
    text, ok = QInputDialog().getText(window, "Missing Module Partnumber", "Input Module Partnumber: ")

    if ok and text:
        return text
        
    return ''

QCoreApplication.setLibraryPaths([os.getcwd() + "\\virtualenv\\Lib\\site-packages\\PyQt5\\Qt5\\plugins"])

app = QApplication(sys.argv)

window = MainWindow()
window.show()

window.start_worker()

app.exec_()
  • As already told you in the comments to [the post you deleted](//stackoverflow.com/q/75302575/2001654), UI elements **can not** be created in external threads. – musicamante Feb 06 '23 at 20:57

1 Answers1

0

This is likely not the best solution, but here is an example of how you could open a QDialog and use it's return value in the same function from a thread. There is likely a better alternative to solving your problem, however given the limited context you have provided, it is difficult to know what to suggest.

The key is to use Signals trigger the launching of the dialog, so that it is opened from the main thread. Then the issue of getting the return value back to the same thread function can be accomplished by simply stalling the function until the thread can see and access the value.

class Worker_Class(QThread):
    opendialog = Signal()

    def __init__(self, window):
        super().__init__()
        self.window = window
        self.value = None

    def setvalue(self, value):
        self.value = value

    def run(self):
        self.opendialog.emit()
        while self.value is None:
            pass
        print(self.value)
        # do something with value
        # ...
        # ...

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("My App")
        button = QPushButton("Press Me!")
        self.setFixedSize(QSize(400, 300))
        self.setCentralWidget(button)

    def start_worker(self):
        self.thread = Worker_Class(self)
        self.thread.finished.connect(self.thread.deleteLater)
        self.thread.opendialog.connect(self.get_dialog_value)
        self.thread.start()

    def get_dialog_value(self):
        text, _ = QInputDialog().getText(window, "Missing Module Partnumber", "Input Module Partnumber: ")
        self.thread.setvalue(text)

app = QApplication(sys.argv)
window = MainWindow()
window.show()
window.start_worker()
app.exec_()

If you run the above code you will see that the dialog return value is successfully printed in the threads run function once the dialog closes.

Alexander
  • 16,091
  • 5
  • 13
  • 29
  • Notes: 1. QThread already has a `finished` signal and it shouldn't be overwritten; assuming a QApplication exists and its event loop is running, that default signal will be emitted when `run()` returns; 2. while not strictly forbidden, it's usually discouraged to emit a signal from another object (especially when dealing with multi threading): separation of concerns is important in OOP and objects should never act on others that are not directly (as in *direct, private attribute*) related actually; in fact, your code could be much simpler: just explicitly call `setvalue()` instead. – musicamante Feb 07 '23 at 02:13
  • Note that the above is also valid for the `self.window.opendialog.emit()` in the worker class. A more proper way to do so would be to create an `opendialog` signal for the thread, and connect it in the main window *before* starting the thread. – musicamante Feb 07 '23 at 02:14
  • Yes, precisely. Child objects (especially those that may live in different threads) should not be aware or base their existence/behavior on their "parents" unless they are *extremely* correlated; and threads normally are not. It may seem more complicated, but an `openDialog` signal for the thread, connected in the main window with the actual function, is a better choice (separation of concerns). I know that your code works perfectly fine, but that's for *explanation purposes* and we can never assume that the reader understands that context (they may believe that it's an accepted practice). – musicamante Feb 07 '23 at 03:22
  • Got it. Shoot... I need to go through all my applications and make changes then. @musicamante thanks again – Alexander Feb 07 '23 at 03:25
  • Well, you don't really need to... But it might be the case. As it always happen, rules can be broken. As long as it works, it doesn't affect the program and you don't expect to extend it, you can always keep the "bad" code: "if it ain't broke, don't fix it". There's no point in refactoring a whole, working, finished program if it does what you need and it doesn't have issues. Sure, it may have issues conceptually speaking, its logic may be conceptually "wrong", but if you're happy with what it does and you don't need to update things related to those aspects, just leave it as it is. – musicamante Feb 07 '23 at 03:34