PyQt5 Unexpected Error "QObject::connect:" при использовании отдельного QThread

При использовании отдельного потока для синхронизации с БД в PyQt5 появляется пара ошибок:

QObject::connect: Cannot queue arguments of type 'QTextCharFormat'
(Make sure 'QTextCharFormat' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)

Долго пытался понять откуда берется сообщения и вычислил виновника - HistoryBox() текстовое поле для вывода результатов (логов), но в чем именно причина не понимаю. Подскажите что за ошибка и как пофиксить? (для воспроизведения события код максимально упростил)

main.py

import logging, sys
from PyQt5.QtCore import pyqtSignal, QObject, Qt, QThread
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from history import HistoryBox


logger = logging.getLogger("Test")


class AppsSyncWorker(QObject):
    sync_progress_changed_signal = pyqtSignal(int)
    sync_progress_result_signal = pyqtSignal(bool)

    def __init__(self, config_section: str = None, config_id: int = None, parent=None):
        super(AppsSyncWorker, self).__init__(parent)
        self.__config_section = config_section
        self.__config_id = config_id

    def task_sync_run(self):
        logging.info("Sync Run")

        progress_counter = 0
        progress_result = []
        for item in range(10):
            progress_counter += 1
            progress_result.append(True)
            self.sync_progress_changed_signal.emit(progress_counter)
            QThread.msleep(100)
        self.sync_progress_result_signal.emit(all(progress_result))
        logging.info("Sync Done")


class MainInterface(QMainWindow):

    def __init__(self):
        super().__init__()

        self.sync_thread = None

        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)

        # Flexible Window with Mim Size Limitation
        self.setMinimumHeight(350)
        self.setMinimumWidth(550)

        self.run_button = QPushButton("Run")
        self.run_button.clicked.connect(self.__sync_worker_start)
        self.sync_progressbar = QProgressBar()
        self.sync_progressbar.setMaximum(10)

        g_layout = QGridLayout(self.centralWidget)
        g_layout.addWidget(self.run_button, 1, 1)
        g_layout.addWidget(self.sync_progressbar, 1, 2)
        g_layout.addWidget(HistoryBox(), 2, 1, 1, 2)

    def __sync_worker_start(self):

        if not self.sync_thread:
            self.run_button.setDisabled(True)

            self.sync_thread = QThread()

            self.worker = AppsSyncWorker()
            self.worker.moveToThread(self.sync_thread)

            self.sync_thread.started.connect(self.worker.task_sync_run)
            self.sync_thread.start()

            self.worker.sync_progress_changed_signal.connect(self.sync_progressbar.setValue, Qt.QueuedConnection)
            self.worker.sync_progress_result_signal.connect(self.__sync_worker_end)

    def __sync_worker_end(self, sync_result: bool):

        self.sync_thread.terminate()
        self.sync_thread.wait(500)

        self.sync_progressbar.setFormat(
            "Synchronization {result}".format(result="Success" if sync_result else "Error Occurred"))
        if self.sync_thread.isFinished():
            self.run_button.setDisabled(False)
            self.sync_thread = None


if __name__ == '__main__':

    app = QApplication(sys.argv)
    app.setStyle("Fusion")
    ex = MainInterface()
    ex.show()
    sys.exit(app.exec_())

history.py

import logging
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from log_settings import MAIN_LOG_FORMAT


class HistoryBox(QWidget):

    """
        Main Tabs - History Tab Interface Class
        """

    def __init__(self, parent=None):
        super(History, self).__init__(parent)

        history_handler = HistoryQTextEditHandler(self)
        history_handler.setFormatter(logging.Formatter(MAIN_LOG_FORMAT))
        # TODO: Change Log Level Management
        history_handler.setLevel(logging.INFO)

        logging.getLogger().addHandler(history_handler)

        history_text = history_handler.history_text

        history_box = QGroupBox("History")
        history_box_h_layout = QHBoxLayout(history_box)
        history_box_h_layout.addWidget(history_text)

        tab_v_layout = QVBoxLayout(self)
        tab_v_layout.addWidget(history_box)


class HistoryQTextEditHandler(logging.Handler):

    """
        Main Tabs - History QTextEdit Logging Handler
        """

    def __init__(self, parent):
        super().__init__()

        self.history_text = QTextEdit(parent)
        self.history_text.setReadOnly(True)
        self.history_text.setLineWrapMode(QTextEdit.NoWrap)

    def emit(self, record: logging.LogRecord):

        """
        Emit Log Signal
        :param record:
        :return:

        Other way to set the text color
        log_message = "<span style='color:#ff00ff;'>{log_message}</span>"
        """

        cursor = QTextCursor(self.history_text.document())
        cursor.setPosition(0)
        self.history_text.setTextCursor(cursor)

        log_message = self.format(record)
        if "DEBUG" in log_message:
            self.history_text.setTextColor(QColor("#ff00ff"))     # magenta
        elif "INFO" in log_message:
            self.history_text.setTextColor(QColor("#00ffff"))     # cyan
        elif "WARNING" in log_message:
            self.history_text.setTextColor(QColor("#ffff00"))     # yellow
        else:
            self.history_text.setTextColor(QColor("#ff0000"))     # red
        self.history_text.insertPlainText(log_message + "\n")

log_settings.py

import logging


MAIN_LOG_FORMAT = "[%(asctime)s]:[%(filename)s:%(lineno)d] - %(levelname)s - %(name)s - %(message)s"

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

stream_handler = logging.StreamHandler()
stream_handler.setFormatter(logging.Formatter(MAIN_LOG_FORMAT))
stream_handler.setLevel(logging.INFO)

logger.addHandler(stream_handler)

Ответы (0 шт):