3

当一个小部件停靠时,我希望它改变方向并相对于扩展坞的扩展方式具有最小的尺寸。

那是,

  • 左/右停靠 => 具有最小宽度的从上到下的方向
  • 顶部/底部停靠 => 具有最小高度的从左到右方向

问题是,每当方向改变时,dock 就会出现看似任意的宽度或高度。停靠时,我找不到将停靠小部件调整大小/强制为特定大小的方法。我尝试了无数种覆盖sizeHint, minimumSizeHint, 调用adjustSize和摆弄sizePolicy.

如何确保初始坞站大小?


我的基本应用程序如下所示:

在此处输入图像描述

该应用程序显示主要和次要信息以及相应的控件集。包含主要和次要内容的选项卡小部件被设置为中央小部件。一个 QStackedWidget 容纳了各个仪表板中的控件,位于一个扩展坞中。当标签更改时,显示相应的仪表板。下面在基本应用程序代码中给出了此代码

困难在于改变仪表板的方向会扰乱码头的尺寸。

调整仪表盘方向,我可以想到两个合理的解决方案:

  • 通过resizeEvent
  • 通过dockLocationChanged信号

通过调整方向resizeEvent

在我看来,这似乎是更可取的选择。它为用户提供了最大的灵活性。如果他们不喜欢停靠点的方向,将其拖过特定限制将允许他们更改停靠点的方向。在这里,我检查它是否宽于高。

    class MyDock(QtWidgets.QDockWidget):

        def __init__(self):

            super(MyDock, self).__init__()

        def resizeEvent(self, event):
            size = event.size()
            is_wide = size.width() > size.height()

            container_object = self.widget().currentWidget()

            if is_wide:
                container_object.setDirection(QtWidgets.QBoxLayout.LeftToRight)
            else:
                container_object.setDirection(QtWidgets.QBoxLayout.TopToBottom)

下面在resize 方法中给出了完整的代码。

改变方向dockLocationChange

由于调整大小事件一直在发生,另一种方法可能是仅在停靠位置更改时更改方向。为此,请将功能连接到dockLocationChanged信号并根据扩展坞调整方向。

    class MyDock(QtWidgets.QDockWidget):

        def __init__(self):

            super(MyDock, self).__init__()

            self.dockLocationChanged.connect(self.dock_location_changed)

        def dock_location_changed(self, area):
            top    = QtCore.Qt.DockWidgetArea.TopDockWidgetArea
            bottom = QtCore.Qt.DockWidgetArea.BottomDockWidgetArea

            container_object = self.widget().currentWidget()

            if area in [top, bottom]:
                container_object.setDirection(QtWidgets.QBoxLayout.LeftToRight)
            else:
                container_object.setDirection(QtWidgets.QBoxLayout.TopToBottom)

基本应用代码

该计划由 5 个独立的课程组成。

为了

  1. MyWindow,
  2. PrimaryDashboard, 和
  3. SecondaryDashboard

分离的原因应该足够清楚。

为了

  1. MyDock
  2. DockContainer

