恒源云GPU服务器使用Linux图形化界面编译软件记录

首先你要明白我这篇文章的目的是啥,是将Windows中编译通过生成快捷键的exe软件程序进一步在Linux上编译,是为了使UI界面程序尽可能的去适配国产系统例如银河麒麟V10等操作系统。

大概讲一下我的思路先在Windows上验证软件,并且生成外部链接库,不和exe打包到一起。

首先在pycharm上运行大概是这样子的。

为了尽可能的使exe小,我将依赖库全部放到libs中了,有torch,ultralytics,numpy,pandas等等

安装教程:

cd D:\PyCharm\UI

pyinstaller MainUI.spec

python 复制代码
# -*- MainUI.spec -*-

import sys
import os
import pkgutil
from PyInstaller.utils.hooks import collect_submodules

# ============ 配置 ============
project_path = os.path.abspath(SPECPATH)
libs_path = os.path.join(project_path, 'libs')

if os.path.isdir(libs_path) and libs_path not in sys.path:
    sys.path.insert(0, libs_path)

block_cipher = None

# ============ 收集完整 stdlib(含所有子模块) ============
def get_stdlib_top_names():
    """获取所有标准库顶层模块名"""
    if hasattr(sys, 'stdlib_module_names'):
        names = set(sys.stdlib_module_names)
    else:
        stdlib_path = os.path.dirname(os.__file__)
        names = set()
        for m in pkgutil.iter_modules([stdlib_path]):
            names.add(m.name)
        for entry in os.listdir(stdlib_path):
            full = os.path.join(stdlib_path, entry)
            if os.path.isdir(full) and os.path.isfile(os.path.join(full, '__init__.py')):
                names.add(entry)
    # 排除不需要的
    exclude = {'antigravity', 'this', 'idlelib', 'turtledemo', 'tkinter', 'test', 'tests', '__phello__', '_pyio'}
    return [n for n in names if not n.startswith('_') and n not in exclude]

# 顶层模块名
STDLIB_TOP = get_stdlib_top_names()

# 递归收集所有子模块(关键!解决 unittest.mock 这类问题)
STDLIB_ALL = []
for name in STDLIB_TOP:
    try:
        STDLIB_ALL.extend(collect_submodules(name))
    except Exception:
        STDLIB_ALL.append(name)

print(f'[spec] 收集到 {len(STDLIB_ALL)} 个标准库模块')

# ============ 要排除的第三方库(不打包进 exe,运行时从 libs 外部加载) ============
EXCLUDE_LIBS = [
    'torch', 'torchvision', 'torchaudio', 'torchmetrics', 'torchsummary',
    'ultralytics', 'ultralytics_thop', 'numpy', 'pandas', 'scipy', 'sklearn', 'scikit-learn',
    'matplotlib', 'matplotlib_inline', 'mpl_toolkits', 'seaborn',
    'PIL', 'Pillow', 'yaml', 'tqdm', 'requests', 'urllib3', 'certifi', 'charset_normalizer',
    'idna', 'psutil', 'six', 'pytz', 'dateutil', 'python_dateutil',
    'packaging', 'pyparsing', 'cycler', 'kiwisolver', 'fonttools', 'contourpy', 'joblib', 'threadpoolctl',
    'IPython', 'ipykernel', 'notebook', 'jupyter',
]

# ============ 强制打包进 exe 的库(即使它们在 libs 中也不排除) ============
FORCE_BUNDLE = [
    'cv2',         # OpenCV --- DLL 依赖复杂,打包进来更稳
    'PyQt5',       # Qt GUI --- 避免平台插件缺失
    'six',         # dateutil 的依赖,libs 中残缺
]

# ============ 自动检测 libs 下的所有包并加入排除列表 ============
def get_all_libs_packages(libs_dir):
    """扫描 libs 目录,返回所有可导入的包名"""
    pkgs = set()
    if not os.path.isdir(libs_dir):
        return pkgs
    for item in os.listdir(libs_dir):
        item_path = os.path.join(libs_dir, item)
        # 目录且不是 __pycache__、不是 .dist-info
        if os.path.isdir(item_path) and not item.startswith('__') and not item.endswith('.dist-info') and not item.endswith('.libs'):
            # 排除一些非包的目录
            if item in ('docs', 'tests', 'third_party', 'bin', 'Scripts'):
                continue
            pkgs.add(item)
    # 合并手动列表
    pkgs.update(EXCLUDE_LIBS)
    # 剔除强制打包的
    pkgs.difference_update(FORCE_BUNDLE)
    return sorted(pkgs)

