Starting a QInputDialog from inside another thread and storing the inputted value into a variable

111 views Asked by At

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_()
1

There are 1 answers

5
Alexander On

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.