分隔是为了便于覆盖sizeHint,setDirection或其他方法。

    import qtpy
    from qtpy import QtWidgets, QtGui, QtCore
    import sys


    class PrimaryDashboard(QtWidgets.QWidget):

        def __init__(self):

            super(PrimaryDashboard, self).__init__()

            self.init_widgets()
            self.init_layout()

        def init_widgets(self):
            self.label = QtWidgets.QLabel('Primary dashboard')
            self.ok = QtWidgets.QPushButton('OK')
            self.cancel = QtWidgets.QPushButton('Cancel')

        def init_layout(self):
            self.layout = QtWidgets.QHBoxLayout()
            self.layout.addWidget(self.label)
            self.layout.addWidget(self.ok)
            self.layout.addWidget(self.cancel)
            self.setLayout(self.layout)

        def setDirection(self, direction):
            self.layout.setDirection(direction)


    class SecondaryDashboard(QtWidgets.QWidget):

        def __init__(self):

            super(SecondaryDashboard, self).__init__()

            self.init_widgets()
            self.init_layout()

        def init_widgets(self):
            self.label = QtWidgets.QLabel('Secondary dashboard')

            self.descr1 = QtWidgets.QLabel('Thing 1')
            self.check1 = QtWidgets.QCheckBox()

            self.descr2 = QtWidgets.QLabel('Thing 2')
            self.check2 = QtWidgets.QCheckBox()

        def init_layout(self):
            self.layout = QtWidgets.QVBoxLayout()

            self.grid = QtWidgets.QGridLayout()
            self.grid.addWidget(self.descr1, 0, 0)
            self.grid.addWidget(self.check1, 0, 1)
            self.grid.addWidget(self.descr2, 1, 0)
            self.grid.addWidget(self.check2, 1, 1)

            self.layout.addWidget(self.label)
            self.layout.addLayout(self.grid)
            self.setLayout(self.layout)

        def setDirection(self, direction):
            self.layout.setDirection(direction)


    class DockContainer(QtWidgets.QStackedWidget):

        def __init__(self):

            super(DockContainer, self).__init__()


    class MyDock(QtWidgets.QDockWidget):

        def __init__(self):

            super(MyDock, self).__init__()


    class MyWindow(QtWidgets.QMainWindow):

        def __init__(self, parent=None):
            super(MyWindow, self).__init__(parent=parent)

            self.resize(600, 400)

            self.init_widgets()
            self.init_layout()

        def init_widgets(self):

            self.tab_widget = QtWidgets.QTabWidget()
            self.tab1 = QtWidgets.QLabel('Primary content')
            self.tab2 = QtWidgets.QLabel('Secondary content')
            self.tab_widget.addTab(self.tab1, 'Primary')
            self.tab_widget.addTab(self.tab2, 'Secondary')
            self.tab_widget.currentChanged.connect(self.tab_selected)

            self.primary_dashboard = PrimaryDashboard()
            self.secondary_dashboard = SecondaryDashboard()

            self.dashboard = DockContainer()
            self.dashboard.addWidget(self.primary_dashboard)
            self.dashboard.addWidget(self.secondary_dashboard)
            self.dashboard.setCurrentWidget(self.primary_dashboard)

            self.dock = MyDock()
            self.dock.setWidget(self.dashboard)
            self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.dock)

        def init_layout(self):
            self.main_layout = QtWidgets.QVBoxLayout()
            self.main_layout.addWidget(self.tab_widget)

            self.main_widget = QtWidgets.QWidget()
            self.main_widget.setLayout(self.main_layout)
            self.setCentralWidget(self.main_widget)

        def tab_selected(self):
            tab_index = self.tab_widget.currentIndex()
            if self.tab_widget.tabText(tab_index) == 'Secondary':
                self.dashboard.setCurrentWidget(self.secondary_dashboard)
                self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dock)
            else:  # Primary
                self.dashboard.setCurrentWidget(self.primary_dashboard)
                self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.dock)


    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)

        window = MyWindow()
        window.show()
        sys.exit(app.exec_())

调整大小方法

