最近, 有一个软件, 它需要排班部分的界面, 并且其使用要尽可能直观化。去年, 我做过一个大项目, 类似这个软件, 当时是前端基于react跑在本地的应用, 整个项目涉及前后端分离那些操作, 还有求解条件初始化相关操作, 有这些不同操作。最终呈现的结果是, 这个应用于实际投入使用的情形里, 显得极为缺乏直观性, 并且其安装过程十分繁杂;鉴于后续有可能会将一些拷贝分发给那些并非特别擅长操作电脑的人, 因而此次我抉择运用更为基础性的技术栈(诸如某些gui库, 像tk以及qt, 再加上一个相对易于分发的规模大小, 还有一个清晰明确的程序入口)。
再者, 先前我已然撰写好了几个tk的小型项目, 我觉得当下也是时候去尝试一番pyqt了, 在与gpt的主要助力之下, 我将那个原本仅能于shell当中使用的交互式求解器转移到了pyqt里面去, (要说题外话, 就是qt的窗口性能着实优越, 比我所预想的还要优越许多)

凭借qt的神力, 能让win11、win10直接拥有一个勉强看得过去的界面, 存在现成的解决方法。
我先前主要遭遇过两种解决途径, 其一为, 另一为打包。时常出现的状况是, 打包致使整个库扩充至几百M, 而的关键问题是打包耗时过长, 缘由在于每次都必须将所有的依赖转变为c的库接着编译至一个exe之中。然而正如大家所目睹的, 安装包过大的问题实际上是相对次要的, 而最为关键的是要能使软件便于安装且能够正常使用(能够正常使用也是最初编写gui的一个缘由, shell肯定是唯有经过专业训练才得以使用的标点)
在初期, 我针对两者分别简单写了一个demo做试验, 仅让其在被打包成exe后能够正常求解并输出结果, 然而, 不管我尝试了多少种方法, 也不管我在网上搜索了多少相关的解决办法, 都没办法使其正常使用。
这里是我之前搜资料的时候就找到的网页
其实, 先前我便已然瞧见了韦易笑大佬所撰写的用于打包内容的文章, 我寻思, 按照常理而言, 通常是直接将环境进行解压操作, 而后放置到一个名为temp的文件夹里头, 那么, 我不禁要问, 我自己可不可以直接去制作出这样一个环境来?
我瞅了一下repo, 最新型的也仅仅有, 因而莫不如就依照这个思路自己炮制一个出来。
哦对, 我询问了使用场景, 此场景能够假设所有人都是拥有win10的。因此py38的需求实际上针对这次项目而言并非那么关键重要。
能跑的尝试
最先需要的是一个具备可跑特性的, 最为臃肿的那种实现, 在此处呈现的是相对而言比较新颖的3.13.11的嵌入式版本, 对于这个版本而言, 你能够在官网进行下载。

其次是依赖的环境, 先是按照上头文章所讲的, 利用完整的环境去创建一个虚拟环境, 而后径直把venv/lib/site-移到分发的文件夹底下, 大致是如下这般操作, 遵循这样的步骤。
python venv venv
pip install -r req.txt

接着, 要把这个site – 文件, 复制到进行分发的文件夹的下方, 随后, 再把库添加到环境里面去。
这件事情是, 在其中遇到了一个莫名其妙的状况, 那就是不管怎去么尝试, 这个都不会接纳环境变量里的内容, 所以到最后, 我选择直接运用sys去修改path, 从而强行达成目的(当然这种做法并不是十分优雅, 欢迎各位提出更优的办法)

进行了一通操作后, 软件起码是能够运行起来了, 来来去去操作了半天后, 整个分发包的大小为400M, 已然远远超出了一个“小工具”的大小。

基础环境的site-大小简单的剪枝

此处呈现的是具备干净环境的完整规模, 若要进行手动剪枝, 我提议依据这个规模去判别哪些能够直接予以删除。在此所提及的test明显是能够舍弃的, 毕竟我们所编写的程序显然不会对重新测试的正确性提出要求, pip亦是如此: 我们并不要求手动额外安装任何软件包, 因而这两者皆可删除。
这两个加起来一个是35M一个是11M,这已经相对比较可观了
更细致的剪枝
看到上面, qt的空间占比是非常突出显著的, 能够开展更为细致深入的剪枝工作。它是一个极为规整完善的库, 此库整体大小为62M, 而其中52M都是dll文件。

而pyqt的依赖库更庞大

pyqt6
首先, 我最开始的流程是这样的, 要去删一个大一点的库, 然后查看程序是否还能够使用。要是能使用的话, 那就重启之后再去删下一个库;要是不能使用, 那就进行恢复操作。然而, 这个过程来来回回实在是太过缓慢了。所以呢, 我干脆去问了一下 gpt 并且发现了相关情况。它能够直接监听所有的情况, 要是有一个在运行的时候需要使用某个东西, 那么肯定会发生对这个文件的一次 read 操作, 而这就会被记录下来。只要将条件设定为, 如果访问的路径不包含 site – 的内容, 那就排除掉。最终, 我们能够获得一大串的文件。
然后我们可以导出这些访问文件来确定其他没用的文件是什么
import csv
import os
def load_logfile(file_path):
log_data = []
with open(file_path, "r", encoding="utf-8") as file:
reader = csv.DictReader(file)
for row in reader:
log_data.append(row)
return log_data
used_file = set()
d = load_logfile("Logfile.CSV")
for entry in d:
used_file.add(entry["Path"])
root_directory = r"ext"
root_directory = os.path.abspath(root_directory)
for dirpath, dirnames, filenames in os.walk(root_directory):
for filename in filenames:
file_path = os.path.join(dirpath, filename)
if "__pycache__" in used_file:
os.remove(file_path)
将root设置成pyqt在此处之后, 便能够针对qt开展更为精细的剪枝操作, 最终获取到:
200MB剪枝到了40MB
同样的道理, 在这儿, 我针对整个site-采取了更为激进的做剪枝的行为, 实际上, 这般做仍旧不是特别稳当, 不太推荐这么去做。

最终呈现的结果是, 这个整体的site, 剩余的大小为150MB, 要是打算开展更为精细的剪枝操作, 或许就只好着手裁切dll文件自身的大小, 而这会变得相当复杂。
额外章节 – 让程序长得更像程序
最终来编写一个小型的exe, 或者写这样一个bat能启动嵌入式以及那一些主要代码, 这儿呈现的就是bat的版本。
@echo off
start "" "./runtime/pythonw.exe" "./gui.py"
一旦bat此项启动, 在好多情形下, 使用者根本不清楚这竟为具备可触属性之物, 我们能够撰写一个exe文件, 达成完全等同之事。
#include
#include
int main() {
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Command line (must be mutable!)
char cmd[] = ".\\\\runtime\\\\pythonw.exe .\\\\gui.py";
if (!CreateProcess(
NULL, // Application name (NULL = use command line)
cmd, // Command line
NULL, // Process security attributes
NULL, // Thread security attributes
FALSE, // Inherit handles
CREATE_NO_WINDOW, // Creation flags
NULL, // Environment
NULL, // Current directory
&si, // Startup info
&pi // Process info
))
{
printf("CreateProcess failed (%lu)\\n", GetLastError());
return 1;
}
// Optionally wait for Python to exit
// WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}
接着, 使用gcc进行编译, 使其成为start.exe, 之后将其放置在分发文件夹当中, 如此这般, 功能便是相同的了。





