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

此生再无相见时 提交于 2021-02-11 14:10:51

问题


I which to bring back to initial state an Undocked or floated QDockWidget with a QPushButton.

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)


回答1:


I fixed your code, it is working now as expected. All variables functions and methods should be either some_function_name or someFunctionName, the former is called C style and the latter Java style. Further, your class has to inherit from QMainWindow, and a QLayout has to be added to every QWidget. Then, the subwidgets are added to the QLayout with QLayout.addWidget. Following the code, where i added some comments for explanation.

#!/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_())



回答2:


The "main" issue is that you're not correctly adding the dock widget to the window, as the right way to do it is using the addDockWidget() method of QMainWindow.

What you actually did was to create the dock widget as a child of the central widget, and that's not a valid approach.

Your first function "works" just because when setFloating(True) is called, QDockWidget changes its window flags and ensures that it becomes a top level window.
The restore function doesn't work because the dock widget has never been correctly added to the main window, so it has no reference to know where it should "dock back".

The solution, theoretically, would be to add this line after the dock widget is created:

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

But, it would be only a partial solution, as your code has two other major problems.

  • You are not using layout managers; this will become a serious problem whenever any resizing happens to or inside the window, including trying to drag a floating dock widget on the sides of the window: for instance, if the window is not wide enough, dragging the dock widget on the side will result in partially (or completely) hiding its buttons;
  • You are modifying a file generated by pyuic; this is considered bad practice for lots of reasons, and while your program "works" right now, sooner or later (but most probably sooner) you'll face unexpected behavior and confusion about the implementation; those files are only intended to be imported, as explained in the guidelines about using Designer;

There are two other (relatively) minor issues:

  • I don't know what RestoreState is, but it certainly is not a member of QDockWidget, nor of Qt with the uppercase R, as the only restoreState() functions in Qt (for QMainWindow, QSplitter, etc) require a QByteArray, not a bool;
  • only classes and constants should have capitalized names, not functions (nor variables);


来源:https://stackoverflow.com/questions/65795241/pyqt5-how-to-re-dock-back-a-floated-qdockwidget-via-a-qpushbutton

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!