首页 > 其他分享 >记录 PyQt6 / PySide 6 自定义边框窗口的 Bug 及可能可行的解决方案:窗口抖动和添加 DWM 环绕阴影的大致原理

记录 PyQt6 / PySide 6 自定义边框窗口的 Bug 及可能可行的解决方案:窗口抖动和添加 DWM 环绕阴影的大致原理

时间:2024-09-06 20:54:54浏览次数:12  
标签:窗口 自定义 PySide 抖动 边框 调整 阴影 DWM

前言:

本篇文章将要讨论我在前不久发表的关于 PyQt6 / PySide6 自定义边框窗口代码及内容中的问题:

(终)PyQt6 / PySide 6 + Pywin32 自定义标题栏窗口 + 完全还原 Windows 原生窗口边框特效_pyside6 win32 无边框窗口-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/2402_84665876/article/details/141535937?spm=1001.2014.3001.5501

问题:

1. 窗口右边框和下边框的抖动问题:

这算是 FramelessWindowHint 的通病,在 UI 基于 Qt 的应用程序中,大多数去掉系统边框的应用都会发生在拖拽窗口顶部,右上方或左侧调整大小时窗口右侧和底部发生抖动的情况,比如 123 云盘的 Windows 客户端,OpenShot 和 QtFramelessWindow 第三方库,这是我推测的调整大小的信号传输步骤:

 无论如何,当你调整一个自定义窗口的大小时,NativeEvent 和 resizeEvent 事件在同时进行,但由于两个事件不是平级关系,NativeEvent 将比 resizeEvent 先一步收到系统通知,这导致窗口调整大小时会出现延迟现象。

2.添加 DWM 阴影不等于窗口绘制:

在【前言】所提到的文章中,我曾备注过这样一段话:

注:使用此方法给窗口添加 DWM 阴影实际上就是新建了一个空白的带阴影的窗口,再将自己创建的窗口绘制到该窗口上(覆盖原有标题栏),所以修改后的窗口拖动和改变大小时占用的 CPU 和内存资源大概是未添加阴影时的 2~3 倍甚至更多,当系统刚启动时或外部应用占用的 CPU 和内存资源过多时,拖动和缩放窗口会出现明显的卡顿。

 其实这种说法是错误的,窗口图形的 ”边界“ 和系统给窗口的 ”边框“ 并不是一回事,添加阴影的原理是利用 Windows 的桌面窗口管理器 (DWM) 来扩展窗口的框架区域,使得阴影效果能够显示在窗口周围。这种效果是 DWM 在窗口外框绘制阴影,而不是实际改变窗口本身的边界或形状。

问题原因和可能可行的解决方案:

* 这篇文章并没有给出解决问题后的代码,因为我也正在尝试用此方法解决问题。

** 答案来源:ChatGPT 4

原问题: 为什么给窗口设置 FramelessWindowHint 后,调整大小时窗口的右侧或底部边界会发生“抖动”?有没有办法解决这个问题?
当设置 FramelessWindowHint 时,Qt 窗口会失去原生的 Windows 标题栏和边框。此时,Qt 本身负责处理窗口的边界调整和绘制。这可能导致调整大小时的抖动问题,尤其是在应用 DwmExtendFrameIntoClientArea 时,因为这会修改窗口的视觉区域。
  1. 抖动的原因:抖动问题通常由以下几个原因导致:

    • 不一致的窗口区域更新:DWM 扩展区域和 Qt 本身的窗口边界之间可能出现同步问题。DWM 更新边界时,窗口内容有时可能会滞后于实际的框架阴影扩展,导致“抖动”或闪烁。
    • 多线程或异步绘制冲突:窗口边界调整和重新绘制过程中的线程不安全操作或异步更新可能导致这种抖动。
  2. 解决方法

    • 同步更新:确保 DwmExtendFrameIntoClientArea 和窗口大小调整同步。你可以在大小调整事件中调用 DwmExtendFrameIntoClientArea 来重新设置边界。
    • 使用 Win32 API 的原生调整大小:使用 Windows API 的原生窗口调整大小处理,而不是 Qt 的事件。你可以捕获窗口的 WM_NCHITTEST 消息来处理拖动和调整大小区域,从而保留原生行为。
    • 避免频繁调用 DWM 扩展:在窗口大小调整过程中,避免频繁调用 DwmExtendFrameIntoClientArea。可以尝试只在初始化时调用一次。

