Single QPushButton Object on multiple frames

24 Views Asked by At

I have an application that I'm writing that will have different layers for different purposes. Some layers will have functionality that differs from other layers, but some functionality will remain the same for all layers. As a user switches between the layers, I want to avoid having to have the user switch back to a previous layer to do certain operations. As an example, I need an enable button that should be present for all layers. The enable functionality does the same thing for all layers, with zero difference. So rather than creating five buttons, one for each layer, I was thinking one button, that is passed to each of the different layers would be the way to go.

my first attempt at this has failed. I get a RuntimeError

RuntimeError: wrapped C/C++ object of type QDoubleSpinBox has been deleted

This isn't going to work as I expected, which I'm guessing has to do with some form of ownership over the objects. So I created a simpler application, that has a single button, with two different panels.

import sys

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (
    QApplication,
    QFrame,
    QMainWindow,
    QPushButton,
    QSplitter,
    QStackedWidget,
    QVBoxLayout,
    QWidget,
)


class IO:
    def __init__(self) -> None:
        self.button = QPushButton("Panel A")

    def get_io(self):
        return self.button


class FrameA:
    def get_frame(self, btn):
        frame = QFrame()
        frame.setFrameShape(QFrame.Shape.NoFrame)
        frame.setLineWidth(0)
        frame.setMidLineWidth(0)

        layout = QVBoxLayout(frame)

        layout.addWidget(btn)

        return frame


class PanelA(QWidget):
    def __init__(self, io, parent=None):
        QWidget.__init__(self)
        self.parent = parent

        self.build_panel(io)

    def build_panel(self, btn):

        layout = QVBoxLayout()

        vertical_splitter = QSplitter(Qt.Orientation.Vertical)
        vertical_splitter.setStyleSheet("QSplitter::handle {image: none;}")

        vertical_splitter.addWidget(FrameA().get_frame(btn))

        layout.addWidget(vertical_splitter)
        self.setLayout(layout)


class PanelB(QWidget):
    def __init__(self, io, parent=None):
        QWidget.__init__(self)
        self.parent = parent

        self.build_panel(io)

    def build_panel(self, btn):

        layout = QVBoxLayout()

        vertical_splitter = QSplitter(Qt.Orientation.Vertical)
        vertical_splitter.setStyleSheet("QSplitter::handle {image: none;}")

        vertical_splitter.addWidget(FrameA().get_frame(btn))

        layout.addWidget(vertical_splitter)
        self.setLayout(layout)


class UI(QWidget, IO):
    def __init__(self, parent=None):
        QWidget.__init__(self)
        IO.__init__(self)

        layout = QVBoxLayout()
        self.stack = QStackedWidget()

        self.stack.addWidget(PanelA(self.get_io()))
        self.stack.addWidget(PanelB(self.get_io()))
        self.stack.setCurrentIndex(1)

        layout.addWidget(self.stack)

        self.setLayout(layout)

        self.init_btn_connections()

    def init_btn_connections(self):
        self.button.clicked.connect(lambda: self.btnstate(self.button))

    def btnstate(self, btn):
        print(f"Button: {btn}")

        if btn.text() == "Panel A":
            print(
                f"----> Button, Current Stack Idx: {self.stack.currentIndex()}"
            )
            self.button.setText("Panel B")
            self.stack.setCurrentIndex(1)
            print(f"\tSwitched to stack index: {self.stack.currentIndex()}")
        elif btn.text() == "Panel B":
            print(
                f"---> Default, Current Stack Idx: {self.stack.currentIndex()}"
            )
            self.button.setText("Panel A")
            self.stack.setCurrentIndex(0)


class App(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.gui = UI()
        self.setCentralWidget(self.gui)


def main():
    app = QApplication(sys.argv)
    ex = App()
    ex.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()

I've tried to replicate in the simplest way possible what my actual application is doing. Here is the rub of it. I can not replicate the *Object has been deleted error with this simpler app. This seems to tell me that the object that is being deleted is going out of scope and Python is tossing it. Which means I have to chase it down for the object itself.

This simple application runs just fine. However, what I don't have is a button on both panels. When the application launches I see nothing but the application frame. If I set the stack to the 2nd position I see the button.

What appears to be happening in this situation, is that I set the button on the first stack. But when I attempt to build the 2nd panel, it removes it from the 1st stack and places it on the 2nd stack.

I would not be opposed to this if there was a way for me to refresh the panel with the button when I switch between the stacks.

What I was hoping to avoid by using stacks, was the need to build a new panel every time I switch between panels.

How do others handle situations like this?

0

There are 0 best solutions below