ALL_EXCLUDE = get_all_libs_packages(libs_path)
print(f'[spec] 自动排除 {len(ALL_EXCLUDE)} 个包')
print(f'[spec] 强制打包: {FORCE_BUNDLE}')

a = Analysis(
    ['MainUI.py'],
    pathex=[project_path, libs_path],
    binaries=[],
    datas=[
        (os.path.join(project_path, 'config', 'locations.json'), 'config'),
        (os.path.join(project_path, 'best.pt'), '.'),
        (os.path.join(project_path, 'ui', '*.ui'), 'ui'),
        (os.path.join(project_path, 'ui', '*.qrc'), 'ui'),
        (os.path.join(project_path, 'ui', 'icon_rc.py'), 'ui'),
    ],
    hiddenimports=STDLIB_ALL + [
        # 你项目自己的模块
        'historical_analysis',
        'historical_analysis.data_processor',
        'historical_analysis.main',
        'historical_analysis.map_visualizer',
        'historical_analysis.stats_calculator',
        'historical_analysis.time_analyzer',
        'ui.detect_ui_v2',
        'ui.huwai',
        'ui.huwai_dialog',
        'ui.video_choose',
        'ui.video_choose_dialog',
        'ui.icon_rc',
        'utils.capnums',
        'utils.main_utils',
        'utils.message',
    ],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=['runtime_hook.py'],
    excludes=ALL_EXCLUDE,
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)

pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    [],
    exclude_binaries=True,
    name='MainUI',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=True,
)

coll = COLLECT(
    exe,
    a.binaries,
    a.zipfiles,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='MainUI',
)

装好了。现在重新运行 exe 试试:

终端运行可以查看错误

D:\PyCharm\UI\dist\MainUI\MainUI.exe

尽量在终端中运行MainUI.exe,你会发现你有很多报错的。一般是没有包没找到库地址,你让AI改改就好了。

之后会在dist中生成exe,正常情况下是可以执行跳转的。

接下来就是在Linux有GPU的条件下进行测试了。可以看看这篇文章我主要是参考的这篇文章。
https://blog.csdn.net/m0_62627216/article/details/135589831?fromshare=blogdetail&sharetype=blogdetail&sharerId=135589831&sharerefer=PC&sharesource=weixin_52531699&sharefrom=from_link

实例开启自定义服务

终端中输入:

c 复制代码
cd ~
curl -OL https://download.gpushare.com/download/platform/install_desktop/install_desktop
c 复制代码
chmod +x ./install_desktop
./install_desktop

等待安装完成,网速快的话几分钟就可以下载好

下载好之后,会出现如下界面,密码可以自定义,但是一定要记住,这个是用于连接图形化界面的:

2.启动桌面

输入manage_vnc可以查看启动和停止命令:

然后输入manage_vnc start

图形化界面就开始启动了,然后回到控制台,找到自定义服务:

三、必做设置

1.熄屏设置

如果你一两分钟不碰它,他会自己断开连接,只能重启VNC服务才行,所以要设置永不熄屏,以防止数据丢失。


完成以上设置后,安装目标检测库之后。

pip install PyQt5 ultralytics pandas seaborn

pip install pyinstaller opencv-python numpy pandas matplotlib

seaborn PyQt5 PyYAML tqdm requests psutil python-dateutil

packaging joblib scipy scikit-learn Pillow ultralytics

首先在ubuntu中测试UI程序。

切记一定要在Ubuntu中打开,你在服务器的jupterLab中打开会显示缺少 XCB 库的。以上可知测试没问题。

同样我是将库放到了libs中

Linux安装:

将依赖包转移

