PyQt - легко добавить QWidget ко всем представлениям - PullRequest
1 голос
/ 07 апреля 2019

У меня есть следующее

class MyView(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        layout = QVBoxLayout()
        layout.addWidget(QLabel('Hello World'))
        self.setLayout(layout)


class NavigationMenu(QWidget):
    pass
    # Renders a bar of full width and 15 px height

Какой самый простой способ добавить NavigationMenu к MyView?

В будущем мне нужно будет также добавить NavigationMenu ко всем остальным представлениям, поэтому я ищу что-то масштабируемое с точки зрения типизации и удобства обслуживания.

Я пробовал декораторы (просто @NavigationMenuDecorator поверх класса), но я либо не могу связать их, либоони инициализируются во время разбора и при ошибке QWidget: Must construct a QApplication before a QWidget.

Я пытался просто добавить его в MyView, но там много шаблонов

class MyWidget(Widget.QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        layout = Widget.QVBoxLayout()
        layout.addWidget(QLabel('Hello World'))

        topLayout = Widget.QVBoxLayout()
        topLayout.setContentsMargins(0, 0, 0, 0)
        topLayout.addWidget(NavigationMenu())
        topLayout.addLayout(layout)

        self.setLayout(topLayout)

Ответы [ 2 ]

3 голосов
/ 07 апреля 2019

Возможное решение - использовать метакласс:

from PyQt5 import QtCore, QtWidgets

class NavigationMenu(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(QtWidgets.QLabel("NavigationMenu"))

class MetaNavigationMenu(type(QtWidgets.QWidget), type):
    def __call__(cls, *args, **kw):
        obj = super().__call__(*args, **kw)
        lay = obj.layout()
        if lay is not None:
            lay.addWidget(NavigationMenu())
        return obj

class View(QtWidgets.QWidget, metaclass=MetaNavigationMenu):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(QtWidgets.QLabel('Hello World'))
        self.setLayout(layout)

if __name__=="__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = View()
    w.show()
    sys.exit(app.exec_())

Обновление:

С помощью следующего метода вы можете добавить представление и дополнительные аргументы, необходимые для представления:

from PyQt5 import QtCore, QtWidgets

class NavigationMenu(QtWidgets.QWidget):
    def __init__(self, value, text="", parent=None):
        super().__init__(parent)
        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(QtWidgets.QLabel(text))
        print(value)

class MetaMenu(type(QtWidgets.QWidget), type):
    def __new__(cls, class_name, parents, attrs, **kwargs):
        cls._view = kwargs.pop('view', None)
        cls._args = kwargs.pop('args', tuple())
        cls._kwargs = kwargs.pop('kwargs', dict())
        return type.__new__(cls, class_name, parents, attrs)

    def __call__(cls, *args, **kw):
        obj = super().__call__(*args, **kw)
        layout = getattr(obj, 'layout', None)
        if callable(layout) and View is not None:
            layout().addWidget(cls._view(*cls._args, **cls._kwargs))
        return obj

class View(QtWidgets.QWidget, metaclass=MetaMenu, view=NavigationMenu, args=(10, ), kwargs={"text": "NavigationMenu"}):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(QtWidgets.QLabel('Hello World'))
        self.setLayout(layout)

if __name__=="__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = View()
    w.show()
    sys.exit(app.exec_())
0 голосов
/ 07 апреля 2019

Другое решение здесь удивительно, и я многое узнал о метаклассах.Тем не менее, это довольно трудно читать и добавляет ненужную сложность.Я остановился на композиционном подходе, где я просто извлек шаблон для отдельной функции.

Функция add_navigation() оборачивает старый макет в виджет, создает QVBoxLayout с NavigationMenu и старыймакет, и, наконец, меняет макеты.

def add_navigation(widget, title)
    main = QWidget()
    main.setLayout(widget.layout())

    layout = QVBoxLayout()
    layout.setContentsMargins(0, 0, 0, 0)
    layout.setSpacing(0)
    layout.addWidget(NavigationBar(title))
    layout.addWidget(main)

    widget.setLayout(layout)

У нас есть только один вкладыш в шаблон, и код становится.

class MyView(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        layout = QVBoxLayout()
        layout.addWidget(QLabel('Hello World'))
        self.setLayout(layout)

        add_navigation(self, 'Navigation Title')


class NavigationMenu(QWidget):
    pass
    # Renders a bar of full width and 15 px height
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...