此代码与基本应用程序代码相同,但resizeEvent在停靠小部件中被覆盖。

    import qtpy
    from qtpy import QtWidgets, QtGui, QtCore
    import sys


    class PrimaryDashboard(QtWidgets.QWidget):

        def __init__(self):

            super(PrimaryDashboard, self).__init__()

            self.init_widgets()
            self.init_layout()

        def init_widgets(self):
            self.label = QtWidgets.QLabel('Primary dashboard')
            self.ok = QtWidgets.QPushButton('OK')
            self.cancel = QtWidgets.QPushButton('Cancel')

        def init_layout(self):
            self.layout = QtWidgets.QHBoxLayout()
            self.layout.addWidget(self.label)
            self.layout.addWidget(self.ok)
            self.layout.addWidget(self.cancel)
            self.setLayout(self.layout)

        def setDirection(self, direction):
            self.layout.setDirection(direction)


    class SecondaryDashboard(QtWidgets.QWidget):

        def __init__(self):

            super(SecondaryDashboard, self).__init__()

            self.init_widgets()
            self.init_layout()

        def init_widgets(self):
            self.label = QtWidgets.QLabel('Secondary dashboard')

            self.descr1 = QtWidgets.QLabel('Thing 1')
            self.check1 = QtWidgets.QCheckBox()

            self.descr2 = QtWidgets.QLabel('Thing 2')
            self.check2 = QtWidgets.QCheckBox()

        def init_layout(self):
            self.layout = QtWidgets.QVBoxLayout()

            self.grid = QtWidgets.QGridLayout()
            self.grid.addWidget(self.descr1, 0, 0)
            self.grid.addWidget(self.check1, 0, 1)
            self.grid.addWidget(self.descr2, 1, 0)
            self.grid.addWidget(self.check2, 1, 1)

            self.layout.addWidget(self.label)
            self.layout.addLayout(self.grid)
            self.setLayout(self.layout)

        def setDirection(self, direction):
            self.layout.setDirection(direction)


    class DockContainer(QtWidgets.QStackedWidget):

        def __init__(self):

            super(DockContainer, self).__init__()


    class MyDock(QtWidgets.QDockWidget):

        def __init__(self):

            super(MyDock, self).__init__()

        def resizeEvent(self, event):
            size = event.size()
            is_wide = size.width() > size.height()

            container_object = self.widget().currentWidget()

            if is_wide:
                container_object.setDirection(QtWidgets.QBoxLayout.LeftToRight)
            else:
                container_object.setDirection(QtWidgets.QBoxLayout.TopToBottom)


    class MyWindow(QtWidgets.QMainWindow):

        def __init__(self, parent=None):
            super(MyWindow, self).__init__(parent=parent)

            self.resize(600, 400)

            self.init_widgets()
            self.init_layout()

        def init_widgets(self):

            self.tab_widget = QtWidgets.QTabWidget()
            self.tab1 = QtWidgets.QLabel('Primary content')
            self.tab2 = QtWidgets.QLabel('Secondary content')
            self.tab_widget.addTab(self.tab1, 'Primary')
            self.tab_widget.addTab(self.tab2, 'Secondary')
            self.tab_widget.currentChanged.connect(self.tab_selected)

            self.primary_dashboard = PrimaryDashboard()
            self.secondary_dashboard = SecondaryDashboard()

            self.dashboard = DockContainer()
            self.dashboard.addWidget(self.primary_dashboard)
            self.dashboard.addWidget(self.secondary_dashboard)
            self.dashboard.setCurrentWidget(self.primary_dashboard)

            self.dock = MyDock()
            self.dock.setWidget(self.dashboard)
            self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.dock)

        def init_layout(self):
            self.main_layout = QtWidgets.QVBoxLayout()
            self.main_layout.addWidget(self.tab_widget)

            self.main_widget = QtWidgets.QWidget()
            self.main_widget.setLayout(self.main_layout)
            self.setCentralWidget(self.main_widget)

        def tab_selected(self):
            tab_index = self.tab_widget.currentIndex()
            if self.tab_widget.tabText(tab_index) == 'Secondary':
                self.dashboard.setCurrentWidget(self.secondary_dashboard)
                self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dock)
            else:  # Primary
                self.dashboard.setCurrentWidget(self.primary_dashboard)
                self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.dock)


    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)

        window = MyWindow()
        window.show()
        sys.exit(app.exec_())

dockLocationChanged方法

