pythonuser-interfacepyqtpyqt5mdi

How to open a new MDI sub-window in PyQt5?


What I want to do is to open a new Countrypage sub-window by clicking on the "New" button which is in Countrypage itself.

For example, if I click the "New" button in a CountryPage window (window title: "Country page"), one more new Countrypage window will be opened in the MDI area (window title: "Country Page 1"). Now if we click the "New" button in "Country Page 1", one more new window will open in the MDI area (window title: "Country page 2") and so on - and I want to close the windows one by one by pressing the corresponding "Close" button in Countrypage. New window are opened only by pressing a "New" button.

And if we close the last opened window by pressing the "Close" button, the text item in the "Country" text-box will be automatically updated in the previous window's "Country" text-box and so on.

Main Script :

import sys,os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

from sample_countrypage import Countrypage

class MainPage(QMainWindow):
    count = 0

    def __init__(self):
        super().__init__()

        self.mdi = QMdiArea()
        self.mdi.setFixedSize(1000,400)
        self.mdi.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.mdi.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)

        self.setWindowTitle(" Sample Programme")
        self.setGeometry(100,100,1600,600)
        self.Ui()
        self.show()

    def Ui(self):
        self.btn1=QPushButton("Country")
        self.btn1.setFixedSize(100, 30)
        self.btn1.clicked.connect(self.countrypage)

        self.left_layout = QVBoxLayout()
        self.right_layout = QHBoxLayout()
        self.main_layout = QHBoxLayout()

        self.left_layout.setContentsMargins(3,5,5,3)
        self.left_layout.addWidget(self.btn1)
        self.left_layout.addStretch()
        self.right_layout.addWidget(self.mdi)

        self.main_layout.setSpacing(5)
        self.main_layout.setContentsMargins(0,0,0,0)
        self.main_layout.addLayout(self.left_layout)
        self.main_layout.addLayout(self.right_layout)
        self.main_layout.addStretch()

        widget = QWidget()
        widget.setLayout(self.main_layout)
        self.setCentralWidget(widget)

        self.subwindow1 = QMdiSubWindow()
        self.subwindow1.setObjectName("SubWindow_1")
        # self.subwindow1.setWindowFlag(Qt.FramelessWindowHint)


    print(Countrypage.btn2click)

    def countrypage(self):
        self.countrywindow = Countrypage()
        self.subwindow1.setWidget(self.countrywindow)
        self.subwindow1.setWindowTitle("Create Country")
        self.subwindow1.setFixedWidth(300)
        self.mdi.addSubWindow(self.subwindow1)
        self.subwindow1.show()
        self.mdi.cascadeSubWindows()
        self.countrywindow.closeRequsted.connect(self.subwindow1close)

    def subwindow1close(self):
        print("close activated from mdi programme")

        self.subwindow1.close()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainwindow = MainPage()
    app.setStyle("Windows")
    mainwindow.show()
    sys.exit(app.exec_())

Countrypage.py

import sys,os
from PyQt5.QtWidgets import QWidget,QApplication,QPushButton,QLineEdit,QFormLayout,QVBoxLayout,QHBoxLayout
from PyQt5.QtCore import pyqtSignal

class Countrypage(QWidget):
    closeRequsted = pyqtSignal()
    def __init__(self):
        super().__init__()

        self.btn1 = QPushButton("close")
        self.btn2 = QPushButton("New")
        self.btn1.clicked.connect(self.result)
        self.btn2.clicked.connect(self.btn2click)

        self.tb_country = QLineEdit()
        self.tb_continent =QLineEdit()

        self.form_layout = QFormLayout()
        self.form_layout.addRow("Country",self.tb_country)
        self.form_layout.addRow("continent",self.tb_continent)
        self.form_layout.addRow("",self.btn2)
        self.form_layout.addRow("",self.btn1)
        self.setLayout(self.form_layout)

    def result(self):
        self.closeRequsted.emit()

    def btn2click(self):
        btn2text = (self.btn2.text())
        print(btn2text)

if __name__=="__main__":
    app = QApplication(sys.argv)
    countrywin = Countrypage()
    countrywin.show()
    sys.exit(app.exec_())

Solution

  • The adding and closing of sub-windows is best handled by the main-window. The CountryPage class doesn't need to know anything about the sub-windows. The new/close buttons can be directly connected to methods of the main-window. This makes it easier to manage the sub-windows via the functions of the mdi-area.

    Below is a re-write of your example which should do what you asked for:

    Main Script:

    import sys, os
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *   
    
    class MainPage(QMainWindow):
        def __init__(self):
            super().__init__()
            self.mdi = QMdiArea()
            self.mdi.setFixedSize(1000, 400)
            self.mdi.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
            self.mdi.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
            self.setWindowTitle("Sample Programme")
            self.setGeometry(100, 100, 1600, 600)
            self.Ui()
    
        def Ui(self):
            self.btn1 = QPushButton("Country")
            self.btn1.setFixedSize(100, 30)
            self.btn1.clicked.connect(self.countrypage)
            self.left_layout = QVBoxLayout()
            self.right_layout = QHBoxLayout()
            self.main_layout = QHBoxLayout()
            self.left_layout.setContentsMargins(3, 5, 5, 3)
            self.left_layout.addWidget(self.btn1)
            self.left_layout.addStretch()
            self.right_layout.addWidget(self.mdi)
            self.main_layout.setSpacing(5)
            self.main_layout.setContentsMargins(0, 0, 0, 0)
            self.main_layout.addLayout(self.left_layout)
            self.main_layout.addLayout(self.right_layout)
            self.main_layout.addStretch()
            widget = QWidget()
            widget.setLayout(self.main_layout)
            self.setCentralWidget(widget)
    
        def countrypage(self):
            page = Countrypage()
            subwindow = self.mdi.addSubWindow(page)
            subwindow.setWindowTitle("Create Country")
            subwindow.setFixedWidth(300)
            page.btn_close.clicked.connect(self.subwindowclose)
            page.btn_new.clicked.connect(self.countrypage)
            subwindow.show()
            self.mdi.cascadeSubWindows()
    
        def subwindowclose(self):
            print("close activated from mdi programme")
            current = self.mdi.activeSubWindow()
            if current is not None:
                self.mdi.activatePreviousSubWindow()
                previous = self.mdi.activeSubWindow()
                if previous is not None:
                    previous.widget().update_fields(current.widget())
                current.close()
    
    if __name__ == "__main__":
    
        app = QApplication(sys.argv)
        mainwindow = MainPage()
        app.setStyle("Windows")
        mainwindow.show()
        sys.exit(app.exec_())
    

    Countrypage.py:

    import sys,os
    from PyQt5.QtWidgets import QWidget,QApplication,QPushButton,QLineEdit,QFormLayout,QVBoxLayout,QHBoxLayout
    from PyQt5.QtCore import pyqtSignal
    
    class Countrypage(QWidget):
        def __init__(self):
            super().__init__()
            self.btn_close = QPushButton("Close")
            self.btn_new = QPushButton("New")
            self.tb_country = QLineEdit()
            self.tb_continent = QLineEdit()
            self.form_layout = QFormLayout()
            self.form_layout.addRow("Country", self.tb_country)
            self.form_layout.addRow("Continent", self.tb_continent)
            self.form_layout.addRow("", self.btn_close)
            self.form_layout.addRow("", self.btn_new)
            self.setLayout(self.form_layout)
    
        def update_fields(self, other):
            if isinstance(other, Countrypage):
                self.tb_country.setText(other.tb_country.text())
                self.tb_continent.setText(other.tb_continent.text())
            else:
                raise TypeError('invalid page type')