Невозможно вызвать функцию QMessage.critical, запущенную в другом потоке - PullRequest
2 голосов
/ 24 февраля 2020

Я использую отдельный метод класса в другом потоке (создан 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())

Также я был бы благодарен, если бы вы могли рассказать мне, как передавать объекты родительского потока в дочерний поток.

1 Ответ

1 голос
/ 24 февраля 2020

Вы не должны изменять или создавать GUI элементы из другого потока, вместо этого вы должны использовать сигналы для отправки информации. С другой стороны, не наследуйте от Ui_Dialog, поскольку для заполнения виджета используется только класс, вместо этого вы должны наследовать от соответствующего виджета, поэтому при использовании декоратора 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_())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...