PyQt5:如何通过 QPushButton 重新停靠浮动的 QDockWidget?

PyQt5: How to Re-Dock back a floated QDockWidget via a QPushButton?

我要使用 QPushButton 将未停靠或浮动 QDockWidget 恢复到初始状态。

from PyQt5 import QtCore, QtGui, QtWidgets

class Mainwindow(object):
    def setupUi(self, window):
        window.setObjectName("window")
        window.resize(309, 148)
        self.centralwidget = QtWidgets.QWidget(window)
        self.centralwidget.setObjectName("centralwidget")
        self.Undock_btn = QtWidgets.QPushButton(self.centralwidget)
        self.Undock_btn.setGeometry(QtCore.QRect(4, 4, 100, 22))

        self.Undock_btn.setStyleSheet("background: rgba(255, 217, 90, 255)\n")
        self.Undock_btn.setObjectName("Undock_btn")
        self.ReDock_btn = QtWidgets.QPushButton(self.centralwidget)
        self.ReDock_btn.setGeometry(QtCore.QRect(110, 4, 96, 22))
        
        self.ReDock_btn.setStyleSheet("background:rgba(9,  17, 188, 109);")
        self.ReDock_btn.setObjectName("ReDock_btn")
        self.dockw = QtWidgets.QDockWidget(self.centralwidget)
        self.dockw.setTitleBarWidget(None)
        self.dockw.setGeometry(QtCore.QRect(4, 34, 200, 110))
        self.dockw.setStyleSheet("background:rgba( 0,188, 0, 29);\n")
        self.dockw.setObjectName("dockw")
        self.dockWidgetContents = QtWidgets.QWidget()
        self.dockWidgetContents.setObjectName("dockWidgetContents")
        self.dockw.setWidget(self.dockWidgetContents)
        window.setCentralWidget(self.centralwidget)
        self.retranslateUi(window)
        QtCore.QMetaObject.connectSlotsByName(window)
        #-----------------------------------------------------------------
        self.Undock_btn.setCheckable(True)
        self.connexions()
    def connexions(self):
        self.Undock_btn.clicked.connect(self.Method_Float_it)
        self.ReDock_btn.clicked.connect(self.Method_BringBack)

    def Method_Float_it(self):
        print("Method_Float_it")
        self.dockw.setFloating(True)

        if  self.dockw.isFloating():
            print("is Floating now...")
            return True
        
    def Method_BringBack(self):
        self.dockw.setFloating(False)
        self.dockw.RestoreState(True)
        
    def retranslateUi(self, window):
        _translate = QtCore.QCoreApplication.translate
        window.setWindowTitle(_translate("window", "GUI"))
        self.Undock_btn.setText(_translate("window", "UnDock Button"))
        self.ReDock_btn.setText(_translate("window", "Bring back button"))
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = QtWidgets.QMainWindow()
    ui = Mainwindow()
    ui.setupUi(window)
    window.show()
    sys.exit(app.exec_())

image of when it's floated after pressed QPushButton

The Problem is: after I undocked the QDockWidget with first button, the second button fails to bring it back to normal (initial state)

“主要”问题是您没有正确地将停靠小部件添加到 window,因为正确的方法是使用 addDockWidget() QMainWindow 的方法。

您实际所做的是将停靠小部件创建为中央小部件的子项,这不是一种有效的方法。

您的第一个函数“有效”只是因为当调用 setFloating(True) 时,QDockWidget 更改其 window 标志 并确保它成为顶级 window.
恢复功能不起作用,因为停靠小部件从未正确添加到主 window,因此它没有参考知道应该“停靠回”的位置。

理论上的解决方案是在创建停靠小部件后添加此行:

    window.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.dockw)

但是,这只是部分解决方案,因为您的代码还有另外两个 主要 问题。

  • 您没有使用布局管理器;每当 任何 调整大小发生在 window 上或内部时,这将成为一个严重的问题,包括试图在 window 的两侧拖动浮动停靠小部件:对于例如,如果 window 不够宽,拖动侧面的停靠小部件将导致部分(或完全)隐藏其按钮;
  • 您正在修改pyuic生成的文件;由于很多原因,这被认为是 不好的 做法,虽然您的程序现在“有效”,但迟早(但很可能更早)您将面临意想不到的行为和对实施的困惑;这些文件仅用于导入,如有关 using Designer;
  • 的指南中所述

还有两个(相对)小问题:

  • 我不知道RestoreState是什么,但它肯定不是QDockWidget的成员,也不是带有大写R的Qt的成员,因为Qt中唯一的restoreState()函数(对于QMainWindow , QSplitter 等)需要一个 QByteArray,而不是一个布尔值;
  • 只有 类 和常量应该有大写的名称,而不是函数(也不是变量);

我修复了你的代码,它现在可以正常工作了。所有变量函数和方法都应该是some_function_namesomeFunctionName,前者称为C风格,后者称为Java风格。此外,您的 class 必须继承自 QMainWindow,并且必须向每个 QWidget 添加一个 QLayout。然后,子窗口小部件被添加到 QLayoutQLayout.addWidget。在代码之后,我在其中添加了一些注释以进行解释。

#!/usr/bin/python3
#-*-coding: utf-8-*-

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class Mainwindow(QtWidgets.QMainWindow):
    def setupUI(self):
        self.setObjectName("window")
        self.resize(309, 148)
        self.centralWid = QtWidgets.QWidget(self) # not naming it centralWidget, because that would override the centralWidget() function of QMainWindow
        self.centralWid.setObjectName("centralwidget")
        self.centralLay = QtWidgets.QHBoxLayout(self.centralWid) # create a layout
        self.centralWid.setLayout(self.centralLay) # and set it on the central widget
        self.setCentralWidget(self.centralWid) # set centralWidget as the centralWidget of the window
        self.undockButton = QtWidgets.QPushButton(self.centralWid)
        self.undockButton.setStyleSheet("background: rgba(255, 217, 90, 255);")
        self.undockButton.setObjectName("undockbutton")
        self.centralLay.addWidget(self.undockButton)
        self.redockButton = QtWidgets.QPushButton(self.centralWid)
        self.redockButton.setStyleSheet("background: rgba(9,  17, 188, 109);")
        self.redockButton.setObjectName("redockButton")
        self.centralLay.addWidget(self.redockButton)
        self.dock = QtWidgets.QDockWidget("Dock title", self.centralWid)
        self.dock.setTitleBarWidget(None)
        self.dock.setStyleSheet("background: rgba( 0,188, 0, 29);")
        self.dock.setObjectName("dock")
        self.dockContents = QtWidgets.QWidget()
        self.dockContents.setObjectName("dockcontents")
        self.dock.setWidget(self.dockContents)
        self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.dock)
        self.translateUI()
        QtCore.QMetaObject.connectSlotsByName(self)
        self.connectSlots()
    
    def connectSlots(self):
        self.undockButton.clicked.connect(self.undock)
        self.redockButton.clicked.connect(self.redock)
    
    def undock(self):
        self.dock.setFloating(True)
    
    def redock(self):
        if self.dock.isFloating():
            self.dock.setFloating(False)
    
    def translateUI(self):
        _translate = QtCore.QCoreApplication.translate
        self.setWindowTitle(_translate("window", "Window"))
        self.undockButton.setText(_translate("window", "Undock"))
        self.redockButton.setText(_translate("window", "Redock"))

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    mainWin = Mainwindow()
    mainWin.setupUI()
    mainWin.show()
    sys.exit(app.exec_())