此代码与基本应用程序代码相同,但dockLocationChanged信号连接到根据当前停靠位置调整方向的方法。

    import qtpy
    from qtpy import QtWidgets, QtGui, QtCore
    import sys


    class PrimaryDashboard(QtWidgets.QWidget):

        def __init__(self):

            super(PrimaryDashboard, self).__init__()

            self.init_widgets()
            self.init_layout()

        def init_widgets(self):
            self.label = QtWidgets.QLabel('Primary dashboard')
            self.ok = QtWidgets.QPushButton('OK')
            self.cancel = QtWidgets.QPushButton('Cancel')

        def init_layout(self):
            self.layout = QtWidgets.QHBoxLayout()
            self.layout.addWidget(self.label)
            self.layout.addWidget(self.ok)
            self.layout.addWidget(self.cancel)
            self.setLayout(self.layout)

        def setDirection(self, direction):
            self.layout.setDirection(direction)


    class SecondaryDashboard(QtWidgets.QWidget):

        def __init__(self):

            super(SecondaryDashboard, self).__init__()

            self.init_widgets()
            self.init_layout()

        def init_widgets(self):
            self.label = QtWidgets.QLabel('Secondary dashboard')

            self.descr1 = QtWidgets.QLabel('Thing 1')
            self.check1 = QtWidgets.QCheckBox()

            self.descr2 = QtWidgets.QLabel('Thing 2')
            self.check2 = QtWidgets.QCheckBox()

        def init_layout(self):
            self.layout = QtWidgets.QVBoxLayout()

            self.grid = QtWidgets.QGridLayout()
            self.grid.addWidget(self.descr1, 0, 0)
            self.grid.addWidget(self.check1, 0, 1)
            self.grid.addWidget(self.descr2, 1, 0)
            self.grid.addWidget(self.check2, 1, 1)

            self.layout.addWidget(self.label)
            self.layout.addLayout(self.grid)
            self.setLayout(self.layout)

        def setDirection(self, direction):
            self.layout.setDirection(direction)


    class DockContainer(QtWidgets.QStackedWidget):

        def __init__(self):

            super(DockContainer, self).__init__()


    class MyDock(QtWidgets.QDockWidget):

        def __init__(self):

            super(MyDock, self).__init__()

            self.dockLocationChanged.connect(self.dock_location_changed)

        def dock_location_changed(self, area):
            top    = QtCore.Qt.DockWidgetArea.TopDockWidgetArea
            bottom = QtCore.Qt.DockWidgetArea.BottomDockWidgetArea
            # left   = QtCore.Qt.DockWidgetArea.LeftDockWidgetArea
            # right  = QtCore.Qt.DockWidgetArea.RightDockWidgetArea

            container_object = self.widget().currentWidget()

            if area in [top, bottom]:
                container_object.setDirection(QtWidgets.QBoxLayout.LeftToRight)
            else:
                container_object.setDirection(QtWidgets.QBoxLayout.TopToBottom)


    class MyWindow(QtWidgets.QMainWindow):

        def __init__(self, parent=None):
            super(MyWindow, self).__init__(parent=parent)

            self.resize(600, 400)

            self.init_widgets()
            self.init_layout()

        def init_widgets(self):

            self.tab_widget = QtWidgets.QTabWidget()
            self.tab1 = QtWidgets.QLabel('Primary content')
            self.tab2 = QtWidgets.QLabel('Secondary content')
            self.tab_widget.addTab(self.tab1, 'Primary')
            self.tab_widget.addTab(self.tab2, 'Secondary')
            self.tab_widget.currentChanged.connect(self.tab_selected)

            self.primary_dashboard = PrimaryDashboard()
            self.secondary_dashboard = SecondaryDashboard()

            self.dashboard = DockContainer()
            self.dashboard.addWidget(self.primary_dashboard)
            self.dashboard.addWidget(self.secondary_dashboard)
            self.dashboard.setCurrentWidget(self.primary_dashboard)

            self.dock = MyDock()
            self.dock.setWidget(self.dashboard)
            self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.dock)

        def init_layout(self):
            self.main_layout = QtWidgets.QVBoxLayout()
            self.main_layout.addWidget(self.tab_widget)

            self.main_widget = QtWidgets.QWidget()
            self.main_widget.setLayout(self.main_layout)
            self.setCentralWidget(self.main_widget)

        def tab_selected(self):
            tab_index = self.tab_widget.currentIndex()
            if self.tab_widget.tabText(tab_index) == 'Secondary':
                self.dashboard.setCurrentWidget(self.secondary_dashboard)
                self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dock)
            else:  # Primary
                self.dashboard.setCurrentWidget(self.primary_dashboard)
                self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.dock)


    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)

        window = MyWindow()
        window.show()
        sys.exit(app.exec_())
