Search code examples
pythonpython-3.xpyqtpyqt5qthread

How to generate an exception in a different thread


good night I am trying to generate an exception in this example code, the exception of generates if at the time of changing the text in the QLineEdit the text could not be converted into a number.

However, when I'm running, I get an error and the program stops

error:

QObject::setParent: Cannot set parent, new parent is in a different thread

this is the code:

from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox
from PyQt5 import QtCore
from PyQt5 import uic 
import threading

class TheThread(threading.Thread):
    def __init__(self,text):
        threading.Thread.__init__(self)
        self.tx = text
    def run(self):
        try:
            print(int(self.tx),"number")
        except:
            QMessageBox.critical(None,"Error","Error ",QMessageBox.Ok)



class Principal(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        uic.loadUi("test.ui",self)


        self.lineEdit.textChanged.connect(lambda:self.evalNumeros(self.lineEdit.text()))

    @QtCore.pyqtSlot(str)
    def evalNumeros(self,texto):
        print(texto)
        try:
            threadClass = TheThread(texto)
            threadClass.start()
        except:
            print("error")

app = QApplication([])
p = Principal()
p.show()
app.exec_()

enter image description here

this is the filu.ui for this code test.ui

<?xml version="1.0" encoding="UTF-8"?>
            <ui version="4.0">
             <class>MainWindow</class>
             <widget class="QMainWindow" name="MainWindow">
              <property name="geometry">
               <rect>
                <x>0</x>
                <y>0</y>
                <width>641</width>
                <height>479</height>
               </rect>
              </property>
              <property name="windowTitle">
               <string>MainWindow</string>
              </property>
              <widget class="QWidget" name="centralwidget">
               <widget class="QLineEdit" name="lineEdit">
                <property name="geometry">
                 <rect>
                  <x>200</x>
                  <y>100</y>
                  <width>191</width>
                  <height>20</height>
                 </rect>
                </property>
               </widget>
               <widget class="QLabel" name="yes">
                <property name="geometry">
                 <rect>
                  <x>170</x>
                  <y>150</y>
                  <width>111</width>
                  <height>31</height>
                 </rect>
                </property>
                <property name="styleSheet">
                 <string notr="true">color:white;
            background:red;</string>
                </property>
                <property name="text">
                 <string/>
                </property>
               </widget>
               <widget class="QLabel" name="no">
                <property name="geometry">
                 <rect>
                  <x>300</x>
                  <y>150</y>
                  <width>111</width>
                  <height>31</height>
                 </rect>
                </property>
                <property name="styleSheet">
                 <string notr="true">color:white;
            background:green;</string>
                </property>
                <property name="text">
                 <string/>
                </property>
               </widget>
              </widget>
              <widget class="QMenuBar" name="menubar">
               <property name="geometry">
                <rect>
                 <x>0</x>
                 <y>0</y>
                 <width>641</width>
                 <height>21</height>
                </rect>
               </property>
              </widget>
              <widget class="QStatusBar" name="statusbar"/>
             </widget>
             <resources/>
             <connections/>
            </ui>

Solution

  • The problem is that you can not invoke the GUI directly in another thread, so there are 2 possible solutions: use a signal to invoke a method that shows the QMessageBox or use QMetaObject.invokeMethod to invoke that method, in this case I will use the second since the first one there are many examples in SO.

    from PyQt5 import QtCore, QtWidgets, uic
    from PyQt5 import uic 
    import threading
    
    class TheThread(threading.Thread):
        def __init__(self, text, obj):
            threading.Thread.__init__(self)
            self.tx = text
            self.obj = obj
    
        def run(self):
            try:
                print(int(self.tx),"number")
            except:
                QtCore.QMetaObject.invokeMethod(self.obj, "onError", 
                    QtCore.Qt.QueuedConnection,
                    QtCore.Q_ARG(str, "Error"),
                    QtCore.Q_ARG(str, "Error"))
    
    
    class Principal(QtWidgets.QMainWindow):
        def __init__(self):
            super(Principal, self).__init__()
            uic.loadUi("test.ui",self)
            self.lineEdit.textChanged.connect(self.evalNumeros)
    
        @QtCore.pyqtSlot(str)
        def evalNumeros(self,texto):
            threadClass = TheThread(texto, self)
            threadClass.start()
    
        @QtCore.pyqtSlot(str, str)
        def onError(self, title, text):
            QtWidgets.QMessageBox.critical(None, title, text, QtWidgets.QMessageBox.Ok)
    
    
    app = QtWidgets.QApplication([])
    p = Principal()
    p.show()
    app.exec_()