Need advice to keep GUI responsive

我只是一个虾纸丫 提交于 2021-01-29 13:31:13

问题


Basically, what I have is a GUI with some QLineEdits, a "search button" and a table. You hit the button and a class called DataGrabber searches a database for data, processes them, returns a list with dictionaries with which the table is filled, accordingly. These searches can take a while and I need to keep my GUI responsive. Also, I want the statusbar message to change as long as the search is going on (something like "Searching." -> "Searching.." -> "Searching...", the functionality is not rly important here, it's just about understanding how I can handle this properly).

I started with threading everything and created a queue between the thread that handles the search and the function that handles the statusbar, to know when the search is done. But that seems really goofy. Especially since Qt provides all kind of tools, like QThread and Signals. But I'm rly lost right now. What would be the best way to handle the responsiveness when having such a time-consuming action like a database search? And what would be the best way to tell the main/child thread that the search is finished?

Here is a reduced version of what I have right now:

class GUI(Ui_MainWindow, InitGlobals):
    def __init__(dialog):
        ...
        self.start_button_3.clicked.connect(\
                 lambda: self.start_search(self.result_tab_3))
        ...
    def start_search():
       ...
       search_paras = [3,
                       self.name_box_3.text(),
                       self.project_combo_3.currentText(),
                       self.voltage_box.text(),
                       self.volume_box.text()]
       queue = Queue()
       thr = Thread(target=self.search_thread, args=(queue, search_paras,))
       thr.start()
       data_lst = statusbar_load(queue, self.statusbar, option="loader")
       thr.join()
       self.statusbar.showMessage("Search completed...")

       for dic in data_lst:
            self.write_to_table(dic, tab)

    def search_thread(self, queue, args):
        grabber = DataGrabber(self.db_config)
        ...
        if args[0] == 3:
            queue.put(grabber.alpha_search(args[1], args[2],
                                           args[3], args[4]))
        queue.task_done()

    def statusbar_load(queue, statusbar_obj, option="spinner"):
        data = None
        i = 0
        while data is None:
            try:
                data = queue.get(timeout=0.1)
            except Empty:
                if option == "spinner":
                    status = ["-", "\\", "|", "/"]
                    statusbar_obj.showMessage("Searching  [" + status[i%4] + "]")
                ....
                i = i + 1
        return data

回答1:


This can be handled with signals. You can use signals to send the results to the GUI and to update the GUI of the progress.

Here is a quick example of the implementation with a progress bar and status label. These can be included in a status bar if you would like:

class GUITest(QtWidgets.QWidget):

    def __init__(self):
        QtWidgets.QWidget.__init__(self)

        layout = QtWidgets.QGridLayout()

        self.button = QtWidgets.QPushButton('Run')
        self.button.clicked.connect(self.run)

        self.result_box = QtWidgets.QTextBrowser()

        self.label = QtWidgets.QLabel()

        self.progress_bar = QtWidgets.QProgressBar()
        self.progress_bar.setVisible(False)
        self.progress_bar.setMinimum(0)
        self.progress_bar.setMaximum(100)
        self.progress_bar.setValue(0)

        layout.addWidget(self.button)
        layout.addWidget(self.result_box)
        layout.addWidget(self.label)
        layout.addWidget(self.progress_bar)

        self.setLayout(layout)

    def run(self):
        self.progress_bar.setVisible(True)
        self.label.setText('Searching...')
        self.thread = QtCore.QThread()
        self.data_grabber = DataGrabber()
        self.data_grabber.moveToThread(self.thread)
        self.data_grabber.update_progress.connect(self.update_progress_bar)
        self.data_grabber.results.connect(self.display_results)
        self.data_grabber.finished.connect(self.complete)
        self.data_grabber.finished.connect(self.thread.quit)
        self.data_grabber.finished.connect(self.data_grabber.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.thread.started.connect(self.data_grabber.run)
        self.thread.start()

    def update_progress_bar(self):
        self.progress_bar.setValue(self.progress_bar.value() + 1)

    def complete(self):
        self.label.setText('Complete')
        self.progress_bar.setVisible(False)

    def display_results(self, results):
        for key, value in results.items():
            self.result_box.append('%s: %s' % (key, value))


class DataGrabber(QtCore.QObject):

    finished = QtCore.pyqtSignal()
    update_progress = QtCore.pyqtSignal()
    results = QtCore.pyqtSignal(dict)  # set the type of object you are sending

    def __init__(self):
        super().__init__()
        self.count = 0

    def run(self):
        # search database here and emit update_progress when appropriate
        while self.count <= 100:
            self.update_progress.emit()
            self.count += 1
            time.sleep(0.02)
        self.send_results()  # when done, send the results
        self.finished.emit()

    def send_results(self):
        results = {'one': 'Result One', 'two': 'Result Two', 'three': 'Result Three'}
        self.results.emit(results)


来源:https://stackoverflow.com/questions/50705536/need-advice-to-keep-gui-responsive

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!