3

I have a main Window derived form QMainWindow that may show different Widgets, depending on the task at hand.

I created a simplified example below:

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys


class MainWindow(QMainWindow):

    def __init__(self, parent=None):
        '''
        Constructor
        '''
        QMainWindow.__init__(self, parent)
        self.central_widget = QStackedWidget()
        self.setCentralWidget(self.central_widget)
        self.start_screen = Start(self)
        self.second_screen = Second(self)
        self.central_widget.addWidget(self.start_screen)
        self.central_widget.addWidget(self.second_screen)
        self.central_widget.setCurrentWidget(self.start_screen)



class Start(QWidget):

    def __init__(self, parent=None):
        super(Start, self).__init__(parent)
        layout = QHBoxLayout()
        button = QPushButton(text=QString('Push me!'))
        layout.addWidget(button)
        self.setLayout(layout)
        self.connect(button, SIGNAL("clicked()"), self.change_widget)

    def change_widget(self):
        self.parent().setCurrentWidget(
            self.parent().parent().second_screen)


class Second(QWidget):

    def __init__(self, parent=None):
        super(Second, self).__init__(parent)
        layout = QHBoxLayout()
        button = QPushButton(text=QString('Back to Start!'))
        layout.addWidget(button)
        self.setLayout(layout)
        self.connect(button, SIGNAL("clicked()"), self.change_widget)

    def change_widget(self):
        self.parent().setCurrentWidget(
            self.parent().parent().start_screen)

app = QApplication(sys.argv)
myWindow = MainWindow(None)
myWindow.show()
app.exec_()

This toggles between two different central widgets, nevertheless I found the handling with parent() functions tedious, and I'd like to know if there is a better method to organise the widgets.

The setCurrentWidget method will probably be always accessible with one parent() statement, but if for any reasons the hierarchy-depth changes, is there a better method to access the QMainWindow than parent().parent()? Or would I just re-create the Widget every time a button is clicked (I assume, if this would be a widget with data-Input possibilities, these data would be lost, which is annoying).

I appreciate any suggestions for improvement.

MichaelA
  • 1,866
  • 2
  • 23
  • 38

1 Answers1

4

You should be using Signals. This allows any parent hierarchy and the child widgets don't have to have any knowledge of how they're being used or what order they're being displayed in. The main widget handles it all.

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys


class MainWindow(QMainWindow):

    def __init__(self, parent=None):
        '''
        Constructor
        '''
        QMainWindow.__init__(self, parent)
        self.central_widget = QStackedWidget()
        self.setCentralWidget(self.central_widget)
        self.start_screen = Start(self)
        self.second_screen = Second(self)
        self.central_widget.addWidget(self.start_screen)
        self.central_widget.addWidget(self.second_screen)
        self.central_widget.setCurrentWidget(self.start_screen)

        self.start_screen.clicked.connect(lambda: self.central_widget.setCurrentWidget(self.second_screen))
        self.second_screen.clicked.connect(lambda: self.central_widget.setCurrentWidget(self.start_screen))



class Start(QWidget):

    clicked = pyqtSignal()

    def __init__(self, parent=None):
        super(Start, self).__init__(parent)
        layout = QHBoxLayout()
        button = QPushButton(text=QString('Push me!'))
        layout.addWidget(button)
        self.setLayout(layout)
        button.clicked.connect(self.clicked.emit)


class Second(QWidget):

    clicked = pyqtSignal()

    def __init__(self, parent=None):
        super(Second, self).__init__(parent)
        layout = QHBoxLayout()
        button = QPushButton(text=QString('Back to Start!'))
        layout.addWidget(button)
        self.setLayout(layout)
        button.clicked.connect(self.clicked.emit)


app = QApplication(sys.argv)
myWindow = MainWindow(None)
myWindow.show()
app.exec_()
Brendan Abel
  • 35,343
  • 14
  • 88
  • 118
  • Thanks, this helped a lot, as it not only solved my problem but also improved my understanding of basic PyQt Methods. As a follow up question: omitting the `clicked = pyqtSignal()` and `button.clicked.connect(self.clicked.emit)` and instead using `self.second_screen.button.clicked.connect(lambda: self.central_widget.setCurrentWidget(self.start_screen))` in MainWindow achieves the same. Are there any drawbacks in this way I did not realise? Edit: `button` must be changed to `self.button` too. – MichaelA Feb 25 '16 at 13:54
  • 1
    It's kind of the same as your original problem but in reverse. Doing it that way requires the parent to know implementation details and the internal structure of the child widget. Later on, if you want to change the structure of the child widget (add/remove buttons, change widget names, etc) you won't be able to because the parent is expecting the internal structure to stay the same. It's much better to define *interfaces* (in this case, using Signals) that are separate from the internal structure of your widget to communicate with other widgets. – Brendan Abel Feb 25 '16 at 17:15