Cheerio, Qt новичок здесь готов учиться ...
В настоящее время я пытаюсь создать приложение Qt, которое печатает logging.Record
объекты из нескольких QThread
экземпляров.
Для этого я разработал минимальный рабочий пример, включающий само приложение с QTextEdit
и QPushButton
, настраиваемый регистратор и произвольный длительный процесс, который должен выполняться из отдельного потока.
Код на самом деле работает! Причина, по которой я публикую это здесь, заключается в том, что я хотел бы услышать ваше мнение о том, как улучшить это. Также я надеюсь получить от вас некоторую информацию, чтобы написать для этого стабильный работающий маленький Boilerplate. Я вижу людей, имеющих эту проблему довольно часто.
Так вот код ...
app.py
Устанавливает базовый QApplication
и инициализирует QThread
, выполняя LongRunningProcess.run()
.
import sys
from PySide2 import QtCore, QtGui, QtWidgets
from logger import Logger
from lrp import LongRunningProcess
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.log = Logger(self._log_to_qtextedit)
self.create_gui()
self.thread = QtCore.QThread()
self.worker = LongRunningProcess(self._log_to_qtextedit)
self.thread.started.connect(self.worker.run)
self.worker.moveToThread(self.thread)
self.button.clicked.connect(self.run_process)
def run_process(self):
self.thread.start()
def create_gui(self):
self.centralwidget = QtWidgets.QWidget(self)
layout = QtWidgets.QVBoxLayout(self.centralwidget)
self.textedit = QtWidgets.QTextEdit(self)
# change font of output log to monospaced
font = QtGui.QFont()
font.setFamily("Courier New")
self.textedit.setFont(font)
self.button = QtWidgets.QPushButton("push me")
layout.addWidget(self.textedit)
layout.addWidget(self.button)
self.setLayout(layout)
self.setCentralWidget(self.centralwidget)
self.resize(700, 100)
self.log.info("Gui initialised...")
def on_application_shutdown(self):
self.thread.quit()
@QtCore.Slot(str)
def _log_to_qtextedit(self, msg):
self.textedit.append(msg)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
app.aboutToQuit.connect(window.on_application_shutdown)
window.show()
sys.exit(app.exec_())
logger.py
Здесь я немного запутался, как мне кажется. Для каждого вызывающего пользователя, который создает экземпляр этого регистратора, Queue
будет создано и передано QueueHandler
.
QueueListener
подключается к пользовательскому обработчику QtHandler
. Этот обработчик создает экземпляр QObject
, чтобы иметь возможность излучать сигналы каждый раз, когда вызывается метод обработчика emit()
. Этот сигнал будет подключен к переданному log_func
из Logger
.
Я также пытался разделить очередь между всеми потоками, но это привело к тому, что в графическом интерфейсе не отображалось довольно много записей.
import logging
import sys
from logging.handlers import QueueHandler, QueueListener
from queue import Queue
from PySide2 import QtCore
class SignalEmitter(QtCore.QObject):
request_textedit_update = QtCore.Signal(str)
def __init__(self):
super(SignalEmitter, self).__init__()
class QtHandler(logging.Handler):
def __init__(self):
super(QtHandler, self).__init__()
self.signal_emitter = SignalEmitter()
def emit(self, record):
record = self.format(record)
self.signal_emitter.request_textedit_update.emit(record)
class Logger(logging.Logger):
def __init__(self, log_func, level=logging.DEBUG, name="root"):
super(Logger, self).__init__(name)
queue = Queue()
self.setLevel(level)
self.handlers_ = {
"queue_handler": QueueHandler(queue),
"console_handler": logging.StreamHandler()
}
formatter = logging.Formatter("%(asctime)s %(name)-12s %(levelname)-8s %(message)s")
for handler in self.handlers_.values():
print(handler)
handler.setFormatter(formatter)
self.addHandler(handler)
qthandler = QtHandler()
qthandler.signal_emitter.request_textedit_update.connect(log_func)
qthandler.setFormatter(formatter)
self.listener = QueueListener(
queue, qthandler
)
self.listener.start()
def stop_listening(self):
self.listener.stop()
lrp.py
Ну, это может быть что угодно, что требует времени.
from logger import Logger
from PySide2.QtCore import QObject
class LongRunningProcess(QObject):
def __init__(self, guilog_func):
super(LongRunningProcess, self).__init__()
self.log = Logger(guilog_func)
def run(self):
for i in range(1000):
self.log.info(i)