4

1 回答 1

1

将应用程序视为一组俄罗斯套娃。内部娃娃的大小决定了后续外部娃娃的大小。显然,一个内部娃娃不能比包含它的那个更大! QWidgets的建模类似。

默认情况下,不提供大小提示的复合小部件将根据其子小部件的空间要求调整大小。

QWidget.sizeHint()的文档继续说,

如果此小部件没有布局,则 QWidget.sizeHint() 的默认实现返回无效大小,否则返回布局的首选大小。

总而言之,一个小部件的尺寸是由内而外的,基于布局。

如果您要为基本应用程序代码中的每个对象实现resizeEvent1,您将看到以下尺寸序列,

  1. PrimaryDashboard调整大小事件
  2. DockContainer调整大小事件
  3. MyDock调整大小事件

这是我们期望的嵌套。首先PrimaryDashboard咨询其大小,然后是DockContainer,然后是MyDock。从技术上讲,它一直是小部件。但是,PrimaryDashboard在大多数情况下,包含的按钮和标签应该小于 MainWindow 的宽度/高度。序列中第一个显着影响码头尺寸的玩偶是PrimaryDashboard.

检查resizeEvent,使用event.size(),我们可以看到水平仪表板的合理最小值是120像素高度,而垂直方向的合理最小宽度为146. 然后sizeHint()可以将 设置为返回并且对于每个仪表板2minimumSizeHint()具有最小返回。实际上,这告诉应用程序更喜欢最小大小,同时通常仍允许调整大小。(146, 120)(146, 120)

def sizeHint(self):
    return self.minimumSizeHint()

def minimumSizeHint(self):
    return QtCore.QSize(146, 120)

诚然,使用固定大小可能是危险的,因为绝对值是不可原谅的,并且根据定义不灵活。但是,内容可能具有自然的最小大小3。我们可以简单地setMinimumSize()在整个应用程序上不允许调整小于我们的minimumSizeHint().

要改变停靠小部件内容的方向,我们可以使用dockLocationChanged信号。我们还可以使代码比问题中的呈现方式更简洁一些。与其在停靠小部件中连接信号,我们可以在MyWindow. 其实根本不需要定义MyDock。一个平原QDockWidget就足够了。

具有最小初始大小的码头

import qtpy
from qtpy import QtWidgets, QtGui, QtCore
import sys


class PrimaryDashboard(QtWidgets.QWidget):

    def __init__(self):

        super(PrimaryDashboard, self).__init__()

        self.init_widgets()
        self.init_layout()

    def init_widgets(self):
        self.label = QtWidgets.QLabel('Primary dashboard')
        self.ok = QtWidgets.QPushButton('OK')
        self.cancel = QtWidgets.QPushButton('Cancel')

    def init_layout(self):
        self.layout = QtWidgets.QHBoxLayout()
        self.layout.addWidget(self.label)
        self.layout.addWidget(self.ok)
        self.layout.addWidget(self.cancel)
        self.setLayout(self.layout)

    def setDirection(self, direction):
        self.layout.setDirection(direction)

    def sizeHint(self):
        return self.minimumSizeHint()

    def minimumSizeHint(self):
        return QtCore.QSize(146, 120)


class SecondaryDashboard(QtWidgets.QWidget):

    def __init__(self):

        super(SecondaryDashboard, self).__init__()

        self.init_widgets()
        self.init_layout()

    def init_widgets(self):
        self.label = QtWidgets.QLabel('Secondary dashboard')

        self.descr1 = QtWidgets.QLabel('Thing 1')
        self.check1 = QtWidgets.QCheckBox()

        self.descr2 = QtWidgets.QLabel('Thing 2')
        self.check2 = QtWidgets.QCheckBox()

    def init_layout(self):
        self.layout = QtWidgets.QVBoxLayout()

        self.grid = QtWidgets.QGridLayout()
        self.grid.addWidget(self.descr1, 0, 0)
        self.grid.addWidget(self.check1, 0, 1)
        self.grid.addWidget(self.descr2, 1, 0)
        self.grid.addWidget(self.check2, 1, 1)

        self.layout.addWidget(self.label)
        self.layout.addLayout(self.grid)
        self.setLayout(self.layout)

    def setDirection(self, direction):
        self.layout.setDirection(direction)

    def sizeHint(self):
        return self.minimumSizeHint()

    def minimumSizeHint(self):
        return QtCore.QSize(146, 120)


