├── .github └── workflows │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── PyStand.cpp ├── PyStand.h ├── README.md ├── VSCode Environment └── .vscode │ ├── launch.json │ └── settings.json ├── appicon.ico ├── build.bat └── resource.rc /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: PyStand 2 | 3 | on: 4 | push: 5 | pull_request: 6 | types: [ opened, synchronize, reopened, ready_for_review ] 7 | 8 | env: 9 | BUILD_TYPE: Release 10 | 11 | jobs: 12 | build-msvc: 13 | if: >- 14 | github.event.pull_request.draft == false 15 | 16 | runs-on: windows-latest 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | arch: [ Win32, x64 ] 22 | subsystem: [ GUI, CLI ] 23 | include: 24 | - generator: Visual Studio 17 2022 25 | 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v3 29 | 30 | - name: Configure 31 | run: > 32 | cmake 33 | -G "${{ matrix.generator }}" 34 | -A ${{ matrix.arch }} 35 | -B build 36 | ${{ matrix.subsystem == 'GUI' && '-DPYSTAND_CONSOLE=OFF' || '-DPYSTAND_CONSOLE=ON' }} 37 | 38 | - name: Build 39 | run: | 40 | cmake --build build --config ${{ env.BUILD_TYPE }} 41 | 42 | - name: Upload artifacts 43 | uses: actions/upload-artifact@v3 44 | with: 45 | name: PyStand-${{ matrix.arch }}-${{ matrix.subsystem }} 46 | path: build/${{ env.BUILD_TYPE }} 47 | 48 | build-gcc: 49 | if: >- 50 | github.event.pull_request.draft == false 51 | 52 | runs-on: windows-latest 53 | 54 | strategy: 55 | fail-fast: false 56 | matrix: 57 | sys: [ mingw32, mingw64 ] 58 | subsystem: [ GUI, CLI ] 59 | 60 | steps: 61 | - name: Checkout 62 | uses: actions/checkout@v3 63 | 64 | - name: Setup WinLibs 65 | run: | 66 | if ( '${{ matrix.sys }}' -eq 'mingw32' ) 67 | { 68 | Invoke-WebRequest -Uri https://github.com/brechtsanders/winlibs_mingw/releases/download/7.5.0-7.0.0-r1/winlibs-i686-posix-dwarf-gcc-7.5.0-mingw-w64-7.0.0-r1.7z -OutFile ${{ matrix.sys }}.7z 69 | } 70 | else 71 | { 72 | Invoke-WebRequest -Uri https://github.com/brechtsanders/winlibs_mingw/releases/download/7.5.0-7.0.0-r1/winlibs-x86_64-posix-seh-gcc-7.5.0-mingw-w64-7.0.0-r1.7z -OutFile ${{ matrix.sys }}.7z 73 | } 74 | 7z x ${{ matrix.sys }}.7z 75 | "${{ github.workspace }}\${{ matrix.sys }}\bin" >> $env:GITHUB_PATH 76 | 77 | - name: Configure 78 | run: > 79 | cmake 80 | -G "MinGW Makefiles" 81 | -B build 82 | -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} 83 | ${{ matrix.subsystem == 'GUI' && '-DPYSTAND_CONSOLE=OFF' || '-DPYSTAND_CONSOLE=ON' }} 84 | 85 | - name: Build 86 | run: | 87 | cmake --build build --config ${{ env.BUILD_TYPE }} 88 | 89 | - name: Upload artifacts 90 | uses: actions/upload-artifact@v3 91 | with: 92 | name: PyStand-${{ matrix.sys }}-${{ matrix.subsystem }} 93 | path: build\PyStand.exe 94 | 95 | release: 96 | if: startsWith(github.ref, 'refs/tags/') 97 | 98 | runs-on: windows-latest 99 | 100 | needs: [ build-msvc, build-gcc ] 101 | 102 | steps: 103 | - name: Download artifacts 104 | uses: actions/download-artifact@v3 105 | 106 | - name: List downloaded files 107 | run: ls -R 108 | 109 | - name: Create archives 110 | run: > 111 | 7z 112 | a 113 | PyStand-v${{ github.ref_name }}-exe.zip 114 | PyStand-Win32-CLI 115 | PyStand-Win32-GUI 116 | PyStand-x64-CLI 117 | PyStand-x64-GUI 118 | PyStand-mingw32-CLI 119 | PyStand-mingw32-GUI 120 | PyStand-mingw64-CLI 121 | PyStand-mingw64-GUI 122 | 123 | - name: Release 124 | uses: softprops/action-gh-release@v1 125 | env: 126 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 127 | with: 128 | generate_release_notes: true 129 | files: | 130 | PyStand*.zip 131 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | *.pch 35 | *.sdf 36 | 37 | /.root 38 | 39 | __pycache__/* 40 | *.pyc 41 | *.pyo 42 | 43 | /runtime/* 44 | /source/runtime/* 45 | 46 | /source/objs/* 47 | /source/Debug/* 48 | /source/Release/* 49 | /source/x64/* 50 | /source/.vs/* 51 | /source/build/* 52 | 53 | /objs/* 54 | /Debug/* 55 | /Release/* 56 | /x64/* 57 | /.vs/* 58 | /build/* 59 | 60 | /.tasks 61 | /site-packages/* 62 | /publish/* 63 | /test/ztest_*.cpp 64 | /ztest_*.cpp 65 | 66 | /.vscode -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project (PyStand LANGUAGES CXX RC) 4 | 5 | option(PYSTAND_CONSOLE "Build PyStand as a console application." OFF) 6 | 7 | # sources 8 | set(sources 9 | PyStand.cpp 10 | resource.rc 11 | ) 12 | 13 | if (PYSTAND_CONSOLE) 14 | add_executable(PyStand ${sources}) 15 | target_compile_definitions(PyStand PRIVATE PYSTAND_CONSOLE) 16 | else() 17 | add_executable(PyStand WIN32 ${sources}) 18 | endif() 19 | 20 | # static link 21 | if (CMAKE_GENERATOR MATCHES "Visual Studio") 22 | set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") 23 | target_compile_options(PyStand 24 | PUBLIC 25 | $<$:/MTd> 26 | $<$:/MT> 27 | $<$:/MT> 28 | ) 29 | target_link_options(PyStand PUBLIC /INCREMENTAL:NO /NODEFAULTLIB:MSVCRT) 30 | else() 31 | # for mingw 32 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_RELEASE} -s") 33 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_RELEASE} -s") 34 | target_link_libraries(PyStand 35 | -static 36 | shlwapi 37 | winmm 38 | ) 39 | endif() 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Linwei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PyStand.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiroi-sora/PyStand-Sora/524d6df24a5631d45f37836a72a3137d9afb9a4f/PyStand.cpp -------------------------------------------------------------------------------- /PyStand.h: -------------------------------------------------------------------------------- 1 | //===================================================================== 2 | // 3 | // PyStand.h - 4 | // 5 | // Created by skywind on 2022/02/03 6 | // Last Modified: 2022/02/03 23:39:52 7 | // 8 | //===================================================================== 9 | #ifndef _PYSTAND_H_ 10 | #define _PYSTAND_H_ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | //--------------------------------------------------------------------- 19 | // PyStand 20 | //--------------------------------------------------------------------- 21 | class PyStand 22 | { 23 | public: 24 | virtual ~PyStand(); 25 | PyStand(const wchar_t *runtime); 26 | PyStand(const char *runtime); 27 | 28 | public: 29 | std::wstring Ansi2Unicode(const char *text); 30 | 31 | int RunString(); 32 | 33 | int DetectScript(); 34 | 35 | protected: 36 | bool CheckEnviron(const wchar_t *rtp); 37 | bool LoadPython(); 38 | 39 | protected: 40 | typedef int (*t_Py_Main)(int argc, wchar_t **argv); 41 | t_Py_Main _Py_Main; 42 | 43 | protected: 44 | HINSTANCE _hDLL; 45 | std::wstring _cwd; // current working directory 46 | std::wstring _args; // arguments 47 | std::wstring _pystand; // absolute path of pystand 48 | std::wstring _runtime; // absolute path of embedded python runtime 49 | std::wstring _home; // home directory of PyStand.exe 50 | std::wstring _script; // init script like PyStand.int or PyStand.py 51 | std::vector _argv; 52 | std::vector _py_argv; 53 | std::vector _py_args; 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyStand by hiroi-sora 2 | 3 | [skywind3000/PyStand](https://github.com/skywind3000/PyStand) 是一个非常好用的Python独立部署环境,使用一个小巧的exe作为启动器拉起Python运行。可让Python代码在一台没有安装任何环境的机器上跑起来,极大方便了Python程序的打包和发布。 4 | 5 | 本项目基于原项目做了一些改动,在README中记录了一些我的经验和遇到的坑,并附带了一套使用 VS Code 开发内嵌式Python的解决方法(拥有完整的语法提示和调试功能)。 6 | 7 | **使用案例: [Umi-OCR](https://github.com/hiroi-sora/Umi-OCR)** 8 | 9 | --- 10 | 11 | 本文可以指导小白通过PyStand部署项目,自定义项目名称和路径,享受愉快的Python开发体验。完事了可以轻松打包带走。不会C++或者Cmake也木有关系,这里提供了一键编译脚本。 12 | 13 | 不妨先看看 [原项目](https://github.com/skywind3000/PyStand) 的Readme。 14 | 15 | 下面将详细介绍本项目的特点和使用方法。 16 | 17 | ## 将环境初始化转移到外部脚本 18 | 19 | 在原版PyStand中,运行环境初始化是由内嵌在cpp中的一段Python代码字符串负责,这个字符串会作为`-c`启动参数传给Python解释器。所以如果想自己设置运行环境会比较麻烦,要重新编译exe。 20 | 21 | 为了便于自定义和修改环境配置(比如指定包的路径),我将环境初始化这部分工作转移到外部的启动脚本.py中进行。同时,这样可以兼顾使用 VS Code 在完全相同的环境中进行开发和调试。 22 | 23 | ## 自定义启动脚本及解释器路径 24 | 25 | 在原版PyStand中,启动脚本是与exe同级的 `PyStand.int` 或同名int文件。Python解释器路径则默认是`runtime`目录。 26 | 27 | 而在我的版本中,需要在 `PyStand.cpp` 开头指定启动脚本和Python解释器路径: 28 | 29 | - `PYSTAND_STATIC_PATH` :启动脚本路径 30 | - `PYSTAND_RUNTIME_PATH` :运行环境目录(即Python解释器的目录) 31 | 32 | 例: 33 | ``` 34 | #define PYSTAND_STATIC_PATH L"\\AppData\\main.py" 35 | #define PYSTAND_RUNTIME_PATH "\\AppData\\.runtime" 36 | ``` 37 | 38 | 上面这个例子中,我把解释器和脚本统统都放在 `AppData` 这个内容目录中,这样可以做到程序根目录下只有两个文件(夹),更加简洁优雅。 39 | 40 | 建议将内容目录改成你项目的名字便于用户区分。比如项目叫 MyApp ,那么文件夹可以叫 `MyApp-data` 。 41 | 42 | 示例,可以是这样的结构(假设不封包): 43 | ``` 44 | MyApp 45 | ├─ MyApp-data 46 | │ ├─ .runtime 47 | │ │ └─ Python解释器扔在这~ 48 | │ ├─ .site-packages 49 | │ │ ├─ PyQt放在这~ 50 | │ │ └─ 其他Python模块也丢在这~ 51 | │ ├─ app 52 | │ │ └─ 程序逻辑 53 | │ ├─ assets 54 | │ │ └─ 资源文件 55 | │ ├─ ui 56 | │ │ └─ 界面文件 57 | │ ├─ utils 58 | │ │ └─ 通用工具 59 | │ └─ main.py 60 | └─ MyApp.exe 61 | ``` 62 | 63 | 当然,如果考虑封包和加密的话则可能不适用这种目录结构,可以参考原作者的 [文章](https://www.zhihu.com/question/48776632/answer/2336654649) 。 64 | 65 | ## 编译 PyStand.exe 66 | 67 | 配置好自己的启动脚本及解释器路径后就可以编译生成exe了,编译时可以指定生成32位还是64位程序。32位程序只能使用32位的Python解释器及模块(PyQt),64位同理。 68 | 69 | 提示:QT6(PySide6)只支持64位Win10以上系统,不支持32位系统或者64位Win7。 70 | - 如果要用QT6,那么也必须编译64位PyStand。 71 | - 如果要兼容Win7,那么必须使用QT5。可配合32位或64位PyStand。 72 | 73 | #### 前期准备: 74 | 75 | 1. 安装Cmake、VS 2019。 76 | 2. 替换 `appicon.ico` 为你的程序图标。 77 | 78 | #### 一键编译脚本: 79 | 80 | 运行 `build.bat` 即可,会自动编译为32位程序。 81 | 82 | 这个脚本将在`build/Release`目录下生成 `PyStand.exe` ,然后将exe拷贝到本目录下,重命名为与bat同名(`build.exe`)。 83 | 84 | 如果你想修改生成exe的名称,则改动bat的文件名即可,如改为 `我的程序.bat` 。 85 | 86 | #### 手动编译: 87 | 88 | ``` 89 | 在项目目录中打开cmd 90 | ``` 91 | 92 | 构建工程,可自己调整参数或指定编译器 93 | 94 | ```cmd 95 | # 创建 build 子目录,用于存储构建过程中生成的临时文件 96 | mkdir build 97 | 98 | # 指定构建目录为build,生成器为VS2019,生成32位exe,当前目录为根目录 99 | cmake -B build -G "Visual Studio 16 2019" -A Win32 . 100 | ``` 101 | 102 | 编译 103 | 104 | ``` 105 | # 生成到 build/Release 目录下 106 | cmake --build build --config Release 107 | ``` 108 | 109 | ## 配置Python运行和开发环境 110 | 111 | 完成以上步骤后,就可以抛开跟c++有关的东西,专注于我们的Python了。 112 | 113 | > 后续的步骤也可以转移到一台空白的机器上进行,不需要安装VS或Python。 114 | > 115 | > 比如可以在模拟生产环境/用户日常使用的环境中进行,以便及时发现兼容性问题,补充必要的运行库,及手动裁切Python模块。 116 | 117 | ### 下载Python嵌入式包 118 | 119 | 以32位为例: 120 | 121 | 1. 打开官网 https://www.python.org/downloads/windows/ 122 | 2. Ctrl+F,搜索 `Python 3.8.10` 123 | 3. `Download Windows embeddable package (32-bit)` 下载32位嵌入式包 124 | 4. 最终得到一个 `python-3.8.10-embed-win32.zip` 。 125 | 126 | ### 搭建基础Python环境 127 | 128 | 1. 在合适的位置创建你Python项目的文件夹。(小技巧:为了保证日后的兼容性,可以特意在中文及含空格目录中开发,以便及时发现和处理路径兼容性问题。如 `E:/工程/My App/` 。) 129 | 2. 将之前编译得到的 `PyStand.exe` 复制过去。直接运行,应该会弹窗报错 `无法找到Python解释器……` ,就正常。 130 | 3. 创建目录: `AppData/.runtime` ,将刚下载好的Python嵌入式包解压进入。再创建一个 `.site-packages` 预留给未来放置第三方库。形成这样的结构: 131 | ``` 132 | My Project 133 | ├─ AppData 134 | │ ├─ .site-packages 135 | │ └─ .runtime 136 | │ ├─ python38.dll 137 | │ ├─ python38.zip 138 | │ ├─ pythonw.exe 139 | │ ├─ ……一堆东西 140 | └─ PyStand.exe 141 | ``` 142 | 再运行PyStand,会弹窗报错 `无法找到启动脚本……` ,就正常。 143 | 4. 在 `AppData` 目录下写一个简单的 `main.py` : 144 | ```Python 145 | import os 146 | import sys 147 | 148 | # 重定向输出流到控制台窗口 149 | try: 150 | fd = os.open('CONOUT$', os.O_RDWR | os.O_BINARY) 151 | fp = os.fdopen(fd, 'w') 152 | sys.stdout = fp 153 | sys.stderr = fp 154 | except Exception as e: 155 | fp = open(os.devnull, 'w') 156 | sys.stdout = fp 157 | sys.stderr = fp 158 | 159 | # 向控制台输出 160 | print("Cmd: Hello PyStand!") 161 | 162 | 163 | # 定义一个消息弹窗函数 164 | def MessageBox(msg, info='Message'): 165 | import ctypes 166 | ctypes.windll.user32.MessageBoxW(None, str(msg), str(info), 0) 167 | return 0 168 | 169 | 170 | # 展示弹窗 171 | MessageBox("Msg: Hello PyStand!") 172 | ``` 173 | 直接运行,会展示弹窗 `Msg: Hello PyStand!` 。如果在控制台中运行,还会在控制台打印 `Cmd: Hello PyStand!` 。 174 | 175 | ### 搭建 VS Code 开发环境 176 | 177 | 由于我个人喜欢 VS Code ,就以它为例。如果你习惯用PyCharm或别的编辑器,应该也是可以的。 178 | 179 | #### 下载 VS Code 180 | 181 | ##### Win10/11 用户: 182 | 183 | 1. 打开官网: https://code.visualstudio.com/Download 184 | 2. 下载 System Installer x64 185 | 3. 安装过程中注意:建议勾选 `将“通过Code打开”操作添加到Windows资源管理器文件(目录)上下文菜单` 186 | 187 | ##### Win7 用户: 188 | 189 | - `1.70.3`版是最后一个支持win7的版本,请在下述地址下载。(选Windows System) 190 | - https://code.visualstudio.com/updates/v1_70 191 | 192 | #### 安装插件 193 | 194 | 1. 打开 VS Code ,按 `Ctrl+Shift+X` 打开插件商店,搜索并安装 `Python` 。这是辅助语法高亮的插件。 195 | 2. 如果有需要,搜索并安装 `Chinese (Simplified)` 语言插件。 196 | 3. 关闭 VS Code 。 197 | 198 | #### 配置环境 199 | 200 | 1. 在上文创建好的项目目录(假设是`My Project`)中,将本项目的 `VSCode Environment` 中的 `.vscode` 目录复制过去。形成这样的结构: 201 | ``` 202 | My Project 203 | ├─ .vscode 204 | │ ├─ launch.json 205 | │ └─ settings.json 206 | ├─ AppData 207 | │ └─ ...... 208 | └─ PyStand.exe 209 | ``` 210 | 2. 文件管理器在 `My Project` 中,右键 → 通过Code打开。 211 | 3. VS Code中,点左上角`文件`→`将工作区另存为`,保存到 `.vscode` 目录中或者任意你喜欢的目录。以后你再次打开VS Code时,就可以直接在欢迎页面打开这个工作区了。 212 | 4. 修改一下 `.vscode` 目录中两个json配置文件,将启动脚本、解释器和第三方库的路径改为你自己的。需要修改的地方,已经用`//【改】……`的注释标出来了。其他条目就不要乱动。 213 | 5. 点F5调试程序。如果有弹窗 `Msg: Hello PyStand!` ,且VS Code内部的控制台打印了 `Cmd: Hello PyStand!` ,就说明VS Code开发环境已经配置好了。 214 | 215 | ### 安装pip 216 | 217 | > 若你的机器上已经安装过常规版本Python可以跳过这一部分,用常规Python即可。 218 | 219 | 嵌入式Python默认是不带pip的。PyStand也不鼓励通过pip来安装模块,我们推荐手动配置和维护第三方模块。 220 | 221 | 不过,我们仍需要通过pip来方便下载第三方模块,为此,需要在嵌入式Python环境中安装pip。请参考以下步骤。 222 | 223 | (记得挂好科学上网。) 224 | 225 | 1. 为了不影响原始环境,我们可以复制一份环境(`.runtime`文件夹)放在另外的目录下,命名为`runpip`。这份环境专门用来下载模块,与你的项目本体并不关联。 226 | 2. 安装pip,在控制台中执行下列操作:(建议在VS Code内置控制台进行) 227 | ``` 228 | # 下载pip安装脚本到当前目录下 229 | curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py 230 | 231 | # 用runpip环境中的Python解释器运行该脚本 232 | runpip/python.exe get-pip.py 233 | ``` 234 | 如果最后输出类似 `Successfully installed pip-xxx` 说明安装成功了。 235 | 3. 添加路径:打开 `runpip/python38._pth` ,把`#import site`前面的#删掉,变成 `import site` 。保存。 236 | 237 | ### 下载模块 238 | 239 | 以 PySide2 为例,讲解如何下载、安装和使用模块。 240 | 241 | 1. 通过pip下载PySide2包(而不安装)。注意平台是win32。需要下载别的模块的话,将指令最后的PySide2改为你需要的包名。 242 | ``` 243 | runpip/python.exe -m pip download --only-binary=:all: --platform win32 PySide2 244 | ``` 245 | 2. 在runpip的同级目录就可以看到下载好了两个包。用压缩软件(如7z,winrar)可以直接打开它们,或者后缀改为.zip也能打开。 246 | ``` 247 | ├─ PySide2-5.15.2.1-5.15.2-cp35.cp36.cp37.cp38.cp39.cp310-none-win32.whl 248 | └─ shiboken2-5.15.2.1-5.15.2-cp35.cp36.cp37.cp38.cp39.cp310-none-win32.whl 249 | ``` 250 | 3. 在你的项目的AppData下(即`.runtime`同级目录)新建一个文件夹`.site-packages`,将以上两个包的本体解压进去。 251 | (比如`PySide2`是本体,必须导入;`PySide2-5.15.2.1.dist-info`则不需要。`shiboken2`也是同理。) 252 | 253 | ### 在代码中导入模块 254 | 255 | 将 main.py 的内容全删了,粘贴以下内容: 256 | 257 |
258 | 点击展开 259 | 260 | ```Python 261 | def initRuntimeEnvironment(startup_script): 262 | """初始化运行环境。startup_script: 启动脚本路径""" 263 | import os 264 | import sys 265 | import site 266 | 267 | # 重定向输出流到控制台窗口 268 | try: 269 | fd = os.open('CONOUT$', os.O_RDWR | os.O_BINARY) 270 | fp = os.fdopen(fd, 'w') 271 | sys.stdout = fp 272 | sys.stderr = fp 273 | except Exception as e: 274 | fp = open(os.devnull, 'w') 275 | sys.stdout = fp 276 | sys.stderr = fp 277 | 278 | # 定义一个最简单的消息弹窗 279 | def MessageBox(msg, info='Message'): 280 | import ctypes 281 | ctypes.windll.user32.MessageBoxW(None, str(msg), str(info), 0) 282 | return 0 283 | os.MessageBox = MessageBox 284 | 285 | # 初始化工作目录和Python搜索路径 286 | script = os.path.abspath(startup_script) # 启动脚本.py的路径 287 | home = os.path.dirname(script) # 工作目录 288 | os.chdir(home) # 重新设定工作目录(不在最顶层,而在UmiOCR-data文件夹下) 289 | for n in ['.', '.site-packages']: # 将模块目录添加到 Python 搜索路径中 290 | path = os.path.abspath(os.path.join(home, n)) 291 | if os.path.exists(path): 292 | site.addsitedir(path) 293 | 294 | # 初始化Qt搜索路径,采用相对路径,避免中文路径编码问题 295 | try: 296 | from PySide2.QtCore import QCoreApplication 297 | QCoreApplication.addLibraryPath('./.site-packages/PySide2/plugins') 298 | except Exception as e: 299 | print(e) 300 | os.MessageBox(f'addLibraryPath fail!\n\n{e}') 301 | os._exit(0) 302 | print('Init runtime environment complete!') 303 | 304 | 305 | if __name__ == '__main__': 306 | initRuntimeEnvironment(__file__) # 初始化运行环境 307 | 308 | ``` 309 | 310 |

311 | 312 | 在VS Code编辑器中,如果 `from PySide2.QtCore import QCoreApplication` 这一句有语法高亮,没有波浪线,说明VS Code已经识别了第三方库。 313 | 314 | 按F5运行一下,如果输出`Init runtime environment complete!`,说明第三方库PySide2已经成功导入。 315 | 316 | 上述代码连带解决了PySide(PyQt同理)在中文路径下无法使用的问题。如果你不需要导入Qt,则将代码中“`初始化Qt搜索路径`”那段 try……except…… 删掉即可。 317 | 318 | 一个非常重要的事项:上述代码将程序的工作目录修改为了当前所在的路径(即`main.py`同级目录),而不是PyStand.exe所在的目录。如果你需要在代码中使用相对路径,要特别注意这一点。 319 | 320 | ### PyQt Hello World 321 | 322 | 在 `AppData` 下编写你项目的正式代码。举个例子,假设直接创建一个 `app.py` : 323 | 324 | ```python 325 | from PySide2.QtWidgets import QApplication, QLabel 326 | 327 | app = QApplication([]) 328 | label = QLabel('Hello World!') 329 | label.setFixedSize(800, 500) 330 | label.show() 331 | 332 | app.exec_() 333 | ``` 334 | 335 | 然后,在初始化脚本 `main.py` 的最后来引用它: 336 | 337 | ```python 338 | if __name__ == '__main__': 339 | initRuntimeEnvironment(__file__) # 初始化运行环境 340 | 341 | # 进入程序正式入口 342 | import app 343 | ``` 344 | 345 | 按F5运行一下试试,窗口显示出来了。 346 | 347 | ### QML Hello World 348 | 349 | 如果在中文路径运行qml,除了需要在main.py中初始化Qt搜索路径,还需要在创建qml引擎时初始化qml库路径。如下: 350 | 351 | app.py 352 | ```python 353 | import os 354 | import sys 355 | from PySide2.QtGui import QGuiApplication 356 | from PySide2.QtQml import QQmlApplicationEngine 357 | 358 | app = QGuiApplication(sys.argv) 359 | engine = QQmlApplicationEngine() 360 | engine.addImportPath("./.site-packages/PySide2/qml") # 相对路径重新导入包 361 | current_dir = os.path.dirname(os.path.abspath(__file__)) # 同级目录 362 | engine.load(f"{current_dir}/app.qml") # 启动同级目录下的app.qml 363 | if not engine.rootObjects(): 364 | sys.exit(-1) 365 | sys.exit(app.exec_()) 366 | ``` 367 | 368 | app.qml 369 | ```qml 370 | import QtQuick 2.15 371 | import QtQuick.Window 2.15 372 | 373 | Window { 374 | visible: true 375 | width: 800 376 | height: 500 377 | title: "Hello World!" 378 | 379 | Text { 380 | text: "Hello World!" 381 | anchors.centerIn: parent 382 | } 383 | } 384 | ``` 385 | 386 | 为了给qml提供语法补全,可以在VS Code插件商店中搜索并安装 `QML`、`QML Snippets` 等插件。 387 | 388 | ### 调试与发布 389 | 390 | 调试直接在VS Code中F5运行,支持断点等功能。发布则直接将代码连同exe压缩成一个文件即可,当然你也可以进一步封包和加密。 391 | 392 | 使用本方案的好处是调试与发布是完全相同的环境,VS Code调用了同一个Python解释器和第三方库文件,可以在开发阶段就检查出兼容性问题。 393 | 394 | ### 手动裁切模块 395 | 396 | 待填坑………… 397 | -------------------------------------------------------------------------------- /VSCode Environment/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: 当前文件", 9 | "type": "python", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/AppData/main.py", // 【改】启动脚本的位置 12 | "cwd": "${workspaceFolder}", // 工作目录,与启动器exe一致 13 | "console": "integratedTerminal", 14 | "justMyCode": true, 15 | "env": { 16 | "PYDEVD_DISABLE_FILE_VALIDATION": "1", // 禁用验证 17 | }, 18 | "python": "${workspaceFolder}/AppData/.runtime/python.exe" // 【改】解释器路径 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /VSCode Environment/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // 指定第三方库的代码提示和自动补全 3 | // 【改】第三方库的目录 4 | "python.autoComplete.extraPaths": [ 5 | "./AppData/.site-packages" 6 | ], 7 | "python.analysis.extraPaths": [ 8 | "./AppData/.site-packages" 9 | ], 10 | "editor.suggest.showWords": true, 11 | "cmake.configureOnOpen": false, 12 | // 隐藏文件和目录 13 | "files.exclude": { 14 | "**/__pycache__": true, 15 | "**/.runtime": true, 16 | "**/**.exe": true 17 | }, 18 | } -------------------------------------------------------------------------------- /appicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hiroi-sora/PyStand-Sora/524d6df24a5631d45f37836a72a3137d9afb9a4f/appicon.ico -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @REM 若build目录不存在 2 | if not exist build ( 3 | 4 | @REM 创建构建目录 5 | mkdir build 6 | 7 | @REM 指定构建目录为build,生成器为VS2019,生成32位exe,当前目录为根目录 8 | cmake -B build -G "Visual Studio 16 2019" -A Win32 . 9 | 10 | ) 11 | 12 | @REM 构建生成到 build/Release 目录下 13 | cmake --build build --config Release 14 | 15 | @REM 将生成的exe复制到本目录,改名为与这个bat同名 16 | copy build\Release\PyStand.exe "%~n0".exe -------------------------------------------------------------------------------- /resource.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "appicon.ico" 2 | 3 | --------------------------------------------------------------------------------