[英]Qt: How does a dock widget get its initial size?
將小部件停靠后,我希望它可以改變方向,並且相對於停靠件的擴展方式具有最小的尺寸。
那是,
問題在於,無論方向如何改變,擴展塢都會呈現出看似任意的寬度或高度。 我無法找到一種在停靠時將停靠小部件調整大小/強制使其達到特定大小的方法。 我嘗試了無數種方法來覆蓋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個單獨的類。
對於
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娃娃 。 內部玩偶的大小決定了隨后的外部玩偶的大小。 顯然,一個內在的洋娃娃不能大於其中的一個! QWidget的建模類似。
默認情況下,不提供大小提示的復合窗口小部件將根據其子窗口小部件的空間要求來調整大小。
QWidget.sizeHint()的文檔繼續說,
如果此小部件沒有布局,則QWidget.sizeHint()的默認實現返回無效的大小,否則,返回布局的首選大小。
總而言之, 小部件的大小是根據布局從內而外的。
如果要為基本應用程序代碼中的每個對象實現resizeEvent
1 ,則會看到以下大小調整順序,
PrimaryDashboard
resizeEvent DockContainer
resizeEvent MyDock
resizeEvent 這是我們期望的嵌套。 首先查詢PrimaryDashboard
的大小,然后查詢DockContainer
,然后查詢MyDock
。 從技術上講,它一直是小部件。 但是, PrimaryDashboard
包含的按鈕和標簽在大多數情況下應小於MainWindow的寬度/高度。 該序列中第一個會顯着影響碼頭尺寸的玩偶是PrimaryDashboard
。
使用event.size()
檢查resizeEvent
,我們可以看到水平儀表板的合理最小高度為120
像素,而垂直方向的合理最小寬度為146
。 然后,可以將sizeHint()
設置為返回minimumSizeHint()
,並使最小返回每個儀表板2的(146, 120)
。 實際上,這告訴應用程序首選最小大小為(146, 120)
同時總體上仍允許調整大小。
def sizeHint(self):
return self.minimumSizeHint()
def minimumSizeHint(self):
return QtCore.QSize(146, 120)
當然,使用固定大小可能會很危險,因為絕對值是不容忍的,並且根據定義不靈活。 但是,內容可能具有自然的最小大小3 。 我們可以簡單地在整個應用程序上設置setMinimumSize()
,以使其大小不能小於我們的minimumSizeHint()
。
要更改dock小部件內容的方向,我們可以使用dockLocationChanged
信號。 我們還可以使代碼比問題中的代碼更加整潔。 可以在MyWindow
的實例級別連接它,而不是在Dock小部件中連接信號。 實際上,根本不需要定義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開發移動設備,那么您應該問自己一些重要問題,例如“為什么?” 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.