Search code examples
pythonpyqtqapplication

PyQt4:different views in centralWidget. Best practice SW-Architecture


In my program I want to define different views to show my data. In my first tries all views were defined in on class (QtGui.QWidget). However since the code for the views started to become longer and I also want to add interactive features, I want to separate the different views to individual classes.

Below you find my first try. Here I was just trying use setCentralWidget to switch between the two views. I have to generate new instances of the views before switching to each view. Otherwise I would generate a runtime error. It seems like every View instance is destroyed as soon as it is not in centralview anymore.

Do I understand this right? Is the proposed code structure ok, or are there problems to expect if I structure my code like this? Any other proposals / best practises for this kind of problem?

import sys
from PyQt4 import QtGui

class View1Widget(QtGui.QWidget):
    pass
    # Describse View 1 of Data

class View2Widget(QtGui.QWidget):
    pass
    # Describse View 2 of Data

class ApplicationWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.main_widget = QtGui.QWidget(self)

        # Generate Instances of both views  
        self.View1 = View1Widget(  self.main_widget)
        self.View2 = View2Widget(  self.main_widget)

        # Init Central View
        self.setCentralWidget(self.View1)

        # Focus
        self.main_widget.setFocus()

        self.views_menu = QtGui.QMenu('&Views', self)  
        self.views_menu.addAction('View 1', self.showView1)
        self.views_menu.addAction('View 2', self.showView2)
        self.menuBar().addMenu(self.views_menu)

    def showView1(self):
        "Switches Central Widget to View1"
        self.View1 = View1Widget(  self.main_widget) # Without this line, I would generate a runtime error
        self.setCentralWidget(self.View1)    

    def showView2(self):
        "Switches Central Widget to View2"
        self.View2 = View2Widget(  self.main_widget) # Without this line, I would generate a runtime error
        self.setCentralWidget(self.View2)




if __name__ == '__main__':
    qApp = QtGui.QApplication(sys.argv)
    aw = ApplicationWindow()
    aw.show()
    sys.exit(qApp.exec_())

Without the marked line I would get the following runtime error after clicking to View 2 and than back to View 1

Exception "unhandled RuntimeError"
wrapped C/C++ object of type View1Widget has been deleted

Solution

  • Use a stack for the central widget:

    import sys
    from PyQt4 import QtGui
    
    class ApplicationWindow(QtGui.QMainWindow):
        def __init__(self):
            QtGui.QMainWindow.__init__(self)
            self.stack = QtGui.QStackedWidget(self)
            self.View1 = QtGui.QLabel('View 1', self.stack)
            self.View2 = QtGui.QLabel('View 2', self.stack)
            self.stack.addWidget(self.View1)
            self.stack.addWidget(self.View2)
            self.setCentralWidget(self.stack)
            menu = self.menuBar().addMenu('&Views')
            menu.addAction('View 1', lambda: self.showView(0))
            menu.addAction('View 2', lambda: self.showView(1))
    
        def showView(self, index):
            self.stack.setCurrentIndex(index)
    
    if __name__ == '__main__':
    
        qApp = QtGui.QApplication(sys.argv)
        aw = ApplicationWindow()
        aw.show()
        sys.exit(qApp.exec_())