如何避免带自定义标题栏的QDockWidget组合浮动时崩溃?
自定义QDockWidget的稳定性与功能优化问题
我扩展了QDockWidget,实现以下功能:
- 使用自定义标题栏完成QDockWidget的浮动与关闭(完整项目还有更多附加操作)
- 标题栏显示QDockWidget标题,但标签化且标签已显示标题时除外
下方最小示例基本实现需求,但存在多个问题,所有问题均与主窗口外合并两个浮动dock的场景相关:合并后会生成一个作为两个dock新父控件的QWidget,后续对这些dock进行停靠操作会导致应用不稳定甚至崩溃。
主要崩溃问题
当两个dock在主窗口外合并后,通过自定义浮动按钮将两者都设为浮动状态时,会残留一个空窗口(推测是仍作为dock父控件的QWidget)。将这些dock合并到第三个窗口或拖回主窗口时,应用会崩溃,退出码包括-1073741819、-1073740791等;部分场景下dock会消失或只能停靠回原外部窗口,完整项目中还会出现dock粘在鼠标上无法停靠的情况。是否有办法实现稳定化?比如通过显式移除该QWidget并将父控件重置回主窗口的workaround?
其他非崩溃相关问题
- 标题栏更新并非总能可靠触发,例如直接从主窗口拖到外部窗口时,是否有可捕获该变化的信号?
- 尽管浮动QWidget是MainWindow的子控件,但主窗口外的标签化dock调用
tabifiedDockWidgets始终返回0。 - 如何移除包含两个dock的QWidget的关闭按钮?自定义关闭按钮已处理关闭逻辑,无需原生按钮,尝试
dock.parent().setWindowFlag(QtCore.Qt.WindowType.WindowCloseButtonHint, False)未成功。
最小复现代码
import sys from PyQt6.QtWidgets import QApplication,QMainWindow,QDockWidget,QWidget,QVBoxLayout,QPushButton,QToolBar,QLabel from PyQt6.QtCore import Qt,QObject,pyqtSignal from PyQt6.QtGui import QAction from threading import Timer, current_thread class MainWindow(QMainWindow): updateTitleBarSignal = pyqtSignal() def __init__(self): super().__init__() self.setGeometry(100, 100, 400, 400) self.updateTitleBarSignal.connect(self.updateTitleBar) self.setDockOptions(QMainWindow.DockOption.AllowTabbedDocks | QMainWindow.DockOption.AllowNestedDocks | QMainWindow.DockOption.GroupedDragging |QMainWindow.DockOption.AnimatedDocks) self.td = BetterDockWidget(self,'topdock') self.ld = BetterDockWidget(self,'leftdock') self.rd = BetterDockWidget(self,'rightdock') self.rd2 = BetterDockWidget(self,'rightdock2') self.addDockWidget(Qt.DockWidgetArea.TopDockWidgetArea,self.td) self.splitDockWidget(self.td,self.ld,Qt.Orientation.Vertical) self.splitDockWidget(self.ld,self.rd,Qt.Orientation.Horizontal) self.tabifyDockWidget(self.rd,self.rd2) self.updateTitleBarButton = QPushButton('updateTitleBars') self.updateTitleBarButton.clicked.connect(self.updateTitleBarDelayed) self.setCentralWidget(self.updateTitleBarButton) self.updateTitleBarDelayed() def updateTitleBarDelayed(self): Timer(.5, self.updateTitleBarSignal.emit).start() def updateTitleBar(self): for d in [self.td,self.ld,self.rd,self.rd2]: d.updateTitleBar() class BetterDockWidget(QDockWidget): def __init__(self, mainWindow, title): super().__init__(title) self.mainWindow = mainWindow self.title = title # in the real application all docks should update if one dock changes. for debugging only update the current titleBar (see below) might be sufficient self.topLevelChanged.connect(self.mainWindow.updateTitleBar) self.dockLocationChanged.connect(self.mainWindow.updateTitleBar) # if I only update the current titleBar, the error still occurs but slightly less frequently. changes of the tabified state of other docks are not handled as intended # self.topLevelChanged.connect(self.updateTitleBar) # self.dockLocationChanged.connect(self.updateTitleBar) self.setFeatures(QDockWidget.DockWidgetFeature.DockWidgetMovable | QDockWidget.DockWidgetFeature.DockWidgetFloatable) # | QDockWidget.DockWidgetFeature.DockWidgetClosable lay = QVBoxLayout() self.titleBar = QToolBar() self.titleBar.setStyleSheet('background:lightgreen') self.titleLabel = QLabel(title) self.titleBar.addWidget(self.titleLabel) self.floatAction = QAction('float') self.floatAction.triggered.connect(lambda : self.setFloating(not self.isFloating())) self.titleBar.addAction(self.floatAction) self.closeAction = QAction('close') self.closeAction.triggered.connect(self.deleteLater) self.titleBar.addAction(self.closeAction) lay.addWidget(self.titleBar) self.statusLabel = QLabel() lay.addWidget(self.statusLabel) self.mainWidget = QWidget() self.mainWidget.setLayout(lay) self.setWidget(self.mainWidget) self.setTitleBarWidget(self.titleBar) def updateTitleBar(self): if self.isFloating(): self.titleLabel.setText(self.title) else: self.titleLabel.setText(self.title if not isinstance(self.parent(), QMainWindow) or len(self.parent().tabifiedDockWidgets(self)) == 0 else '') # show dock status for debugging self.statusLabel.setText(f"floating: {self.isFloating()}\nparent: {self.parent()}\nthread: {current_thread()}\ntabified: {len(self.mainWindow.tabifiedDockWidgets(self))}") if __name__ == '__main__': app = QApplication(sys.argv) mainWindow = MainWindow() mainWindow.show() sys.exit(app.exec())
内容的提问来源于stack exchange,提问作者dukeeloo
相关产品推荐
相关产品推荐

