【Python+Qt】自定义QDockWidget窗口弹出时最大化功能

项目场景:

使用 PySide6(PyQt 应该类似) 开发 GUI 界面,主界面分为很多个板块(使用 QDockWidget),想实现每个 QDockWidget 在 floating 弹出时,默认最大化 floating 窗口,便于用户操作。

但是 QDockWidget 默认只有 close、float 两个按钮,且单击 float 按钮或双击标题栏,QDockWidget 只会简单地弹出,标题栏按钮均消失不见,且无法最大化。

希望增加 QDockWidget 功能,实现 floating 弹出后,QDockwidget 标题栏有最大化、最小化按钮,且支持自动最大化窗口。


解决方案(初)

参考博客 Qt实战10.支持最小化和最大化的QDockWidget,主题思路为创建一个新类继承 QDockWidget 并重写方法,即

def event(self, event: QEvent):
    if event.type() == QEvent.ZOrderChange:
        if self.isFloating():
            w = QWidget(self)
            self.setMaximumSize(w.maximumSize())
            self.setWindowFlags(Qt.Dialog | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint)
            self.show()
    return super(DockWidget, self).event(event)

为了防止用户手残将 QDockWidget 窗口关掉没法恢复,self.setWindowFlags 不添加 Qt.WindowCloseButtonHint,取消关闭按钮功能。

但该方法仍然存在不足:

  1. 若要恢复窗口,只能双击标题栏,点击最小化按钮会使 QDockWidget 收起。希望点击最小化后实现恢复窗口功能。
  2. 窗口 floating 弹出时,只能由用户手动点击最大化按钮实现窗口最大化,无法自动实现最大化功能。

解决方案(终)

经过一顿修改,完整代码如下:

from PySide6.QtCore import Qt, QEvent
from PySide6.QtWidgets import QWidget, QDockWidget


class DockWidget(QDockWidget):
    def __init__(self, parent=None, need_maximize=False):
        super(DockWidget, self).__init__(parent)
        self.window_flags = self.windowFlags()
        self.maximum_size = self.maximumSize()
        self.need_maximize = need_maximize
        self.show_flag = False

    def event(self, event: QEvent):
        if event.type() == QEvent.ZOrderChange:
            if self.isFloating():
                w = QWidget(self)
                self.setMaximumSize(w.maximumSize())
                self.setWindowFlags(Qt.Dialog | Qt.WindowMinimizeButtonHint)
                self.show()
                self.show_flag = True
        return super(DockWidget, self).event(event)

    def changeEvent(self, event: QEvent):
        if event.type() == QEvent.WindowStateChange:
            if self.isMinimized():
                self.setFloating(False)
                self.setMaximumSize(self.maximum_size)
                self.setWindowFlags(self.window_flags)
                self.setWindowState(Qt.WindowNoState)
                self.show()
        if self.need_maximize and (self.windowState() & Qt.WindowNoState == Qt.WindowNoState) and self.show_flag:
            self.showMaximized()
            self.show_flag = False
        return super(DockWidget, self).changeEvent(event)


class DockMaximizeWidget(DockWidget):
    def __init__(self, parent=None):
        super(DockMaximizeWidget, self).__init__(parent, need_maximize=True)

逐段代码分析

  1. 最小化按钮功能更改:changeEvent

    def changeEvent(self, event: QEvent):
            if event.type() == QEvent.WindowStateChange:
                if self.isMinimized():
                    self.setFloating(False)
                    self.setMaximumSize(self.maximum_size)
                    self.setWindowFlags(self.window_flags)
                    self.setWindowState(Qt.WindowNoState)
                    self.show()
            ...
    
    • 使用 event.type() == QEvent.WindowStateChangeself.isMinimized() 检测窗口是否被最小化;
    • self.setFloating(False) 取消窗口浮动状态;
    • self.setMaximumSize(self.maximum_size) self.setWindowFlags(self.window_flags) 用于恢复窗口默认属性;
    • self.setWindowState(Qt.WindowNoState) 将 WindowState 状态清空,防止后续检测不到最小化行为;
    • self.show() 显示 floating 窗口。

    上述代码用于更改最小化按钮的行为,实现 docker 窗口恢复。


    ...
    if self.need_maximize and (self.windowState() & Qt.WindowNoState == Qt.WindowNoState) and self.show_flag:
        self.showMaximized()
        self.show_flag = False
        return super(DockWidget, self).changeEvent(event)
    ...
    
    • self.need_maximize 控制窗口是否要默认最大化;
    • self.windowState() & Qt.WindowNoState == Qt.WindowNoState 确保窗口处于默认状态;
    • self.show_flag 记录窗口的最大化行为;
    • self.showMaximized() 最大化窗口。

    重要:不能在 event 事件中执行 self.showMaximized(),否则最大化无效。

    上述代码效果为,当 docker 窗口从 non-floating 状态变成 floating 弹出状态时,自动最大化窗口。


    ...
    return super(DockWidget, self).changeEvent(event)
    

    一定要调用super(DockWidget, self).changeEvent(event),否则会出现 docker 标题消失等迷之 bug。

  2. 自定义弹出窗口:event

    def event(self, event: QEvent):
       if event.type() == QEvent.ZOrderChange:
           if self.isFloating():
               w = QWidget(self)
               self.setMaximumSize(w.maximumSize())
               self.setWindowFlags(Qt.Dialog | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint)
               self.show()
               self.show_flag = True
       return super(DockWidget, self).event(event)
    
    • 增加 self.show_flag = True 记录窗口从 non-floating 状态变成 floating 弹出状态;
    • 其余与前文博客内容相同。
  3. 类初始化:__init__

    def __init__(self, parent=None, need_maximize=False):
        super(DockWidget, self).__init__(parent)
        self.window_flags = self.windowFlags()
        self.maximum_size = self.maximumSize()
        self.need_maximize = need_maximize
        self.show_flag = False
    
    • need_maximize 设置弹出时窗口是否默认最大化;
    • window_flags maximum_size 记录窗口默认状态;
    • self.show_flag = False 清空窗口弹出记录。
  4. 再包装一下

    class DockMaximizeWidget(DockWidget):
        def __init__(self, parent=None):
            super(DockMaximizeWidget, self).__init__(parent, need_maximize=True)
    

    DockWidget 支持最大、最小化按钮,DockMaximizeWidget 窗口弹出时默认最大化,在 QtDesigner 中可以直接对 QDockWidget 进行类提升,使用方便。


站在巨人的肩膀上+自己动手丰衣足食,完美解决问题,mark 一下。图懒得放了。

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值