Search code examples
python-3.xpyqt6

How to open one or more new instance(s) of the QMainWindow App in Python


I have my QMainWindow set up as the code below shows. I have my menu include "New" and "Quit" menu items (see the image). I successfully coded the Quit one, which was too easy for me. All I want is to open a new instance of my main application when I click the "New" menu item (shown in the image).

enter image description here

class MainApp(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.show()

        self.action_Quit.triggered.connect(self.exit_app)

    def exit_app(self):
        self.close()


app = QApplication(sys.argv)
MainWindow = MainApp()
sys.exit(app.exec())

Solution

  • Creating a new window is just a matter of creating a new instance of it (which is the class of self if you want to create a new instance of the same type).

    The actual issue is keeping references of those windows. If you create a new window without keeping a persistent reference to it, it will be probably destroyed due to the garbage collection of Python (or because no C++ reference is kept, which usually means using a parent QObject).

    If you plan to have multiple windows of the same type, you need to use a common container. To achieve this, there are multiple solutions, but the concept remains: you need a "controller" that can always have pointers to those windows.

    For complex applications, this usually means that the "controller" is a main object that normally exists within the program itself. A common way to do so is by subclassing QApplication and create a data container (even a basic python list) that keeps track of the windows.

    A simpler solution could use a list of those window as a class attribute of the window class itself.

    In the following example, the CommonWindow class has a list set as class attribute that keeps track of the existing windows and eventually adds/removes them when they're created or closed. For explanation reasons, I implemented a basic QListWidget that will be constantly updated with the current list of existing windows, adding them when new windows are created, and removing them when they're closed.

    from PyQt5.QtWidgets import *
    
    class CommonWindow(QMainWindow):
        winIndex = 0
        siblingWindows = []
        def __init__(self):
            super().__init__()
    
            # note: this sets a *class* attribute that always increases the count
            CommonWindow.winIndex += 1
    
            self.setWindowTitle('Window {}'.format(self.winIndex))
            self.siblingWindows.append(self)
    
            menu = self.menuBar().addMenu('File')
            newAction = menu.addAction('New window')
            quitAction = menu.addAction('Quit')
    
            self.windowList = QListWidget()
            self.setCentralWidget(self.windowList)
    
            newAction.triggered.connect(self.newWindow)
            quitAction.triggered.connect(QApplication.quit)
    
            if len(self.siblingWindows) == 1:
                # avoid unnecessary calls
                self.updateWindowList()
    
        def newWindow(self):
            new = CommonWindow()
            new.show()
            for win in self.siblingWindows:
                win.updateWindowList()
    
        def updateWindowList(self):
            self.windowList.clear()
            for win in self.siblingWindows:
                self.windowList.addItem(win.windowTitle())
    
        def closeEvent(self, event):
            self.siblingWindows.remove(self)
            for win in self.siblingWindows:
                win.updateWindowList()
    
    
    app = QApplication([])
    test = CommonWindow()
    test.show()
    app.exec()