Search code examples
pythonuser-interfacepyqtpyqt6

How do I add/remove a layout when triggering action in PyQt6? How do I change the order of a layout?


I'm in the process of making a calculator for a project, and I wanted it so that if I press an action in the menu bar it turns into a scientific calculator, and if I uncheck it, it goes back to a basic calculator.

So far I was able to successfully add the buttons for the scientific calculator, but I want it to go to the left of the horizontal layout (so the basic buttons are on the right, and the advance buttons are on the left) but I have no idea how to do that. I also don't know yet how to revert it back to a basic calculator once it becomes a scientific calculator. Also for some reason the actions of the buttons of the scientific calculator don't seem to be working properly (if I type a button, it should add a text to the QLineEdit). I'm quite at a loss as I don't know where to go from here.

Below I've created a simple example of what I did with my calculator.

import sys, os
from PyQt6.QtCore import *
from PyQt6.QtWidgets import *
from PyQt6.QtGui import *

class MainWindow(QMainWindow):

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

        self.allbuttons = {}
        #self.setFixedSize(200, 200)
        self.setWindowTitle('Try')

        self.verlayout = QVBoxLayout()
        self.horlayout = QHBoxLayout()

        self.qline()
        self.wid1()


        self.verlayout.addLayout(self.horlayout)

        self.centralwidget = QWidget()
        self.setCentralWidget(self.centralwidget)
        self.centralwidget.setLayout(self.verlayout)

    def qline(self):

        self.line = QLineEdit()
        self.line.setFixedHeight(35)
        self.verlayout.addWidget(self.line)

    def wid1(self):

        buttons = QGridLayout()
        buttondict = {
        'A': (0, 0),
        'B': (0, 1),
        'C': (1, 0),
        'D': (1, 1)
        }

        for btn, pos in buttondict.items():

            self.allbuttons[btn] = QPushButton(btn)

            self.allbuttons[btn].setFixedSize(20, 20)
            buttons.addWidget(self.allbuttons[btn], pos[0], pos[1])

        self.horlayout.addLayout(buttons)

    def wid2(self):

        buttons = QGridLayout()
        buttondict = {
        'E': (0, 0),
        'F': (0, 1),
        'G': (1, 0),
        'H': (1, 1)
        }

        for btn, pos in buttondict.items():

            self.allbuttons[btn] = QPushButton(btn)

            self.allbuttons[btn].setFixedSize(20, 20)
            buttons.addWidget(self.allbuttons[btn], pos[0], pos[1])

        self.horlayout.addLayout(buttons)

class Menu:

    def __init__(self, MainWindow):

        super().__init__()

        self.view = MainWindow

        self.menuBar = QMenuBar()
        self.menuBar.setGeometry(QRect(0, 0, 277, 22))
        self.view.setMenuBar(self.menuBar)
        self.open = QMenu(self.menuBar)
        self.open.setTitle('Open')
        self.menuBar.addAction(self.open.menuAction())
        self.this = QAction(self.menuBar)
        self.this.setText('This')
        self.this.setCheckable(True)
        self.open.addAction(self.this)
        
        self.this.triggered.connect(self.show_new_window)

    def show_new_window(self, checked):
        
        if checked:

            self.view.wid2()
            #self.view.resize(300, 200)

        else:

            pass

app = QApplication(sys.argv)
w = MainWindow()
m = Menu(w)
w.show()
app.exec()

Solution

  • Instead of wanting to remove and add layouts you should use a QStackedWidget:

    import sys
    from functools import cached_property
    
    from PyQt6.QtCore import QRect, Qt
    from PyQt6.QtGui import QAction
    from PyQt6.QtWidgets import (
        QApplication,
        QGridLayout,
        QLineEdit,
        QMainWindow,
        QMenu,
        QMenuBar,
        QPushButton,
        QStackedWidget,
        QVBoxLayout,
        QWidget,
    )
    
    
    class MainWindow(QMainWindow):
        def __init__(self):
            super().__init__()
            self.setWindowTitle("Try")
    
            central_widget = QWidget()
            self.setCentralWidget(central_widget)
    
            lay = QVBoxLayout(central_widget)
            lay.addWidget(self.lineedit)
    
            lay.addWidget(self.stacked_widget, alignment=Qt.AlignmentFlag.AlignCenter)
    
            maps = [
                {"A": (0, 0), "B": (0, 1), "C": (1, 0), "D": (1, 1)},  # first page
                {"E": (0, 0), "F": (0, 1), "G": (1, 0), "H": (1, 1)},  # second page
            ]
    
            for m in maps:
                page = self.create_page(m)
                self.stacked_widget.addWidget(page)
    
        @cached_property
        def stacked_widget(self):
            return QStackedWidget()
    
        @cached_property
        def lineedit(self):
            le = QLineEdit()
            le.setFixedHeight(35)
            return le
    
        def create_page(self, map_letters):
            page = QWidget()
            grid_layout = QGridLayout(page)
            for name, pos in map_letters.items():
                button = QPushButton(name)
                button.setFixedSize(20, 20)
                grid_layout.addWidget(button, *pos)
            return page
    
    
    class Menu:
        def __init__(self, MainWindow):
            super().__init__()
            self.view = MainWindow
            self.menuBar = QMenuBar()
            self.menuBar.setGeometry(QRect(0, 0, 277, 22))
            self.view.setMenuBar(self.menuBar)
            self.open = QMenu(self.menuBar)
            self.open.setTitle("Open")
            self.menuBar.addAction(self.open.menuAction())
            self.this = QAction(self.menuBar)
            self.this.setText("This")
            self.this.setCheckable(True)
            self.open.addAction(self.this)
    
            self.this.triggered.connect(self.show_new_window)
    
        def show_new_window(self, checked):
            self.view.stacked_widget.setCurrentIndex(1 if checked else 0)
    
    
    def main():
        app = QApplication(sys.argv)
        w = MainWindow()
        m = Menu(w)
        w.show()
        app.exec()
    
    
    if __name__ == "__main__":
        main()