<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>bhznjns (BHznJNs)</title>
    <link>http://beta.w2solo.com/bhznjns</link>
    <description/>
    <language>en-us</language>
    <item>
      <title>记一次 Python GUI 框架的选择</title>
      <description>&lt;h2 id="为什么要使用 Python 写 GUI 程序？"&gt;为什么要使用 Python 写 GUI 程序？&lt;/h2&gt;
&lt;p&gt;这得说到我最近在维护的开源项目 &lt;a href="https://github.com/BHznJNs/InputShare" rel="nofollow" target="_blank" title=""&gt;InputShare&lt;/a&gt; 了。&lt;/p&gt;

&lt;p&gt;这个项目最开始是没有图形界面的，我想着使用 Python 的话一方面能够快速验证，另一方面 Python 的社区资源足够丰富，就选择使用 Python 编写原型。&lt;/p&gt;

&lt;p&gt;在软件基本可用后，我想着给它加上一个易用的图形界面方便我自己使用，也能让软件方便推广。此时我依然延续了原型设计的思维，选择了我还算熟悉并且能够快速做出界面的 tkinter 和 &lt;a href="https://customtkinter.tomschimansky.com/" rel="nofollow" target="_blank" title=""&gt;CustomTkinter&lt;/a&gt;
但是在最近，我想要改动设置界面，将设置项进行分组，并添加新的设置项，我发现相关的 UI 代码很难修改。
同时，我尝试使用英文版本界面录制推广视频时，意外发现 CustomTkinter 没有自带文本溢出换行！我需要手动在文本中添加 &lt;code&gt;\n&lt;/code&gt; 来实现换行！&lt;/p&gt;

&lt;p&gt;于是我开始尝试使用其它的 Python GUI 框架进行重构。&lt;/p&gt;
&lt;h2 id="我调研了哪些替代品？"&gt;我调研了哪些替代品？&lt;/h2&gt;
&lt;p&gt;大概内容可以参考&lt;a href="https://github.com/BHznJNs/InputShare/issues/32" rel="nofollow" target="_blank" title=""&gt;这个 issue&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我先后调研了如下 GUI 框架：&lt;/p&gt;
&lt;h3 id="Flet"&gt;&lt;a href="https://flet.dev/" rel="nofollow" target="_blank" title=""&gt;Flet&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;看起来很好，是 Flutter 的 Python 封装，跨平台性很好，并且自带多平台构建支持。
经过基本了解后我发现 Flet 并不很适用于 InputShare 这种主要运行在后台的应用，使用这个框架可能需要大幅重构项目，遂放弃。&lt;/p&gt;
&lt;h3 id="PyQt + QFluentWidgets"&gt;PyQt + &lt;a href="https://qfluentwidgets.com/" rel="nofollow" target="_blank" title=""&gt;QFluentWidgets&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;基于成熟的 PyQt，并且使用的 fluent design 也足够美观。
但是我在尝试的过程中发现其文档并不好懂，很多好用一些的组件需要付费，并且官方的 Gallery 实例的性能并不好，目测只有 30 帧。&lt;/p&gt;
&lt;h3 id="pywebview"&gt;&lt;a href="https://github.com/r0x0r/pywebview" rel="nofollow" target="_blank" title=""&gt;pywebview&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;封装了原生的 Webview 并提供 Python Binding，可以使用 Web 前端代码开发界面，也提供了 Python 和 JavaScript 的通信支持，看起来很美好。&lt;/p&gt;

&lt;p&gt;但是这个库控制的窗口没有黑暗模式的支持，即使&lt;a href="https://github.com/r0x0r/pywebview/issues/1494" rel="nofollow" target="_blank" title=""&gt;当系统处于黑暗模式时，窗口的边框依然处于日间模式&lt;/a&gt;。
我尝试过将窗口设为无边框，库倒是有将 DOM 元素设为窗口可拖拽区域的支持，但是在无边框模式下窗口&lt;a href="https://github.com/r0x0r/pywebview/issues/1510" rel="nofollow" target="_blank" title=""&gt;无法被鼠标调整大小&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;遂放弃。&lt;/p&gt;
&lt;h3 id="webui"&gt;&lt;a href="https://github.com/webui-dev/webui" rel="nofollow" target="_blank" title=""&gt;webui&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这个项目另辟蹊径，使用系统安装的浏览器作为应用窗口，并提供包含 Python 在内的多语言支持。
但是在我把示例代码跑起来后就发现了问题，如&lt;a href="https://github.com/webui-dev/webui/issues/550" rel="nofollow" target="_blank" title=""&gt;这个 issue&lt;/a&gt; 中所示。遂放弃。&lt;/p&gt;
&lt;h3 id="PyQt + QtWebEngine（尝试中）"&gt;PyQt + QtWebEngine（尝试中）&lt;/h3&gt;
&lt;p&gt;我在放弃 webui 后想到，要展示前端界面，我未必非得用专门的框架。像 PyQt 这样框架就已经有成熟的 Webview 支持了。
我在成功把内嵌 Webview 的 Qt 窗口运行之后，尝试封装一个&lt;a href="https://github.com/BHznJNs/PyQWebWindow" rel="nofollow" target="_blank" title=""&gt;专门的库&lt;/a&gt;用于展示前端界面。为了这盘醋包了顿饺子了属于是。&lt;/p&gt;
&lt;h2 id="这个经历给了我两个教训"&gt;这个经历给了我两个教训&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Python 真的不适合用于图形界面程序开发。&lt;/li&gt;
&lt;li&gt;引自《程序员修炼之道》，不要把原型用于产品。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;文章最后，是《程序员修炼之道》的原文引用：&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2 id="不要把原型用于产品"&gt;不要把原型用于产品&lt;/h2&gt;
&lt;p&gt;在开始任何基于代码的原型开发之前，请确保每个人都理解，正在编写的是一次性代码。原型可能有着欺骗性的外表，对那些不知道这只是原型的人产生吸引力。你必须非常清楚地表明该代码是用完即弃的，它并不完整也不可能做到完整。
如果你觉得所在的环境或文化中，原型代码的目的很有可能被误解，那么最好使用曳光弹的方法。这样，最后你可以做出一个坚实的框架，在此基础上进行未来的开发。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr&gt;

&lt;p&gt;本文转载自我的个人博客，&lt;a href="https://bhznjns.github.io/#static/%E7%BC%96%E7%A8%8B/Python/%E4%B8%80%E6%AC%A1%20Python%20GUI%20%E6%A1%86%E6%9E%B6%E7%9A%84%E9%80%89%E6%8B%A9.md" rel="nofollow" target="_blank" title=""&gt;点此查看原文&lt;/a&gt;&lt;/p&gt;</description>
      <author>bhznjns</author>
      <pubDate>Mon, 24 Mar 2025 09:59:01 +0800</pubDate>
      <link>http://beta.w2solo.com/topics/5598</link>
      <guid>http://beta.w2solo.com/topics/5598</guid>
    </item>
  </channel>
</rss>
