Нужен совет, чтобы сохранить отзывчивость GUI
По сути, у меня есть графический интерфейс с некоторыми QLineEdits, "кнопкой поиска" и таблицей. Вы нажимаете кнопку, и класс DataGrabber ищет в базе данных данные, обрабатывает их, возвращает список со словарями, которыми заполнена таблица, соответственно. Эти поиски могут занять некоторое время, и мне нужно, чтобы мой графический интерфейс реагировал. Кроме того, я хочу, чтобы сообщение в строке состояния изменялось до тех пор, пока идет поиск (что-то вроде "Поиск" -> "Поиск.." -> "Поиск...", функциональность здесь не очень важна, просто о понимании того, как я могу справиться с этим правильно).
Я начал с создания потока и создал очередь между потоком, который обрабатывает поиск, и функцией, которая обрабатывает строку состояния, чтобы узнать, когда поиск будет завершен. Но это кажется действительно глупым. Тем более, что Qt предоставляет все виды инструментов, таких как QThread и Signals. Но я сейчас потерян. Каков наилучший способ справиться с отзывчивостью, если выполнять такое трудоемкое действие, как поиск в базе данных? И как лучше всего сообщить основной / дочерней ветке, что поиск завершен?
Вот уменьшенная версия того, что у меня есть сейчас:
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 ответ
Это может быть обработано с помощью сигналов. Вы можете использовать сигналы для отправки результатов в GUI и для обновления GUI прогресса.
Вот краткий пример реализации с индикатором выполнения и меткой состояния. Они могут быть включены в строку состояния, если вы хотите:
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)