Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Problem with second window and actions/signals



  • Hi!
    I am a newbie with python/Qt tools and widgets, and I am having problems implementing a GUI with several windows.
    My intention at the moment is that there is a button ("actionModify_room") in the Main Window the displays a second window for setting room dimensions and sensor locations. I am able to open the second window and display some graphics (a circle) on it. However, I am trying to validate the text input of a QLineEdit (several eventually) that is supposed to later place an object in the graphics, but when validate takes place it crashes. Also, I added a button for refreshing the graphics after all inputs are set (call updateGrahics), but in this case it even crashes before loading the second window .

    This is the code:

    
    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QWidget
    from PyQt5.QtGui import QDoubleValidator, QValidator
    import sys
    
    from MainWindowDesign import Ui_MainWindow 
    from RoomDialogDesign import Ui_Dialog
    
    
    
    
    class Window(QMainWindow, Ui_MainWindow):
        def __init__(self):
            super().__init__()
            self.setupUi(self)
            self.win = None # no dialog window yet
            self.connectSignalSlots()
            
        def connectSignalSlots(self):
            self.actionAbout.triggered.connect(self.about)
            self.actionModify_room.triggered.connect(self.OpenDialog)
            
        def about(self):
            QMessageBox.about(self, "BLE  information")
        
        def OpenDialog(self):
            if self.win is None:
                self.win = CustomDialog()   #keep reference to the window
            self.win.show()                 
            
    class CustomDialog(QWidget, Ui_Dialog): 
        def __init__(self):        
            super().__init__()
            QtWidgets.QMainWindow.__init__(self)    # to check why this is necessary
            self.setupUi(self) 
            self.updateGraphics()
            self.connectSignalSlots() 
            
        
        def validate(self):
            validationRule = QDoubleValidator(0,999,2) 
            validationRule.validate(self.text(), 10) 
            if validationRule.validate(self.text(), 10)[0] == QValidator.Acceptable:
                self.setFocus()
            else:
                self.setText('')
                
              
        def updateGraphics(self):                  
            scene = QtWidgets.QGraphicsScene()
            self.graphicsView.setScene(scene)
            pen = QtGui.QPen(QtCore.Qt.darkBlue)
            Brush = QtGui.QBrush(QtCore.Qt.darkBlue)
            Brush.setStyle(QtCore.Qt.SolidPattern)
                     
            scene.addEllipse(10,10,10,10,pen,Brush)
        
        def connectSignalSlots(self):
            self.pushButton.triggered.connect(self.updateGraphics)  # if commented no problems at all
            self.lineEdit_1.editingFinished.connect(self.validate) #second window can load but validation makes the app to crash
            
            
            
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        win = Window()
        win.show() 
        app.exec_()   
    

    Could you give me some guidance here? Since there is not any error, I am a bit lost here.

    Thanks in advance!



  • @Cocor said in Problem with second window and actions/signals:

    Regarding your feedback, I imagine that you were referring to the line of QtWidgets.QMainWindow.init(self). In previous debugging sessions somehow the Ui_Dialog was not getting displayed properly, until I used that line. I guess there was something else incorrect, because I just saw it is not necessary anymore to display Ui_Dialog properly. Feel free to correct me I am doing something not pythonic or wrong (I am trying to learn here).

    Calling an unrelated __init__ looks wrong. I can't comment on the pythonic part. Removing the line was the right move. If using python's classes is unfamiliar territory, https://docs.python.org/3/tutorial/index.html has a decent looking classes section.

    Speaking of Ui_Dialog and Ui_MainWindow, what are these? If it's not code coming from a standard package, either include the source or figure out how to phrase the question without it. There's a significant chance that such code is related to the problem, but we can't tell without seeing it.

        def validate(self):
            validationRule = QDoubleValidator(0,999,2) 
            validationRule.validate(self.text(), 10) 
            if validationRule.validate(self.text(), 10)[0] == QValidator.Acceptable:
    

    This looks like a copy and paste or formatting error. Otherwise, why validate the same input twice, and then treat the enum return value from the second invocation as an array of enums?

    You are on the right lead on this. I am following some youtube tutorials, and concretely in this case one about QDoublevalidator. The first invocation was my mistake (I was using a print), and for the second invocation I was using the array following the example. I though the invocation was returning an array as the video mentioned.

    PyQt's QValidator.validate() return type doesn't match the C++ QValidator::validate()
    !?!
    That surprised me. PyQt returns a tuple that also includes potentially modified input parameters, which the C++ api handles through non-const reference parameters. There is of course at least one stack overflow thread.

    Also I noticed (through pdb.set_trace) that I was referring to self ("def validate(self)") inside the CustomDialog class, therefore it did not have any self.text attribute. I fixed this creating a new class for QLineEdit:

    class Text(QLineEdit):      
        def validating(self):
            pdb.set_trace()
            validationRule = QDoubleValidator(0,999,2) 
            
            if validationRule.validate(self.text(), 10)[0] == QValidator.Acceptable:
                self.setFocus()
            else:
                self.setText('')
    

    However when this call is performed (fixed line to "self.lineEdit_1.editingFinished.connect(Text.validating) "), the app crashes without any feedback. I feel like I am doing wrong use of parents here (an probably along the whole code). Please feel free to comment,

    Text.validating is an unbound reference to an instance method (I think that's the terminology). Calling it doesn't work because there is no 'self'.
    The usage should look more like:

    instance = Text()
    ...
    self.lineEdit_1.editingFinished.connect(instance.Validating)
    

    The class discussion in the python tutorial should cover the how and why.

    def updateGraphics(self):                  
        scene = QtWidgets.QGraphicsScene()
        self.graphicsView.setScene(scene)
        pen = QtGui.QPen(QtCore.Qt.darkBlue)
        Brush = QtGui.QBrush(QtCore.Qt.darkBlue)
        Brush.setStyle(QtCore.Qt.SolidPattern)
                 
        scene.addEllipse(10,10,10,10,pen,Brush)
    

    scene doesn't have a parent, and isn't an attribute of the CustomDialog instance. My expectation is that it will be destroyed sometime after updateGraphics() returns, and a future call to QGraphicsView.setScene() will attempt to access the deleted
    QGraphicsScene.

    I think I understand what you mean, since I am new I did not consider this. Since I used QtDesigner to create the UI looks, now I am trying to refer to those objects in the main.py I shared to give them functionality. Would the scene have as parent the object that is in the .ui created by QtDesigner ("graphicsView" in the python script generated) or rather the QGraphicsView class?

    Parenting the scene to the view might result in the same crash seen with updateGraphics() when the view is destroyed. I haven't looked. Making the scene a child of the dialog it lives in is safe, as long as views in other dialogs don't reference it. Parenting it to the QApplication instance is safer, at the risk of keeping the scene around longer than required.

    I guess python does make it possible to assign the scene to a new python attribute of QGraphicsView. That sounds like the road to confusion.


  • Lifetime Qt Champion

    @Cocor said in Problem with second window and actions/signals:

    but when validate takes place it crashes

    Then please use debugger to see where exactly it crashes and why



  • @Cocor said in Problem with second window and actions/signals:

    Hi!
    I am a newbie with python/Qt tools and widgets, and I am having problems implementing a GUI with several windows.
    My intention at the moment is that there is a button ("actionModify_room") in the Main Window the displays a second window for setting room dimensions and sensor locations. I am able to open the second window and display some graphics (a circle) on it. However, I am trying to validate the text input of a QLineEdit (several eventually) that is supposed to later place an object in the graphics, but when validate takes place it crashes. Also, I added a button for refreshing the graphics after all inputs are set (call updateGrahics), but in this case it even crashes before loading the second window .

    This is the code:

    
    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QWidget
    from PyQt5.QtGui import QDoubleValidator, QValidator
    

    This import block and the subsequent references seem to be a little confused. QMainWindow and QtWidgets.QMainWindow, for example.

            
    class CustomDialog(QWidget, Ui_Dialog): 
        def __init__(self):        
            super().__init__()
            QtWidgets.QMainWindow.__init__(self)    # to check why this is necessary
    

    Calling the QMainWindow constructor from a class that isn't derived from QMainWindow seems like a questionable choice.

    def validate(self):
        validationRule = QDoubleValidator(0,999,2) 
        validationRule.validate(self.text(), 10) 
        if validationRule.validate(self.text(), 10)[0] == QValidator.Acceptable:
    

    This looks like a copy and paste or formatting error. Otherwise, why validate the same input twice, and then treat the enum return value from the second invocation as an array of enums?

    def updateGraphics(self):                  
        scene = QtWidgets.QGraphicsScene()
        self.graphicsView.setScene(scene)
        pen = QtGui.QPen(QtCore.Qt.darkBlue)
        Brush = QtGui.QBrush(QtCore.Qt.darkBlue)
        Brush.setStyle(QtCore.Qt.SolidPattern)
                 
        scene.addEllipse(10,10,10,10,pen,Brush)
    

    scene doesn't have a parent, and isn't an attribute of the CustomDialog instance. My expectation is that it will be destroyed sometime after updateGraphics() returns, and a future call to QGraphicsView.setScene() will attempt to access the deleted QGraphicsScene.



  • @jsulm Thanks a lot. I was actually mainly using prints to follow the execution and found the problematic calls or modules. I will try to use more pdb.set_trace() and its commands in the future. However, sometimes the application crashes without any feedback (I imagine that I am not putting the trace in the right place), so any feedback is welcome. Hopefully, I get soon some more knowledge into this and I can debug on my own, and even in the future answer some questions as well.



  • @jeremy_k said in Problem with second window and actions/signals:

    @Cocor said in Problem with second window and actions/signals:

    Hi!
    I am a newbie with python/Qt tools and widgets, and I am having problems implementing a GUI with several windows.
    My intention at the moment is that there is a button ("actionModify_room") in the Main Window the displays a second window for setting room dimensions and sensor locations. I am able to open the second window and display some graphics (a circle) on it. However, I am trying to validate the text input of a QLineEdit (several eventually) that is supposed to later place an object in the graphics, but when validate takes place it crashes. Also, I added a button for refreshing the graphics after all inputs are set (call updateGrahics), but in this case it even crashes before loading the second window .

    This is the code:

    
    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QWidget
    from PyQt5.QtGui import QDoubleValidator, QValidator
    

    This import block and the subsequent references seem to be a little confused. QMainWindow and QtWidgets.QMainWindow, for example.

    Regarding your feedback, I imagine that you were referring to the line of QtWidgets.QMainWindow.init(self). In previous debugging sessions somehow the Ui_Dialog was not getting displayed properly, until I used that line. I guess there was something else incorrect, because I just saw it is not necessary anymore to display Ui_Dialog properly. Feel free to correct me I am doing something not pythonic or wrong (I am trying to learn here).

            
    class CustomDialog(QWidget, Ui_Dialog): 
        def __init__(self):        
            super().__init__()
            QtWidgets.QMainWindow.__init__(self)    # to check why this is necessary
    

    Calling the QMainWindow constructor from a class that isn't derived from QMainWindow seems like a questionable choice.
    As I mentioned before, I noticed that it was a weird line (and marked it with a comment). I deleted it.

    def validate(self):
        validationRule = QDoubleValidator(0,999,2) 
        validationRule.validate(self.text(), 10) 
        if validationRule.validate(self.text(), 10)[0] == QValidator.Acceptable:
    

    This looks like a copy and paste or formatting error. Otherwise, why validate the same input twice, and then treat the enum return value from the second invocation as an array of enums?

    You are on the right lead on this. I am following some youtube tutorials, and concretely in this case one about QDoublevalidator. The first invocation was my mistake (I was using a print), and for the second invocation I was using the array following the example. I though the invocation was returning an array as the video mentioned.
    Also I noticed (through pdb.set_trace) that I was referring to self ("def validate(self)") inside the CustomDialog class, therefore it did not have any self.text attribute. I fixed this creating a new class for QLineEdit:

    class Text(QLineEdit):      
        def validating(self):
            pdb.set_trace()
            validationRule = QDoubleValidator(0,999,2) 
            
            if validationRule.validate(self.text(), 10)[0] == QValidator.Acceptable:
                self.setFocus()
            else:
                self.setText('')
    

    However when this call is performed (fixed line to "self.lineEdit_1.editingFinished.connect(Text.validating) "), the app crashes without any feedback. I feel like I am doing wrong use of parents here (an probably along the whole code). Please feel free to comment,

    def updateGraphics(self):                  
        scene = QtWidgets.QGraphicsScene()
        self.graphicsView.setScene(scene)
        pen = QtGui.QPen(QtCore.Qt.darkBlue)
        Brush = QtGui.QBrush(QtCore.Qt.darkBlue)
        Brush.setStyle(QtCore.Qt.SolidPattern)
                 
        scene.addEllipse(10,10,10,10,pen,Brush)
    

    scene doesn't have a parent, and isn't an attribute of the CustomDialog instance. My expectation is that it will be destroyed sometime after updateGraphics() returns, and a future call to QGraphicsView.setScene() will attempt to access the deleted
    QGraphicsScene.

    I think I understand what you mean, since I am new I did not consider this. Since I used QtDesigner to create the UI looks, now I am trying to refer to those objects in the main.py I shared to give them functionality. Would the scene have as parent the object that is in the .ui created by QtDesigner ("graphicsView" in the python script generated) or rather the QGraphicsView class?
    In any case after making some changes in the code, app is not crashing anymore, although it would be good to follow good practices, or avoid that the problems appear later on.

    Thanks a lot for taking the time to read into the code. It is very helpful.
    Also, excuse my ignorance. This is the first time I am getting into pyqt, and I am trying to learn as much as possible.



  • @Cocor said in Problem with second window and actions/signals:

    Regarding your feedback, I imagine that you were referring to the line of QtWidgets.QMainWindow.init(self). In previous debugging sessions somehow the Ui_Dialog was not getting displayed properly, until I used that line. I guess there was something else incorrect, because I just saw it is not necessary anymore to display Ui_Dialog properly. Feel free to correct me I am doing something not pythonic or wrong (I am trying to learn here).

    Calling an unrelated __init__ looks wrong. I can't comment on the pythonic part. Removing the line was the right move. If using python's classes is unfamiliar territory, https://docs.python.org/3/tutorial/index.html has a decent looking classes section.

    Speaking of Ui_Dialog and Ui_MainWindow, what are these? If it's not code coming from a standard package, either include the source or figure out how to phrase the question without it. There's a significant chance that such code is related to the problem, but we can't tell without seeing it.

        def validate(self):
            validationRule = QDoubleValidator(0,999,2) 
            validationRule.validate(self.text(), 10) 
            if validationRule.validate(self.text(), 10)[0] == QValidator.Acceptable:
    

    This looks like a copy and paste or formatting error. Otherwise, why validate the same input twice, and then treat the enum return value from the second invocation as an array of enums?

    You are on the right lead on this. I am following some youtube tutorials, and concretely in this case one about QDoublevalidator. The first invocation was my mistake (I was using a print), and for the second invocation I was using the array following the example. I though the invocation was returning an array as the video mentioned.

    PyQt's QValidator.validate() return type doesn't match the C++ QValidator::validate()
    !?!
    That surprised me. PyQt returns a tuple that also includes potentially modified input parameters, which the C++ api handles through non-const reference parameters. There is of course at least one stack overflow thread.

    Also I noticed (through pdb.set_trace) that I was referring to self ("def validate(self)") inside the CustomDialog class, therefore it did not have any self.text attribute. I fixed this creating a new class for QLineEdit:

    class Text(QLineEdit):      
        def validating(self):
            pdb.set_trace()
            validationRule = QDoubleValidator(0,999,2) 
            
            if validationRule.validate(self.text(), 10)[0] == QValidator.Acceptable:
                self.setFocus()
            else:
                self.setText('')
    

    However when this call is performed (fixed line to "self.lineEdit_1.editingFinished.connect(Text.validating) "), the app crashes without any feedback. I feel like I am doing wrong use of parents here (an probably along the whole code). Please feel free to comment,

    Text.validating is an unbound reference to an instance method (I think that's the terminology). Calling it doesn't work because there is no 'self'.
    The usage should look more like:

    instance = Text()
    ...
    self.lineEdit_1.editingFinished.connect(instance.Validating)
    

    The class discussion in the python tutorial should cover the how and why.

    def updateGraphics(self):                  
        scene = QtWidgets.QGraphicsScene()
        self.graphicsView.setScene(scene)
        pen = QtGui.QPen(QtCore.Qt.darkBlue)
        Brush = QtGui.QBrush(QtCore.Qt.darkBlue)
        Brush.setStyle(QtCore.Qt.SolidPattern)
                 
        scene.addEllipse(10,10,10,10,pen,Brush)
    

    scene doesn't have a parent, and isn't an attribute of the CustomDialog instance. My expectation is that it will be destroyed sometime after updateGraphics() returns, and a future call to QGraphicsView.setScene() will attempt to access the deleted
    QGraphicsScene.

    I think I understand what you mean, since I am new I did not consider this. Since I used QtDesigner to create the UI looks, now I am trying to refer to those objects in the main.py I shared to give them functionality. Would the scene have as parent the object that is in the .ui created by QtDesigner ("graphicsView" in the python script generated) or rather the QGraphicsView class?

    Parenting the scene to the view might result in the same crash seen with updateGraphics() when the view is destroyed. I haven't looked. Making the scene a child of the dialog it lives in is safe, as long as views in other dialogs don't reference it. Parenting it to the QApplication instance is safer, at the risk of keeping the scene around longer than required.

    I guess python does make it possible to assign the scene to a new python attribute of QGraphicsView. That sounds like the road to confusion.



  • @jeremy_k

    Ok! Thanks a lot for the suggestions. I read the class section that you proposed into depth (before I just had a brief look. I understand the content (or I think so) and now it´s a matter of applying it.

    In regards to Text.validating, I got the point, although I am now just using "self.lineEdit_1.setValidator" which makes the trick for my application (no ranges but doubles allowed).

    Regarding Ui_Dialog and Ui_MainWindow they were basically python scripts generated (-x .ui -o .py) out of ui files from QtDesigner. That is why I assumed everything was correct there, and that it might not be necessary to add extra information about them. Next time however I will put all code.

    I followed your suggestion of parenting the scene to the QApplication (although parenting scene to QGraphicsView worked as well), and it works well also, so I will keep it like that (to avoid confusion as you mentioned).

    So, since it is working as it should I will close this thread. thanks a lot for your dedication and your time @jeremy_k!


Log in to reply