c 复制代码
mkdir -p "/hy-tmp/UI(Linux)/libs"
cp -r /usr/local/miniconda3/lib/python3.8/site-packages/* "/hy-tmp/UI(Linux)/libs"
c 复制代码
pyinstaller MainUI_linux.spec
bash 复制代码
# -*- MainUI_linux.spec -*-
"""
PyInstaller spec for Linux (Kylin V10 / x86_64) --- onedir 模式
==============================================================
使用方法:
    pyinstaller MainUI_linux.spec

产物:
    dist/MainUI/              ← onedir 输出目录
    ├── MainUI                ← 可执行文件
    ├── start.sh              ← 启动脚本(手动复制)
    └── _internal/            ← 所有依赖和资源

外部依赖策略(与 Windows 版 MainUI.spec 一致):
    大型第三方库(torch/numpy/scipy/matplotlib/ultralytics 等)
    不打包进 exe,而是放在项目根目录的 libs/ 文件夹中。
    运行时由 runtime_hook.py 自动将 libs/ 添加到 sys.path 和
    LD_LIBRARY_PATH,实现外部 .so 库的动态加载。
    这大幅减小了打包体积(从 ~5GB 降到 ~500MB)。
"""
import os
import sys
import pkgutil
from PyInstaller.utils.hooks import collect_submodules

# ============ 项目路径(自动从 spec 文件位置推导)============
PROJECT_DIR = os.path.abspath(SPECPATH)
LIBS_DIR = os.path.join(PROJECT_DIR, 'libs')

# 将 libs/ 加入搜索路径,便于 PyInstaller 分析时找到模块
if os.path.isdir(LIBS_DIR) and LIBS_DIR not in sys.path:
    sys.path.insert(0, LIBS_DIR)

block_cipher = None

# ============ 收集完整标准库(含所有子模块,解决 unittest.mock 等隐式依赖)============
def get_stdlib_top_names():
    """获取所有标准库顶层模块名"""
    if hasattr(sys, 'stdlib_module_names'):
        names = set(sys.stdlib_module_names)
    else:
        stdlib_path = os.path.dirname(os.__file__)
        names = set()
        for m in pkgutil.iter_modules([stdlib_path]):
            names.add(m.name)
        for entry in os.listdir(stdlib_path):
            full = os.path.join(stdlib_path, entry)
            if os.path.isdir(full) and os.path.isfile(os.path.join(full, '__init__.py')):
                names.add(entry)
    exclude = {'antigravity', 'this', 'idlelib', 'turtledemo', 'tkinter', 'test', 'tests', '__phello__', '_pyio'}
    return [n for n in names if not n.startswith('_') and n not in exclude]

STDLIB_TOP = get_stdlib_top_names()
STDLIB_ALL = []
for name in STDLIB_TOP:
    try:
        subs = collect_submodules(name)
        STDLIB_ALL.extend(subs)
    except Exception:
        pass
    # 确保顶层模块本身一定被包含(C 扩展模块如 cmath 可能无子模块,collect_submodules 返回空列表)
    if name not in STDLIB_ALL:
        STDLIB_ALL.append(name)
print(f'[spec] 收集到 {len(STDLIB_ALL)} 个标准库模块')

# ============ C 扩展标准库模块(PyInstaller 钩子可能遗漏,需显式声明)============
STDLIB_C_EXTENSIONS = [
    'cmath',        # 复数数学
    'math',         # 数学函数
    '_json',        # JSON 序列化
    '_socket',      # 网络 socket
    '_ssl',         # SSL/TLS
    '_hashlib',     # 哈希算法
    '_ctypes',      # C 类型调用
    '_io',          # IO 基类
    '_codecs',      # 编解码器
    '_collections', # 集合类型
    '_functools',   # 函数工具
    '_operator',    # 运算符
    '_stat',        # 文件状态
    '_string',      # 字符串工具
    '_struct',      # 二进制结构
    '_weakref',     # 弱引用
    '_abc',         # 抽象基类
    '_csv',         # CSV 解析
    '_datetime',    # 日期时间
    '_decimal',     # 十进制
    '_elementtree', # XML 解析
    '_heapq',       # 堆队列
    '_lsprof',      # 性能分析
    '_md5',         # MD5
    '_multibytecodec', # 多字节编码
    '_opcode',      # 操作码
    '_pickle',      # Pickle
    '_posixsubprocess', # 子进程
    '_queue',       # 队列
    '_random',      # 随机数
    '_sha1', '_sha2', '_sha3',  # SHA 系列
    '_signal',      # 信号处理
    '_sqlite3',     # SQLite
    '_sre',         # 正则表达式
    '_statistics',  # 统计
    '_thread',      # 线程
    '_tokenize',    # 词法分析
    '_tracemalloc', # 内存追踪
    '_typing',      # 类型提示
    '_uuid',        # UUID
    '_warnings',    # 警告
    '_winapi',      # Windows API(Linux 上通常不存在,但不影响)
    '_zoneinfo',    # 时区
    'array',        # 数组
    'binascii',     # 二进制 ASCII
    'fcntl',        # 文件控制
    'grp',          # 组数据库
    'mmap',         # 内存映射
    'ossaudiodev',  # OSS 音频(Linux 特有)
    'posix',        # POSIX 接口
    'pwd',          # 密码数据库
    'readline',     # 行编辑
    'resource',     # 资源限制
    'select',       # IO 多路复用
    'spwd',         # 影子密码
    'syslog',       # 系统日志
    'termios',      # 终端控制
    'unicodedata',  # Unicode 数据库
    'zlib',         # 压缩
]

# ============ 排除列表:不打包进 exe 的大型第三方库(运行时从 libs/ 外部加载)============
EXCLUDE_LIBS = [
    # PyTorch 生态(体积巨大,~3GB)
    'torch', 'torchvision', 'torchaudio', 'torchmetrics', 'torchsummary',
    # YOLO / Ultralytics
    'ultralytics', 'ultralytics_thop',
    # 科学计算
    'numpy', 'pandas', 'scipy', 'sklearn', 'scikit-learn',
    # 可视化
    'matplotlib', 'matplotlib_inline', 'mpl_toolkits', 'seaborn',
    # 图像处理
    'PIL', 'Pillow',
    # 基础依赖
    'yaml', 'tqdm', 'requests', 'urllib3', 'certifi', 'charset_normalizer',
    'idna', 'psutil', 'six', 'pytz', 'dateutil', 'python_dateutil',
    'packaging', 'pyparsing', 'cycler', 'kiwisolver', 'fonttools', 'contourpy',
    'joblib', 'threadpoolctl',
    # 开发工具(本就不需要打包)
    'IPython', 'ipykernel', 'jupyter', 'notebook',
]

# ============ 强制打包:即使 libs/ 中存在也打包进 exe(关键依赖)============
FORCE_BUNDLE = [
    'cv2',         # OpenCV --- .so 依赖链复杂,打包进来更稳定
    'PyQt5',       # Qt GUI --- 避免平台插件缺失
    'six',         # dateutil 的依赖,libs 中可能残缺
]

# ============ 自动扫描 libs/ 目录,合并排除列表 ============
def get_all_libs_packages(libs_dir):
    """扫描 libs 目录,返回所有可导入的包名"""
    pkgs = set()
    if not os.path.isdir(libs_dir):
        return pkgs
    for item in os.listdir(libs_dir):
        item_path = os.path.join(libs_dir, item)
        # 只收集合法的 Python 包目录
        if os.path.isdir(item_path) and not item.startswith('__') and \
           not item.endswith('.dist-info') and not item.endswith('.libs'):
            if item in ('docs', 'tests', 'third_party', 'bin', 'Scripts'):
                continue
            pkgs.add(item)
    # 合并手动列表
    pkgs.update(EXCLUDE_LIBS)
    # 剔除强制打包的
    pkgs.difference_update(FORCE_BUNDLE)
    return sorted(pkgs)

ALL_EXCLUDE = get_all_libs_packages(LIBS_DIR)
print(f'[spec] 自动排除 {len(ALL_EXCLUDE)} 个包(将从 libs/ 外部加载)')
print(f'[spec] 强制打包: {FORCE_BUNDLE}')

# ============ 项目内模块(确保不被 tree-shaking 丢掉)============
PROJECT_HIDDEN_IMPORTS = [
    # 项目主模块
    'detect_mainui',
    'asr_service',
    'target',
    'huwai_detect',
    'relitu',
    'data_processor',
    # historical_analysis 子包
    'historical_analysis',
    'historical_analysis.__init__',
    'historical_analysis.data_processor',
    'historical_analysis.main',
    'historical_analysis.map_visualizer',
    'historical_analysis.stats_calculator',
    'historical_analysis.time_analyzer',
    # ui 子包
    'ui',
    'ui.__init__',
    'ui.detect_ui_v2',
    'ui.huwai',
    'ui.huwai_dialog',
    'ui.video_choose',
    'ui.video_choose_dialog',
    'ui.icon_rc',
    # utils 子包
    'utils',
    'utils.__init__',
    'utils.capnums',
    'utils.main_utils',
    'utils.message',
]

# ============ 隐式导入(仅包含强制打包的库 + 不被排除的库)============
THIRD_PARTY_HIDDEN_IMPORTS = [
    # OpenCV(强制打包)
    'cv2', 'cv2.data',
    # PyQt5(强制打包)
    'PyQt5', 'PyQt5.QtCore', 'PyQt5.QtGui', 'PyQt5.QtWidgets',
    'PyQt5.sip',
]

ALL_HIDDEN_IMPORTS = STDLIB_ALL + STDLIB_C_EXTENSIONS + PROJECT_HIDDEN_IMPORTS + THIRD_PARTY_HIDDEN_IMPORTS

# ============ 数据文件(被打包进 _internal)============
datas = [
    # 模型文件
    (os.path.join(PROJECT_DIR, 'best.pt'), '.'),
    # 配置文件
    (os.path.join(PROJECT_DIR, 'config', 'locations.json'), 'config'),
    # UI 模板文件
    (os.path.join(PROJECT_DIR, 'ui', 'detect_ui_v2.ui'), 'ui'),
    (os.path.join(PROJECT_DIR, 'ui', 'huwai.ui'), 'ui'),
    (os.path.join(PROJECT_DIR, 'ui', 'video_choose.ui'), 'ui'),
    # 图标资源
    (os.path.join(PROJECT_DIR, 'ui', 'icon_rc.py'), 'ui'),
]

# ============ Analysis ============
a = Analysis(
    ['MainUI.py'],
    pathex=[PROJECT_DIR, LIBS_DIR],
    binaries=[],
    datas=datas,
    hiddenimports=ALL_HIDDEN_IMPORTS,
    hookspath=[],
    hooksconfig={},
    runtime_hooks=['runtime_hook.py'],
    excludes=ALL_EXCLUDE,
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)

pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    [],
    exclude_binaries=True,
    name='MainUI',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=True,   # Linux 上建议 console=True,方便看日志
)

coll = COLLECT(
    exe,
    a.binaries,
    a.zipfiles,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='MainUI',
)
c 复制代码
pyinstaller MainUI_linux.spec

编译测试通过。

找到dist中的可执行文件执行程序。

c 复制代码
./MainUI

可执行程序没问题。

编译成功后,将文件压缩即可部署到其它ubuntu中。

c 复制代码
zip -r 'UI(Linux)-test1.zip' 'UI(Linux)'
相关推荐
Bruce_kaizy7 分钟前
c++ linux环境编程——文件io介绍以及open 、write 、read 三剑客深度详解
linux·服务器·c++·ubuntu·操作系统·文件io
亦良Cool22 分钟前
VMware虚拟机ubuntu瘦身,解决虚拟机越用越大
linux·运维·ubuntu
星辰&与海2 小时前
KVM + QEMU虚拟化方案
linux·运维
宋浮檀s2 小时前
应急响应——恶意流量&攻击行为识别
linux·运维·网络·网络安全·应急响应
REDcker2 小时前
Linux OverlayFS详解
java·linux·运维
Royzst2 小时前
xml知识点
java·服务器·前端
TechWJ3 小时前
数据库在公司内网,出差路上想查数据怎么办?
服务器·数据库·mariadb
zizle_lin3 小时前
WSL的系统安装和部分环境配置(按需操作)
运维
lwx9148523 小时前
Linux系统中用户锁定后如何解锁
linux·运维·服务器
zhangrelay4 小时前
ROS 2 Lyrical Luth启程-Ubuntu26.04-
linux·笔记·学习·ubuntu