0

I have a QMainWindow in file Controller.py which creates a QmenuBar on the centralWidget and multiple files named as win0.py, win1.py, ... with different layouts nested in QstackedLayout. Now I want to enable/disable the buttons in the menubar in the Controller's mainWindow and get some variable / call functions in the centralWidget

This example is based on the following question which don't answer mine:

Using QStackedWidget for multi-windowed PyQt application

How to make nested StackedLayouts in PyQt5?

The closest question is:

Setting up stateChanged signal in QStackedWidget, pyqt

But none of them changes items on the MainWindow (Controller), like that what I try to do. See code for details.

controller.py:

import sys

from PyQt5.QtWidgets import *
from PyQt5 import QtCore

from win0 import W0     # sample window
#from win1 import W1     # further windows
#from win2 import W2

class Controller (QMainWindow):
    def __init__(self, parent=None):
        #super(Controller, self).__init__(parent)
        QMainWindow.__init__(self, parent)
        # used variables
        self.winDict = {}
        self.v1 = " self.V1 in Controller"

        # create stack
        self.stackLayout = QStackedLayout ()

        # put stack on MainWindow
        self.MainWidget =QWidget()
        self.MainWidget.setLayout(self.stackLayout)
        self.setCentralWidget(self.MainWidget)
        self.init_MenuBar ()

        # add further layouts
        self.init_Windows()
        # swith to first layout
        self.activeWin = self.switchWin (0)

    def init_MenuBar(self):

        self.toolbarButton_file = QPushButton (self.tr ("File"))
        self.toolbarButton_0 = QPushButton (self.tr ("Win0"))
        self.toolbarButton_1 = QPushButton (self.tr ("Win1"))
        self.toolbarButton_2 = QPushButton (self.tr ("Win2"))

        toolbar = self.addToolBar ("Window Manager")
        toolbar.addWidget (self.toolbarButton_file)
        toolbar.addWidget (self.toolbarButton_0)
        toolbar.addWidget (self.toolbarButton_1)
        toolbar.addWidget (self.toolbarButton_2)
        toolbar.setMovable (False)

        self.toolbarButton_0.clicked.connect ( (lambda: self.switchWin (0)))
        self.toolbarButton_1.clicked.connect ((lambda: self.switchWin (1)))
        self.toolbarButton_2.clicked.connect ((lambda: self.switchWin (2)))
        self.toolbarButton_0.setCheckable (False)
        self.toolbarButton_1.setCheckable(False)
        self.toolbarButton_2.setCheckable(False)
        self.toolbarButton_0.setEnabled (True)
        self.toolbarButton_1.setEnabled (True)
        self.toolbarButton_2.setEnabled (True)

    def init_Windows(self):
        self.switchWin(0, True)
        self.switchWin(1, True)
        self.switchWin(2, True)


    def switchWin(self, id=0, initonly = False):
        print ("-> switchWin:", id)
        if id not in self.winDict.keys ():
            if id == 0:
                self.winDict[id] = W0 ()
            #elif id == 1:
            #    self.winDict[id] = W1 ()
            #elif id == 2:
            #    self.winDict[id] = W2 ()
            self.stackLayout.addWidget (self.winDict[id])

        if not initonly:
            self.stackLayout.setCurrentIndex (id)
        Window = self.winDict[id]
        return Window

    def list_v1(self):
        print("-> Controller.list_V1")
        print("V1:", self.v1)

def main():
    App = QApplication(sys.argv)
    ProgramWindow = Controller()
    ProgramWindow.show()
    App.exec_()

if __name__ == "__main__":
    main()

win0.py (win1.py ... same fundament)

import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic

class W0(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = self.init_UI()
        self.v1 ="Variable in W0"
        self.ui.pushButton.clicked.connect (self.pbClicked)

    def init_UI(self):
        mainForm = ("win0.ui")
        print ("mainForm:", mainForm)
        Widget = uic.loadUi (mainForm, self)
        return Widget

    def pbClicked(self):
        print ("-> win0.pbClicked")
        self.parent().toolbarButton_0.setEnabled (False)       # There is the question
                                                               # How to access widgets and functions, variables on the
                                                               # centralWidget or MainWindow of Controller.py. I want to en/disable button in the menuBar e.g.

if __name__ == "__main__":
    App = QApplication (sys.argv)
    ProgramWindow = W0()
    ProgramWindow.show ()
    App.exec_ ()

win0.ui (same in win1.ui ...)

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QLabel" name="label">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>0</y>
      <width>43</width>
      <height>13</height>
     </rect>
    </property>
    <property name="text">
     <string>Win 0</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>60</x>
      <y>0</y>
      <width>80</width>
      <height>20</height>
     </rect>
    </property>
    <property name="text">
     <string>PushButton</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton_0">
    <property name="geometry">
     <rect>
      <x>180</x>
      <y>0</y>
      <width>80</width>
      <height>16</height>
     </rect>
    </property>
    <property name="text">
     <string>PushButton_0</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>20</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

Using pyqt5 and python 3.7. I tried to use import from Controller which leads to endless loop and self.parent().toolbarButton_0.setEnabled (False) leads to
AttributeError: 'QWidget' object has no attribute 'toolbarButton_0'

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Papageno
  • 305
  • 3
  • 15

1 Answers1

0

Instead of trying to manipulate the Controller widget from W0 directly, you could use signals and slots to communicate. For example, in W0 you could do something like

class W0(QMainWindow):
    button_clicked = QtCore.pyqtSignal()

    def __init__(self):
        ...
        self.ui.pushButton.clicked.connect(self.pbClicked)

    ....

    def pbClicked(self):
        print (f"-> win{self.id}.pbClicked")
        self.button_clicked.emit()

And in Controller

class Controller(QMainWindow):

    ....

    def switchWin(self, id=0, initonly = False):
        print ("-> switchWin:", id)
        if id not in self.winDict.keys ():
            if id == 0:
                self.winDict[id] = W0 ()
                self.winDict[id].button_clicked.connect(self.W0_button_clicked)
        ....

    def W0_button_clicked(self):
        self.toolbarButton_0.setEnabled (False)

Of course, in Controller.SwitchWin you could directly connect to the signal self.winDict[id].ui.pushButton.clicked, but creating a separate signal in W0 and emit that when the button is clicked has the advantage that you can change the Gui of W0 without having to rewrite the signal-slot connections in Controller. Also, you can use the signal to send additional data if needed by specifying input parameters for the signal.

Heike
  • 24,102
  • 2
  • 31
  • 45