You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免带自定义标题栏的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 17:06:04