Qt:停靠小部件如何获得其初始大小?
Qt: How does a dock widget get its initial size?
当一个小部件停靠时,我希望它改变方向并且相对于停靠扩展的方式具有最小尺寸。
即
- left/right 停靠 => 最小宽度的从上到下方向
- top/bottom 停靠 => 最小高度从左到右的方向
问题是,只要方向改变,停靠栏就会呈现出看似任意的宽度或高度。停靠时,我找不到将停靠小部件 resize/force 调整为特定大小的方法。我尝试了无数种覆盖 sizeHint
、minimumSizeHint
、调用 adjustSize
和摆弄 sizePolicy
.
的变体
如何确保初始停靠尺寸?
我的基本应用程序如下所示:
该应用程序显示主要和次要信息以及相应的控件集。包含主要内容和次要内容的选项卡小部件被设置为中央小部件。容纳各个仪表板中控件的 QStackedWidget 位于停靠栏中。当标签更改时,显示相应的仪表板。下面的 基本应用程序代码.
中给出了此代码
困难在于 改变仪表板的方向会扰乱扩展坞的大小。
调整dashboard的方向,我想到了两个比较合理的方案:
- 通过
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 approach.
给出了完整的代码
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 个独立的 类.
对于
MyWindow
、
PrimaryDashboard
和
SecondaryDashboard
分开的原因应该够清楚了吧
对于
MyDock
和
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_())
将一个应用程序视为一组 Matryoshka dolls. An inner doll's size dictates that of the subsequent outer ones. Clearly, an inner doll cannot be bigger than the one that contains it! QWidgets 相似建模。
By default, composite widgets which do not provide a size hint will
be sized according to the space requirements of their child widgets.
QWidget.sizeHint() 的文档接着说,
The default implementation of QWidget.sizeHint() returns
an invalid size if there is no layout for this widget, and returns the
layout’s preferred size otherwise.
总而言之,小部件的大小根据布局由内而外调整。
如果您要为 基本应用程序代码 中的每个对象实现 resizeEvent
1,您将请参阅以下尺码序列,
PrimaryDashboard
resizeEvent
DockContainer
resizeEvent
MyDock
resizeEvent
这是我们期望的嵌套。首先查询 PrimaryDashboard
的大小,然后是 DockContainer
,然后是 MyDock
。从技术上讲,它一直是小部件。但是,PrimaryDashboard
包含的按钮和标签在大多数情况下应该小于 MainWindow 的 width/height。序列中第一个显着影响码头尺寸的娃娃是 PrimaryDashboard
.
使用 event.size()
检查 resizeEvent
,我们可以看到水平仪表板的合理最小高度是 120
像素,而垂直方向具有合理的最小宽度146
。然后可以将 sizeHint()
设置为 return minimumSizeHint()
并使每个仪表板的最小值 return (146, 120)
2。实际上,这告诉应用程序更喜欢 (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. 一个很自然的问题是,"Why not simply set the sizeHint()
to return the minimum size instead of calling minimumSizeHint()
? The best response I have is, "这样不行。" 只设置 sizeHint()
不会像您预期的那样将扩展坞的大小调整到最小。
sizeHint()
和minimumSizeHint()
方法是虚函数。我的猜测是 Qt 有其他我们不知道的功能,它们独立地引用这些方法,要求我们以这种方式定义事物。
3. 如果内容是地图,例如,用户不太可能希望地图是 10px x 10px
。此外,可以合理假设用户不会使用低于 600 x 400
的屏幕分辨率,除非他们使用移动设备。但是,如果您使用 PySide 或 PyQt5 进行移动开发,您应该问自己一些重要的问题,例如 "Why?".
当一个小部件停靠时,我希望它改变方向并且相对于停靠扩展的方式具有最小尺寸。
即
- left/right 停靠 => 最小宽度的从上到下方向
- top/bottom 停靠 => 最小高度从左到右的方向
问题是,只要方向改变,停靠栏就会呈现出看似任意的宽度或高度。停靠时,我找不到将停靠小部件 resize/force 调整为特定大小的方法。我尝试了无数种覆盖 sizeHint
、minimumSizeHint
、调用 adjustSize
和摆弄 sizePolicy
.
如何确保初始停靠尺寸?
我的基本应用程序如下所示:
该应用程序显示主要和次要信息以及相应的控件集。包含主要内容和次要内容的选项卡小部件被设置为中央小部件。容纳各个仪表板中控件的 QStackedWidget 位于停靠栏中。当标签更改时,显示相应的仪表板。下面的 基本应用程序代码.
中给出了此代码困难在于 改变仪表板的方向会扰乱扩展坞的大小。
调整dashboard的方向,我想到了两个比较合理的方案:
- 通过
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 approach.
给出了完整的代码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 个独立的 类.
对于
MyWindow
、PrimaryDashboard
和SecondaryDashboard
分开的原因应该够清楚了吧
对于
MyDock
和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_())
将一个应用程序视为一组 Matryoshka dolls. An inner doll's size dictates that of the subsequent outer ones. Clearly, an inner doll cannot be bigger than the one that contains it! QWidgets 相似建模。
By default, composite widgets which do not provide a size hint will be sized according to the space requirements of their child widgets.
QWidget.sizeHint() 的文档接着说,
The default implementation of QWidget.sizeHint() returns an invalid size if there is no layout for this widget, and returns the layout’s preferred size otherwise.
总而言之,小部件的大小根据布局由内而外调整。
如果您要为 基本应用程序代码 中的每个对象实现 resizeEvent
1,您将请参阅以下尺码序列,
PrimaryDashboard
resizeEventDockContainer
resizeEventMyDock
resizeEvent
这是我们期望的嵌套。首先查询 PrimaryDashboard
的大小,然后是 DockContainer
,然后是 MyDock
。从技术上讲,它一直是小部件。但是,PrimaryDashboard
包含的按钮和标签在大多数情况下应该小于 MainWindow 的 width/height。序列中第一个显着影响码头尺寸的娃娃是 PrimaryDashboard
.
使用 event.size()
检查 resizeEvent
,我们可以看到水平仪表板的合理最小高度是 120
像素,而垂直方向具有合理的最小宽度146
。然后可以将 sizeHint()
设置为 return minimumSizeHint()
并使每个仪表板的最小值 return (146, 120)
2。实际上,这告诉应用程序更喜欢 (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. 一个很自然的问题是,"Why not simply set the sizeHint()
to return the minimum size instead of calling minimumSizeHint()
? The best response I have is, "这样不行。" 只设置 sizeHint()
不会像您预期的那样将扩展坞的大小调整到最小。
sizeHint()
和minimumSizeHint()
方法是虚函数。我的猜测是 Qt 有其他我们不知道的功能,它们独立地引用这些方法,要求我们以这种方式定义事物。
3. 如果内容是地图,例如,用户不太可能希望地图是 10px x 10px
。此外,可以合理假设用户不会使用低于 600 x 400
的屏幕分辨率,除非他们使用移动设备。但是,如果您使用 PySide 或 PyQt5 进行移动开发,您应该问自己一些重要的问题,例如 "Why?".