大秦论书 發表於 2025-10-19 20:07:00

使用PySide6/PyQt6实现自定义窗口布局,实现类似FluentWindow效果

<p>现在在很多项目中,会比较喜欢FluentWindow效果,这种左侧类似于图标菜单或者树形结构的,右侧是是动态窗体或者组件的展示方式,一般不是多文档布局,每次只是打开当前的模块页面,类似于堆叠页面卡片,每次展示最顶端的那个卡片界面。本篇随笔综合介绍一下FluentWindow效果界面的各种展示方式,然后分析页面的内容组成方式,针对性的使用PySide6/PyQt6实现自定义窗口布局的效果。</p>
<h3>1、FluentWindow效果界面介绍</h3>
<p>在我们的WPF开发框架中,界面布局UI基于lepoco/wpfui(https://github.com/lepoco/wpfui),它的布局也类似于这个FluentWindow的风格,如下所示。</p>
<p><img src="https://img2023.cnblogs.com/blog/8867/202310/8867-20231023151036483-210081765.png" alt="" width="961" height="631" class="medium-zoom-image" loading="lazy"></p>
<p>以及一些微软的WPF应用界面(Fluent 主题&nbsp;https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/whats-new/net90),也是类似如此的。</p>
<p>或者类似&nbsp;WinUI 3 Gallery(https://github.com/microsoft/WinUI-Gallery)也是类似的主题风格。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251015204640546-249507067.png" alt="image" width="933" height="554" loading="lazy"></p>
<p>以及WPF界面项目 <span style="color: rgba(255, 0, 0, 1)"><strong>lepoco/wpfu</strong></span>i(https://github.com/lepoco/wpfui)<br><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251015205627743-2094931962.png" alt="image" width="941" height="525" loading="lazy"></p>
<p>&nbsp;本文主要是针对Python开发领域,对使用PySide6/PyQt6实现自定义窗口布局的探讨,因此也注意<span class="sidebar-brand-text">PyQt-Fluent-Widgets</span>&nbsp;(https://github.com/zhiyiYo/PyQt-Fluent-Widgets)这个界面组件的实现效果,非常不错,因此对它的实现方式和组合界面的方式进行了一定的研究学习。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251015211121862-1876841957.png" alt="image" width="594" height="482" loading="lazy"></p>
<h3>&nbsp;2、界面布局的分析</h3>
<p>在对这些界面大致了解后,心里希望模拟他们的实现方式,构造一个类似的自定义窗口布局,其中参考上面组件的作者的图示进行分析下。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251015211413568-718118416.png" alt="image" width="705" height="496" loading="lazy"></p>
<p>左侧的导航栏部分,分为了上中下三个部分,其中导航的滚动布局部分,主要就是用来放置一些比较长内容,如列表或者树控件等内容的。</p>
<p>而右侧的内容区域,主要使用QStackWidget的堆叠式组件,类似于卡片集合,每次显示最顶部的一张。</p>
<p>为了使得标题栏和整个窗体的样式一致化,我们需采用无边框的窗口处理,这个可以采用&nbsp;PySideSix-Frameless-Window(<span class="text-normal">zhiyiYo /&nbsp;PyQt-Frameless-Window</span>),或者参考项目<span class="AppHeader-context-item-label " data-target="context-region-crumb.labelElement">yjg30737</span><span class="AppHeader-context-item-label " data-target="context-region-crumb.labelElement">pyqt-frameless-window</span>(https://github.com/yjg30737/pyqt-frameless-window),两者都可以。</p>
<p>为了更好的对标题栏进行扩展管理,我参考后者项目进行了修改,并增加了对MacOS和Linux的效果支持(<span class="AppHeader-context-item-label " data-target="context-region-crumb.labelElement">yjg30737</span><span class="AppHeader-context-item-label " data-target="context-region-crumb.labelElement">pyqt-frameless-window</span>&nbsp;只有Windows实现,没有MacOS等效果) 。</p>
<p><strong>自定义按钮组件:</strong></p>
<p>界面了无边框窗口的实现后,我们来看看左侧导航栏的实现,首先我们需要把左侧拆分为一个按钮条,其中自定义按钮组件,需要符合下面几个效果,里面包含:</p>
<ul>
<li data-start="31" data-end="62">
<p data-start="34" data-end="62"><strong data-start="34" data-end="60">一个 QLabel 作为背景线条(选中标志)</strong></p>

</li>
<li data-start="63" data-end="110">
<p data-start="66" data-end="110"><strong data-start="66" data-end="108">一个图标(QLabel/QPushButton/QToolButton 等)</strong></p>

</li>
<li data-start="111" data-end="129">
<p data-start="114" data-end="129"><strong data-start="114" data-end="127">选中时:背景色变浅</strong></p>

</li>
<li data-start="130" data-end="148">
<p data-start="133" data-end="148"><strong data-start="133" data-end="146">未选中时:恢复正常</strong></p>

</li>
<li data-start="149" data-end="179">
<p data-start="152" data-end="179"><strong data-start="152" data-end="177">鼠标悬停时:有特殊效果(hover 效果)</strong></p>

</li>

</ul>
<p>在 PySide6 里,我们可以通过自定义 QWidget 来实现。其中整个按钮组为单选组(像单选按钮一样,点击一个自动取消其他的选中,或者叫做<strong data-start="14" data-end="23">互斥选择组</strong>) 管理类,从而组合上面所说的自定义按钮组件。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251015214650261-77729077.png" alt="image" loading="lazy"></p>
<p>&nbsp;有了上面的自定义按钮组件(MySelectableItem)和&nbsp;<em>互斥选择组组件(</em>MySelectableGroup<em id="__mceDel">),</em>我们就可以简单完成了导航按钮栏目的设计了,类似下面的效果,实现选择、悬停、移动鼠标进入的样式不同变化<em id="__mceDel">。</em></p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251015215017929-1379558863.png" alt="image" width="112" height="278" loading="lazy"></p>
<p>&nbsp;</p>
<p><strong>内容区组件:</strong></p>
<p data-start="10" data-end="79"><strong><code data-start="10" data-end="26">QStackedWidget</code> </strong>是 Qt 里专门用来管理 <strong data-start="40" data-end="51">多个页面/界面</strong> 的容器控件,它是Qt框架中的一个堆栈窗口控件,用于在同一空间内堆叠多个子控件(或称“页面”),但一次只显示其中一个。它常用于创建多页面或多视图的应用程序,比如设置向导、选项卡界面(尽管它本身不带选项卡标签)和复杂的表单。<span class="uJ19be notranslate" data-wiz-uids="iqg6pe_j,iqg6pe_k" data-complete="true" data-processed="true"><span class="vKEkVd" data-animation-atomic="" data-sae="">&nbsp;</span></span></p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251015215732159-394805678.png" alt="image" width="534" height="291" loading="lazy"></p>
<p data-start="10" data-end="79">&nbsp;</p>
<p data-start="10" data-end="79">它的工作方式有点像 <strong data-start="68" data-end="76">卡片堆叠</strong>:</p>
<ul data-start="80" data-end="165">
<li data-start="80" data-end="95">
<p data-start="82" data-end="95"><strong>一次只显示一个子界面;</strong></p>

</li>
<li data-start="96" data-end="131">
<p data-start="98" data-end="131"><strong>可以通过索引(int)或者 widget 实例切换显示的页面;</strong></p>

</li>
<li data-start="132" data-end="165">
<p data-start="134" data-end="165"><strong>常用于多页面界面切换(比如“设置/主页/详情”之间切换)。</strong></p>

</li>

</ul>
<h3>3、使用PySide6/PyQt6实现自定义窗口布局的效果</h3>
<p>Window系统的普通的亮色模式下的效果如下所示。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251015160338883-1307613356.png" alt="image" width="755" height="466" loading="lazy"></p>
<p>如果单击折叠按钮,可以看到效果。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251015214905016-1180478075.png" alt="image" width="758" height="464" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;如果切换到Windows深色模式下,窗体颜色变为黑色,同时字体颜色对应变化为亮色一些</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251015160133680-666942920.png" alt="image" width="760" height="416" loading="lazy"></p>
<p>&nbsp;如果切换到全屏模式下,顶部居中位置有全屏退出提示【按ESC键退出】,效果如下</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251015160553832-1597009918.png" alt="image" width="759" height="428" loading="lazy"></p>
<p>&nbsp;而MacOS样式,仿照标题栏左侧放置常规按钮,悬停的时候出现按钮图标效果,如下所示。</p>
<p><img src="https://img2024.cnblogs.com/blog/8867/202510/8867-20251015160020087-55638056.png" alt="image" loading="lazy"></p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    <div style="border-right-color: #cccccc; border-right-width: 1px; border-right-style: solid; padding-right: 5px; border-top-color: #cccccc; border-top-width: 1px; border-top-style: solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left-color: #cccccc; border-left-width: 1px; border-left-style: solid; width: 98%; padding-top: 4px; border-bottom-color: #cccccc; border-bottom-width: 1px; border-bottom-style: solid; background-color: #eeeeee;">
    <img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt>
    <span style="color: #000000"><span class="Apple-tab-span" style="white-space: pre"></span>
   专注于代码生成工具、.Net/Python 框架架构及软件开发,以及各种Vue.js的前端技术应用。著有Winform开发框架/混合式开发框架、微信开发框架、Bootstrap开发框架、ABP开发框架、SqlSugar开发框架、Python开发框架等框架产品。
   <br>  转载请注明出处:撰写人:伍华聪  http://www.iqidi.com <br>    </span></div><br><br>
来源:https://www.cnblogs.com/wuhuacong/p/19143622
頁: [1]
查看完整版本: 使用PySide6/PyQt6实现自定义窗口布局,实现类似FluentWindow效果