class DockContainer(QtWidgets.QStackedWidget):

    def __init__(self):

        super(DockContainer, self).__init__()

    def dock_location_changed(self, area):
        top    = QtCore.Qt.DockWidgetArea.TopDockWidgetArea
        bottom = QtCore.Qt.DockWidgetArea.BottomDockWidgetArea

        container_object = self.currentWidget()

        if area in [top, bottom]:
            container_object.setDirection(QtWidgets.QBoxLayout.LeftToRight)
        else:
            container_object.setDirection(QtWidgets.QBoxLayout.TopToBottom)


class MyWindow(QtWidgets.QMainWindow):

    def __init__(self, parent=None):
        super(MyWindow, self).__init__(parent=parent)

        # Force minimumSize to ensure a sensible dashboard size
        self.setMinimumSize(QtCore.QSize(600, 400))

        self.init_widgets()
        self.init_layout()

    def init_widgets(self):

        self.tab_widget = QtWidgets.QTabWidget()
        self.tab1 = QtWidgets.QLabel('Primary content')
        self.tab2 = QtWidgets.QLabel('Secondary content')
        self.tab_widget.addTab(self.tab1, 'Primary')
        self.tab_widget.addTab(self.tab2, 'Secondary')
        self.tab_widget.currentChanged.connect(self.tab_selected)

        self.primary_dashboard = PrimaryDashboard()
        self.secondary_dashboard = SecondaryDashboard()

        self.dashboard = DockContainer()
        self.dashboard.addWidget(self.primary_dashboard)
        self.dashboard.addWidget(self.secondary_dashboard)
        self.dashboard.setCurrentWidget(self.primary_dashboard)

        self.dock = QtWidgets.QDockWidget()
        self.dock.setWidget(self.dashboard)
        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.dock)

        # Connect signal at the main application level
        self.dock.dockLocationChanged.connect(self.dashboard.dock_location_changed)

    def init_layout(self):
        self.main_layout = QtWidgets.QVBoxLayout()
        self.main_layout.addWidget(self.tab_widget)

        self.main_widget = QtWidgets.QWidget()
        self.main_widget.setLayout(self.main_layout)
        self.setCentralWidget(self.main_widget)

    def tab_selected(self):
        tab_index = self.tab_widget.currentIndex()
        if self.tab_widget.tabText(tab_index) == 'Secondary':
            self.dashboard.setCurrentWidget(self.secondary_dashboard)
            self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dock)
        else:  # Primary
            self.dashboard.setCurrentWidget(self.primary_dashboard)
            self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.dock)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)

    window = MyWindow()
    window.show()
    sys.exit(app.exec_())


1.如何实现这样的resizeEvent,看看谁在调整大小:

    def resizeEvent(self, event):
        print('resizeEvent for ', self, flush=True)

2.一个自然的问题是 ,为什么不简单sizeHint()minimumSizeHint()最低限度,如您所料。sizeHint()

和方法是虚函数sizeHint()minimumSizeHint()我的猜测是 Qt 具有其他我们不知道的功能,它们独立地引用这些方法,要求我们以这种方式定义事物。

3.例如,如果内容是地图,用户不太可能希望地图是10px x 10px. 600 x 400此外,除非他们在移动设备上,否则用户不会使用低于以下分辨率的屏幕分辨率工作,这是一个合理的假设。但是,如果您正在使用 PySide 或 PyQt5 进行移动开发,那么您应该问自己一些重要的问题,例如“为什么?” .

于 2019-04-22T16:13:43.997 回答