标签:窗口,自定义,PySide,抖动,边框,调整,阴影,DWM
From: https://blog.csdn.net/2402_84665876/article/details/141831375

相关文章

  • 自定义排序规则
    Comparator接口的使用:TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则。方式一让自定义的类实现Comparable接口,重写里面的compareTo方法来指定比较规则。​//属性类publicclassStudentimplementsComparable<Student>{  privat......
  • VUE框架Vue3使用自定义事件的方式传递数据------VUE框架
    <template><!--给User绑定事件--><!--带参数的方法不要有括号,否则识别不到,我不懂原理...--><User@event1="showInfo"></User></template><script>importUserfrom"./components/User.vue"exportdefault{......
  • C语言-第六章-加餐:其他自定义类型
    传送门:C语言-第六章:结构体目录第一节:位段    1-1.位段是什么    1-2.位段的大小第二节:联合体    2-1.联合体是什么    2-2联合体的大小第三节:枚举类型    3-1.枚举是什么第四节:结构体中的柔性数组    4-1.柔性数组......
  • 功能发布-自定义SQL查询
    引言本期主要为大家介绍ClkLog九月上线的新功能-自定义SQL查询。什么是自定义SQL查询?自定义SQL查询是指根据具体的应用场景和需求,由开发者或数据库管理员自行编写的SQL(StructuredQueryLanguage,结构化查询语言)语句。自定义SQL查询有什么作用?以精确的方式从数据库中检索数据。在Cl......
  • <Rust>egui学习之小部件(九):如何在窗口中添加下拉列表combobox部件?
    前言本专栏是关于Rust的GUI库egui的部件讲解及应用实例分析,主要讲解egui的源代码、部件属性、如何应用。环境配置系统:windows平台:visualstudiocode语言:rust库:egui、eframe概述本文是本专栏的第九篇博文,主要讲述下拉列表部件combobox的使用。事实上,类似于iced,egui......
  • vue自定义指令
    现象:可使用自定义指令v-per实现:index.js文件importperfrom"./per"importper2from"./per2"//批量注册指令(现在就一个permission)constdirectives={per,per2}//注册的一般写法,循环遍历directives,通过vue.directive注册exportdefault{inst......
  • vxe-table 自定义单元格样式
    <template><div><vxe-tableborderclass="mytable-style":header-cell-class-name="headerCellClassName":row-class-name="rowClassName":cell-class-name="cellClassName&quo......
  • django 自定义中间件
    概述中间件是Django请求/响应处理的框架,用于全局改变Django的请求输入或响应输出。在请求阶段,在调用视图之前,Django按照定义settings.MIDDLEWARE的顺序应用中间件MIDDLEWARE,自顶向下。中间件的结构:classSimpleMiddleware:def__init__(self,get_response):......
  • 讲一下Android Lint工具使用,以及如何自定义lint规则
    Androidlint是一个静态代码分析工具,用于在Android项目中检测潜在的问题和错误。它可以帮助开发者提高代码质量、发现性能问题、确保兼容性以及遵循最佳实践。一、Androidlint的主要功能包括:代码风格检查:确保代码遵循一致的风格规范,如命名约定、缩进等。潜在错误检测:识......
  • Goolge earth studio 高阶3——自定义缓动曲线
    EarthStudio还提供了更高级的选项,比如“自定义”缓动曲线。自定义曲线允许你手动调整曲线,以获得所需的确切时间和流畅度。1、单击我们还要修改的属性,就能在时间框中看到一个表示变化速度的图形编辑器,黄色曲线就是随时间变化的设置值。黄色曲线上的圆圈为关键帧的值,可以拖动......