简体   繁体   English

QDockWidget 不再停靠在 pyQt5 中双击

[英]QDockWidget does not dock any longer on doubleclick in pyQt5

I had a solution in pyQt4 to undock-dock a tab from/to a QTabWidget by using QDockWidgets and the code below.我在 pyQt4 中有一个解决方案,可以使用 QDockWidgets 和下面的代码将选项卡从/到 QTabWidget 取消停靠。 After floating a tab, the re-docking was obtained by double clicking the titlebar of the QDockWidget.浮动一个选项卡后,双击QDockWidget的标题栏获得重新停靠。 But it does not work any more in pyQt5 (the double-click does not seem to trigger a topLevelChanged event).但它在 pyQt5 中不再起作用(双击似乎没有触发 topLevelChanged 事件)。

Why?为什么?

How to fix it and get back the proper re-docking behavior?如何修复它并恢复正确的重新对接行为?

Where is this behavior change explained in the documentation?文档中在哪里解释了这种行为变化?

Thanks for your help.谢谢你的帮助。

import sys
try:
      from PyQt5.QtCore import QEvent
      from PyQt5.QtWidgets import QApplication, QMainWindow, QDockWidget, QTabWidget, QLabel
except:
      from PyQt4.QtCore import QEvent
      from PyQt4.QtGui  import QApplication, QMainWindow, QDockWidget, QTabWidget, QLabel


class DockToTabWidget(QDockWidget):

    def __init__(self, title, parent=0):
        QDockWidget.__init__(self, title, parent)
        self._title = title
        self.topLevelChanged.connect(self.dockToTab)

    def dockToTab(self):
        if not self.isFloating():
            self.parent().addTab(self.widget(), self._title)
            self.close()
            del self


class TabWidgetWithUndocking(QTabWidget):

    def __init__(self):
        super(TabWidgetWithUndocking, self).__init__()
        self.tabBar().installEventFilter(self)

    def eventFilter(self, object, event):
        if object == self.tabBar():
            if event.type() == QEvent.MouseButtonDblClick:
                pos = event.pos()
                tabIndex = object.tabAt(pos)
                title = self.tabText(tabIndex)
                widget = self.widget(tabIndex)
                self.removeTab(tabIndex)
                dockWidget = DockToTabWidget(title, parent=self)
                dockWidget.setFeatures(QDockWidget.AllDockWidgetFeatures)
                dockWidget.setWidget(widget)
                dockWidget.setFloating(True)
                dockWidget.move(self.mapToGlobal(pos))
                dockWidget.show()
                return True
            return False

    def tabClose(self, index):
        self.removeTab(index)

qApp = QApplication([])
qApp.setQuitOnLastWindowClosed(True)
sys.excepthook = sys.__excepthook__
main = QMainWindow()
main.setWindowTitle('Main')
twu = TabWidgetWithUndocking()
for i in range(2):
    twu.addTab(QLabel('tab %i' % i), 'tab %i' % i)
main.setCentralWidget(twu)
main.show()
qApp.exec_()

I don't remember the implementation of Qt4, but in Qt5 the double click always check that the dock widget was actually set on a valid QMainWindow parent before trying to toggle its top level state.我不记得 Qt4 的实现,但是在 Qt5 中,双击总是检查停靠小部件是否实际设置在有效的 QMainWindow 父级上,然后再尝试切换其顶级 state。

You're not adding the QDockWidget, nor you're using a QMainWindow, so the signal is never emitted.您没有添加 QDockWidget,也没有使用 QMainWindow,因此永远不会发出信号。 This also makes it problematic in some cases as it prevent proper handling of mouse events to allow dragging of the dock widget.这在某些情况下也会造成问题,因为它会阻止正确处理鼠标事件以允许拖动停靠小部件。

The only solution is to properly check for double clicks, and that can only happen by overriding event() , since it's internally managed by the dock widget in case a "title bar widget" is set, and mouseDoubleClickEvent will be never called.唯一的解决方案是正确检查双击,这只能通过覆盖event()来实现,因为它是由停靠小部件在内部管理的,以防设置“标题栏小部件”,并且永远不会调用mouseDoubleClickEvent

The following edit of the original code should work fine also for PyQt4.原始代码的以下编辑也适用于 PyQt4。

class DockToTabWidget(QDockWidget):
    attachRequested = pyqtSignal(QWidget, str)
    def __init__(self, title, widget):
        QDockWidget.__init__(self, title, widget.window())
        self.setFeatures(QDockWidget.AllDockWidgetFeatures)
        self.setWidget(widget)
        self.setFloating(True)

        floatButton = self.findChild(QAbstractButton, 'qt_dockwidget_floatbutton')
        floatButton.clicked.connect(self.attach)

    def attach(self):
        self.attachRequested.emit(self.widget(), self.windowTitle())
        self.deleteLater()

    def event(self, event):
        if (
            event.type() == event.MouseButtonDblClick 
            and event.button() == Qt.LeftButton
        ):
            opt = QStyleOptionDockWidget()
            self.initStyleOption(opt)
            if event.pos() in opt.rect:
                self.attach()
                return True
        return super().event(event)


class TabWidgetWithUndocking(QTabWidget):
    def __init__(self):
        super(TabWidgetWithUndocking, self).__init__()
        self.tabBar().installEventFilter(self)

    def eventFilter(self, obj, event):
        if obj == self.tabBar():
            if event.type() == QEvent.MouseButtonDblClick:
                pos = event.pos()
                tabIndex = obj.tabAt(pos)
                title = self.tabText(tabIndex)
                widget = self.widget(tabIndex)
                self.removeTab(tabIndex)
                dockWidget = DockToTabWidget(title, widget)
                dockWidget.attachRequested.connect(self.attachDock)
                dockWidget.move(self.mapToGlobal(pos))
                dockWidget.show()
                return True
            return False

    def attachDock(self, widget, title):
        self.setCurrentIndex(self.addTab(widget, title))

Note: object is a built-in type in Python: while not forbidden, you should not use it as a variable name.注意: object是 Python 中的内置类型:虽然没有被禁止,但不应将其用作变量名。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM