无法在不同线程下调用 QMessage.critical 函数 运行

Unable to invoke QMessage.critical function running under different thread

我是 运行 不同线程中的一个单独的 class 方法(由 threading.Thread 创建)。我想检查用户是否登录成功

如果用户没有登录,我要提示一个消息框

QtWidgets.QMessageBox.critical(
None,
"Wrong Credentials",
"The login credentials provided in settings are wrong. Kindly edit them and restart the application"
)

错误

QObject::setParent: Cannot set parent, new parent is in a different thread
QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?
Segmentation fault (core dumped)

代码

"""
include all related imports
"""

# Code for Ui_Dialog is not important

class Application(Ui_Dialog):
    def __init__(self, Dialog):

        self.completer = QtWidgets.QCompleter(["days", "weeks", "months"],
                                              Dialog)
        self.recent_profiles: List[Dict[str, Union[str, WebElement]]] = []
        self.parent = Dialog
        Dialog.setWindowFlags(QtCore.Qt.WindowCloseButtonHint
                              | QtCore.Qt.WindowMinimizeButtonHint)
        qtRectangle = Dialog.frameGeometry()
        centerPoint = QtWidgets.QDesktopWidget().availableGeometry().center()
        qtRectangle.moveCenter(centerPoint)
        Dialog.move(qtRectangle.topLeft())

        self.setupUi(Dialog)
        self.retranslateUi(Dialog)

        self.db = self.__load_db()

        Thread(
            target=self.__run_auto,
            args=(
                self.db["auto"],
                self.db["email"],
                self.db["password"],
            )
        ).start()

    def __run_auto(self, auto, email, password):
        if not auto["predicate"] or not auto["lim"] or not auto["template"]:
            QtWidgets.QMessageBox.critical("Error", "Auto recent sender is not configured") # the  problem occurs here
        else:
            options = Options()
            options.headless = False
            driver: WebDriver = WebDriver()
            driver.get("https://example.com/user/login")

            user: WebElement = driver.find_element_by_xpath('//*[@id="username"]')
            pasw: WebElement = driver.find_element_by_xpath('//*[@id="password"]')
            login: WebElement = driver.find_element_by_xpath(
                '/html/body/div[1]/main/div/form/div[3]/button')

            user.send_keys(email)
            pasw.send_keys(password)
            login.click()
            SUCCESS = False

            try:
                WebDriverWait(driver, 2).until(
                EC.presence_of_element_located(
                        (By.XPATH,
                            "/html/body/div[1]/main/div/form/div[2]/div/a")))
            except (NoSuchElementException, TimeoutException):
                SUCCESS = True
                pass
            if not SUCCESS:
                # the problem occurs here
                QtWidgets.QMessageBox.critical(
                    None,
                    "Wrong Credentials",
                    "The login credentials provided in settings are wrong. Kindly edit them and restart the application"
                )

            driver.quit()
        pass

    def __load_db(self):
        with open("/home/jarvis/config.json") as file:
            return json.loads(file.read())

此外,如果您能告诉我如何将父线程对象传递给子线程,我将不胜感激。

您不得从另一个线程修改或创建 GUI 元素,而必须使用信号发送信息。另一方面,不要从 Ui_Dialog 继承,因为它只是一个用于填充小部件的 class,相反,您必须从适当的小部件继承,因此当您使用 pyqtSlot 装饰器时,它确保该方法将在 GUI 线程中调用。

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        # ...

    def retranslateUi(self, Dialog):
        # ...


class DriverWorker(QtCore.QObject):
    messageChanged = QtCore.pyqtSignal(str, str)

    def start(self, *args, **kwargs):
        threading.Thread(
            target=self.__run_auto, args=args, kwargs=kwargs, daemon=True
        ).start()

    def __run_auto(self, auto, email, password):
        if not auto["predicate"] or not auto["lim"] or not auto["template"]:
            self.messageChanged.emit(
                "Error", "Auto recent sender is not configured"
            )  # the  problem occurs here
        else:
            options = Options()
            options.headless = False
            driver: WebDriver = WebDriver()
            driver.get("https://example.com/user/login")

            user: WebElement = driver.find_element_by_xpath('//*[@id="username"]')
            pasw: WebElement = driver.find_element_by_xpath('//*[@id="password"]')
            login: WebElement = driver.find_element_by_xpath(
                "/html/body/div[1]/main/div/form/div[3]/button"
            )

            user.send_keys(email)
            pasw.send_keys(password)
            login.click()
            SUCCESS = False

            try:
                WebDriverWait(driver, 2).until(
                    EC.presence_of_element_located(
                        (By.XPATH, "/html/body/div[1]/main/div/form/div[2]/div/a")
                    )
                )
            except (NoSuchElementException, TimeoutException):
                SUCCESS = True
                pass
            if not SUCCESS:
                # the problem occurs here
                self.messageChanged.emit(
                    "Wrong Credentials",
                    "The login credentials provided in settings are wrong. Kindly edit them and restart the application",
                )

            driver.quit()


class Dialog(QtWidgets.QDialog, Ui_Dialog):
    def __init__(self, parent=None):
        super(Dialog, self).__init__(parent)
        self.setupUi(self)

        self.setWindowFlags(
            QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint
        )

        qtRectangle = self.frameGeometry()
        centerPoint = QtWidgets.QDesktopWidget().availableGeometry().center()
        qtRectangle.moveCenter(centerPoint)
        self.move(qtRectangle.topLeft())

        self.db = self.__load_db()

        self.worker = DriverWorker()
        self.worker.start(self.db["auto"], self.db["email"], self.db["password"])
        self.worker.messageChanged.connect(self.on_message_changed)

    @QtCore.pyqtSlot(str, str)
    def on_message_changed(self, title, description):
        QtWidgets.QMessageBox.critical(None, title, description)

    def __load_db(self):
        with open("/home/jarvis/config.json") as file:
            return json.loads(file.read())


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Dialog()
    w.show()
    sys.exit(app.exec_())