Как передать метод экземпляра главного окна, в другое глубоко вложенное окно
Добрый день есть приложение на PyQt5. В MainWindow реализован специфический метод addWidgetToMDI(). Функция addWidgetToMDI() работает с виджетами главного окна и, в частности, QMdiArea.
Вложенность окон большая (главное окно вызывает первое, первое вызывает второе, втрое третье и т. д.), и каждое вложенное окно должно иметь доступ к функции addWidgetToMDI() экземпляра класс MainWindow.
Подскажите есть ли простой прием получить ссылку на эту функцию? Или как из любого места получить экземпляр класса MainWindow.
Ниже представлены пример.
main.py
#!/usr/bin/env/python3
# -*- coding: utf-8 -*-
import datetime
import os
import sys
from PyQt5 import QtCore
from PyQt5.QtWidgets import QApplication
from mainWindow import MainWindow
class App(QApplication):
__version__ = 1
__dateVer__ = datetime.date.today().strftime('%d-%m-%Y')
confDir = os.path.abspath(os.curdir)
# обратите внимание settings тоже передаю в другие окна в нем я сохраняю геометрию и настройки окон выбранные
# пользователем
settings = QtCore.QSettings(os.path.join(confDir, ' view_conf.ini'), QtCore.QSettings.IniFormat)
settings.setIniCodec("utf-8")
def main():
app = App(sys.argv)
# МОЙ КАСТЫЛЬ с помощью которого получаю ссылку на экземпляр класса main_window
app.main_window = MainWindow()
app.main_window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
mainWindow.py
#!/usr/bin/env/python3
# -*- coding: utf-8 -*-
import datetime
from PyQt5 import QtCore, QtGui, QtWidgets
from window_1 import WindowOne
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setObjectName("MainWindow")
self.resize(800, 600)
self.action_window_1 = QtWidgets.QAction(self)
self.action_window_1.setObjectName(u"action_window_1")
self.centralwidget = QtWidgets.QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName(u"gridLayout")
self.mdiArea = MdiAreaCustom(self.centralwidget)
self.mdiArea.setObjectName("mdiArea")
self.gridLayout.addWidget(self.mdiArea, 0, 0, 1, 1)
self.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(self)
self.menubar.setObjectName("menubar")
self.menu = QtWidgets.QMenu(self.menubar)
self.menu.setObjectName("menu")
self.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(self)
self.statusbar.setObjectName("statusbar")
self.setStatusBar(self.statusbar)
self.menubar.addAction(self.menu.menuAction())
self.menu.addAction(self.action_window_1)
self.setWindowTitle("MainWindow")
self.menu.setTitle("Данные")
self.action_window_1.setText('вызвать window_1')
self.settings = QtCore.QCoreApplication.instance().settings
self.action_window_1.triggered.connect(lambda: self.addWidgetToMDI(parent=self, form=WindowOne()))
# !!! Это упрощеный вариант функции которые должны получить все окна
# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
def addWidgetToMDI(self, parent: QtWidgets.QWidget, form: QtWidgets.QWidget) -> None:
sub = MdiSubWindow(parent, flags=form.windowFlags())
sub.setAttribute(QtCore.Qt.WA_DeleteOnClose)
sub.setWidget(form)
sub = self.mdiArea.addSubWindow(sub)
self.menubar.setDisabled(True)
if len(self.mdiArea.subWindowList()) > 1:
self.mdiArea.subWindowList()[-2].setDisabled(True)
sub.closeSub.connect(self.unblockWindow)
sub.resize(form.size().width() + 18, form.size().height() + 34)
sub.show()
sub.setFocus()
def unblockWindow(self):
# Блокирую родителя и меню,
# пользователь должен иметь возможность работать только с одним окном и ни как иначе
if len(self.mdiArea.subWindowList()) > 0:
self.mdiArea.subWindowList()[-1].setDisabled(False)
self.mdiArea.activatePreviousSubWindow()
else:
self.menubar.setDisabled(False)
class MdiSubWindow(QtWidgets.QMdiSubWindow):
closeSub = QtCore.pyqtSignal()
def __init__(self, parent=None, flags=QtCore.Qt.Widget):
super().__init__(parent, flags)
if parent:
self.batya = parent
self.setObjectName(parent.objectName())
self.setStyleSheet('background-color: rgb(240, 240, 240)')
def closeEvent(self, closeEvent: QtGui.QCloseEvent) -> None:
super().closeEvent(closeEvent)
self.closeSub.emit()
closeEvent.accept()
@property
def getDirParent(self) -> object:
# при добавлении окна в mdiarea родителем становится mdiarea,
# чтобы получить доступ к методам и атрибутам класса родителя использую
return self.batya
class MdiAreaCustom(QtWidgets.QMdiArea):
def __init__(self, parent):
super().__init__(parent)
font = QtGui.QFont()
font.setPointSize(17)
self.textDate = QtGui.QStaticText(f"""<font style="color: rgb(77, 75, 227); font-size: 17px; font-weight: bold">
Сегодня {datetime.date.today().strftime('%d.%m.%Y')}</font>""")
self.textDate.setTextWidth(450)
self.VerApp = QtGui.QStaticText()
self.VerApp.setTextWidth(450)
# Ниже идет набор функций необходимы визуала в данном примере представлена только одна
def enterVersionApp(self, text=''):
pass
файл window_1.py
#!/usr/bin/env/python3
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtWidgets
from window_2 import WindowTwo
class WindowOne(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(400, 300)
self.pushButton = QtWidgets.QPushButton(self)
self.pushButton.setObjectName(u"pushButton")
self.pushButton.setGeometry(QtCore.QRect(130, 120, 181, 23))
self.setWindowTitle("Form")
self.pushButton.setText("вызвать окно 2")
# !!! Еще раз повторюсь вложенность окон глубокая, вовремя передачи ссылки через self.parent
# я теряю связь с родителем так как родителем становится QMdiArea
# vvvvvvvvvv Так получаю доступ к экземпляру APP
self.settings = QtCore.QCoreApplication.instance().settings
# vvvvvvvvvv Так получаю доступ к методам MainWindow на мой взгляд крайне неудачная практика
# собственно в этом и вопрос. Как получать методы экземпляра класса main_window
self.addWidgetToMDI = QtCore.QCoreApplication.instance().main_window.addWidgetToMDI
self.pushButton.clicked.connect(lambda: self.addWidgetToMDI(parent=self, form=WindowTwo()))
файл window_2.py
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtWidgets
class WindowTwo(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(400, 300)
self.setWindowTitle("Form_2")
self.settings = QtCore.QCoreApplication.instance().settings
self.addWidgetToMDI = QtCore.QCoreApplication.instance().main_window.addWidgetToMDI
Ответы (1 шт):
Я отметил для вас строки, в которые внес изменения.
q1444033_main.py
import sys
import datetime
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qt import *
from q1444033_window_1 import WindowOne
class MdiAreaCustom(QtWidgets.QMdiArea):
def __init__(self, parent):
super().__init__(parent)
font = QtGui.QFont()
font.setPointSize(17)
self.textDate = QtGui.QStaticText(f"""
<font style="color: rgb(77, 75, 227); font-size: 17px; font-weight: bold">
Сегодня {datetime.date.today().strftime('%d.%m.%Y')}</font>
""")
self.textDate.setTextWidth(450)
self.VerApp = QtGui.QStaticText()
self.VerApp.setTextWidth(450)
# Ниже идет набор функций необходимы визуала в данном примере представлена только одна
def enterVersionApp(self, text=''):
print(f'def enterVersionApp(self, text=''): {text} ???????????????') #
pass
class MdiSubWindow(QtWidgets.QMdiSubWindow):
closeSub = QtCore.pyqtSignal()
def __init__(self, parent=None, flags=QtCore.Qt.Widget):
super().__init__(parent, flags)
# ??? if parent:
# ??? self.batya = parent
# ??? self.setObjectName(parent.objectName())
# +++
self.parent = parent # +++
self.setStyleSheet('background-color: rgb(240, 240, 240)')
def closeEvent(self, closeEvent: QtGui.QCloseEvent) -> None:
super().closeEvent(closeEvent)
self.closeSub.emit()
closeEvent.accept()
# ??? vvvvvvvvvvvvvvvvvvvvvvvvv
@property
def getDirParent(self) -> object:
# при добавлении окна в mdiarea родителем становится mdiarea,
# чтобы получить доступ к методам и атрибутам класса родителя использую
return self.batya
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setObjectName("MainWindow")
self.resize(800, 600)
# +++
self.subs = [] # +++
self.action_window_1 = QtWidgets.QAction(self)
self.action_window_1.setObjectName(u"action_window_1")
self.centralwidget = QtWidgets.QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName(u"gridLayout")
self.mdiArea = MdiAreaCustom(self.centralwidget)
self.mdiArea.setObjectName("mdiArea")
self.gridLayout.addWidget(self.mdiArea, 0, 0, 1, 1)
self.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(self)
self.menubar.setObjectName("menubar")
self.menu = QtWidgets.QMenu(self.menubar)
self.menu.setObjectName("menu")
self.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(self)
self.statusbar.setObjectName("statusbar")
self.setStatusBar(self.statusbar)
self.menubar.addAction(self.menu.menuAction())
self.menu.addAction(self.action_window_1)
self.setWindowTitle("MainWindow")
self.menu.setTitle("Данные")
self.action_window_1.setText('вызвать window_1')
# self.settings = QtCore.QCoreApplication.instance().settings
self.action_window_1.triggered.connect(
# +++-------------------------------------------------------------> self <---- +++
lambda: self.addWidgetToMDI(parent=self, form=WindowOne(self)))
# ----------------------------------------------------------------->^^^^
def addWidgetToMDI(self, parent: QtWidgets.QWidget, form: QtWidgets.QWidget) -> None:
#print(f'def addWidgetToMDI -parent- {parent}')
#print(f'def addWidgetToMDI - form - {form}')
sub = MdiSubWindow(parent, flags=form.windowFlags())
sub.setAttribute(QtCore.Qt.WA_DeleteOnClose)
sub.setWidget(form)
sub = self.mdiArea.addSubWindow(sub)
self.menubar.setDisabled(True)
if len(self.mdiArea.subWindowList()) > 1:
self.mdiArea.subWindowList()[-2].setDisabled(True)
sub.closeSub.connect(self.unblockWindow)
sub.resize(form.size().width() + 18, form.size().height() + 34)
sub.show()
sub.setFocus()
# +++
self.subs.append(sub) # +++
def unblockWindow(self):
if len(self.mdiArea.subWindowList()) > 0:
self.mdiArea.subWindowList()[-1].setDisabled(False)
self.mdiArea.activatePreviousSubWindow()
else:
self.menubar.setDisabled(False)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec())
q1444033_window_1.py
from PyQt5 import QtCore, QtWidgets
from q1444033_window_2 import WindowTwo
class WindowOne(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# +++
self.parent = parent # +++
self.resize(400, 300)
self.pushButton = QtWidgets.QPushButton(self)
self.pushButton.setObjectName(u"pushButton")
self.pushButton.setGeometry(QtCore.QRect(130, 120, 181, 23))
self.setWindowTitle("Form")
self.pushButton.setText("вызвать окно 2")
# Так получаю доступ к экземпляру APP
# self.settings = QtCore.QCoreApplication.instance().settings
# Так получаю доступ к методам MainWindow на мой взгляд
# крайне неудачная практика собственно в этом и вопрос.
# Как получать методы экземпляра класса main_window
# ??? self.addWidgetToMDI = QtCore.QCoreApplication.instance().main_window.addWidgetToMDI
# !!! +++
self.pushButton.clicked.connect(
#- lambda: self.addWidgetToMDI(parent=self, form=WindowTwo()))
lambda: self.parent.addWidgetToMDI(parent=self.parent, form=WindowTwo(self.parent)))
# ----------------> ^^^^^^^^^^^ <-------------------> ^^^^^^^^^^^ <-------------> ^^^^^^^^^^^
q1444033_window_2.py
from PyQt5 import QtCore, QtWidgets
class WindowTwo(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# +++
self.parent = parent # +++
self.resize(400, 300)
self.setWindowTitle("Form_2")
# self.settings = QtCore.QCoreApplication.instance().settings
# ??? self.addWidgetToMDI = QtCore.QCoreApplication.instance().main_window.addWidgetToMDI
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self.pushButton = QtWidgets.QPushButton(self)
self.pushButton.setObjectName(u"pushButton")
self.pushButton.setGeometry(QtCore.QRect(130, 120, 181, 23))
self.pushButton.setText("вызвать окно MainWindow")
self.pushButton.clicked.connect(self._subs)
def _subs(self):
subWindows = self.parent.findChildren(QtWidgets.QMdiSubWindow)
#print(*subWindows, sep='\n')
for sub in self.parent.subs:
if sub in subWindows:
sub.close()
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


