Skip to content

Commit 969419d

Browse files
albertchen857claude
andcommitted
macOS 版本:打包脚本、图标、Release 工作流、issue #7 说明
- README_MACOS.md:安装、Gatekeeper 绕过、自己编译,以及 issue #7 里 easygui 和 pyopengltk 两个报错为什么在 0.9.0 里不会再出现 - macos/build.sh:一条命令打出 .app 和 .dmg,顺便检查 tkinter 在不在 - macos/make_icns.py:从 icon.ico 生成 macOS 要的 .icns - .github/workflows/macos-release.yaml:Intel + Apple Silicon 两个 dmg, 打 v* 标签时发 Release - LitematicaViewer.spec 首次入库(之前被 .gitignore 的 *.spec 挡住了), 带 BUNDLE 段、文件关联和 argv_emulation - .gitignore:修正 !LitematicaViewer.spec(原来写了行尾注释,gitignore 不支持) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent bedd45a commit 969419d

4 files changed

Lines changed: 287 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: macOS Release
2+
3+
# 只在这个分支手动触发,或者打 v* 标签时跑。
4+
# 产出 Intel 和 Apple Silicon 两个 dmg,打标签时顺便发一个 Release。
5+
on:
6+
workflow_dispatch:
7+
push:
8+
tags: ["v*"]
9+
10+
permissions:
11+
contents: write
12+
13+
jobs:
14+
build:
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
include:
19+
- os: macos-13 # Intel
20+
arch: x64
21+
- os: macos-14 # Apple Silicon
22+
arch: arm64
23+
24+
runs-on: ${{ matrix.os }}
25+
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
- uses: actions/setup-python@v5
30+
with:
31+
python-version: "3.12"
32+
33+
- name: Install dependencies
34+
run: |
35+
python -m pip install --upgrade pip
36+
pip install -r requirements.txt
37+
pip install pyinstaller
38+
39+
- name: Check tkinter
40+
run: python -c "import tkinter; print('tkinter', tkinter.TkVersion)"
41+
42+
- name: Make .icns
43+
run: python macos/make_icns.py
44+
45+
- name: Compile translations
46+
run: python tools/lang_build.py
47+
48+
- name: Self test
49+
run: |
50+
python tools/lang_check.py
51+
python -c "import sys; sys.path.insert(0,'script'); import lv.ui_main; print('modules ok')"
52+
53+
- name: Build .app
54+
run: pyinstaller --noconfirm LitematicaViewer.spec
55+
56+
- name: Build .dmg
57+
run: |
58+
cd dist
59+
hdiutil create -volname LitematicaViewer \
60+
-srcfolder LitematicaViewer.app \
61+
-ov -format UDZO LitematicaViewer-${{ matrix.arch }}.dmg
62+
63+
- uses: actions/upload-artifact@v4
64+
with:
65+
name: LitematicaViewer-macos-${{ matrix.arch }}
66+
path: dist/*.dmg
67+
68+
- name: Attach to release
69+
if: startsWith(github.ref, 'refs/tags/')
70+
uses: softprops/action-gh-release@v2
71+
with:
72+
files: dist/*.dmg
73+
body: |
74+
macOS 版本没有 Apple 开发者证书签名,第一次打开需要右键 → 打开,
75+
或者执行 `xattr -dr com.apple.quarantine /Applications/LitematicaViewer.app`。
76+
详见 README_MACOS.md。

README_MACOS.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
[简体中文](./README.md) | [English](./README_EN.md) | **macOS**
2+
3+
# LitematicaViewer on macOS
4+
5+
这个分支放 macOS 相关的打包和文档。代码本身和 `main` 一致 —— 0.9.0 已经把
6+
平台相关的东西都处理掉了,不需要单独维护一份 macOS 代码。
7+
8+
## 直接用
9+
10+
[Releases](https://github.com/albertchen857/LitematicaViewer/releases) 下载
11+
`LitematicaViewer-arm64.dmg`(Apple Silicon)或 `LitematicaViewer-x64.dmg`(Intel),
12+
拖进「应用程序」。
13+
14+
**第一次打开会被拦住**,因为这个 app 没有花钱买 Apple 开发者证书签名。两种办法:
15+
16+
- 在「应用程序」里**右键 → 打开**,弹窗里再点一次「打开」;或者
17+
- 终端里执行 `xattr -dr com.apple.quarantine /Applications/LitematicaViewer.app`
18+
19+
## 自己编译
20+
21+
```bash
22+
git clone https://github.com/albertchen857/LitematicaViewer.git
23+
cd LitematicaViewer
24+
git checkout macos
25+
chmod +x macos/build.sh
26+
./macos/build.sh
27+
```
28+
29+
产物在 `dist/`
30+
31+
不想打包、只想跑源码:
32+
33+
```bash
34+
pip3 install -r requirements.txt
35+
python3 script/LitematicaViewer.py
36+
```
37+
38+
## 关于 issue #7 报的两个错
39+
40+
[#7](https://github.com/albertchen857/LitematicaViewer/issues/7) 里 Python 3.12 / macOS 15.3.2
41+
遇到的两个报错,0.9.0 都从根上解决了:
42+
43+
### `ModuleNotFoundError: No module named 'easygui'`
44+
45+
easygui 是个已经好几年没更新的库,在新版 Python 上装不上。它在旧版里只用来弹
46+
几个对话框(选主题色、选字体、确认框)。
47+
48+
**0.9.0 把 easygui 整个去掉了**,对话框换成 Python 自带的 `tkinter.messagebox`
49+
和一个自己写的列表选择窗口。少一个依赖,也少一个装不上的坑。
50+
51+
### `ImportError: cannot import name 'OpenGLFrame' from partially initialized module 'pyopengltk'`
52+
53+
pyopengltk 靠平台相关的后端把 OpenGL 上下文塞进 Tk 画布里,它的 macOS(Cocoa)
54+
分支基本没人维护,导入时就会挂。旧版的 3D 渲染和左侧那个小预览框都依赖它。
55+
56+
**0.9.0 把 pyopengltk 和 pyopengl 一起去掉了**,3D 渲染改成用
57+
[deepslate](https://github.com/misode/deepslate) 的 WebGL 渲染器,在一个独立子进程的
58+
pywebview 窗口里跑。副作用是渲染效果反而更好了 —— 之前是「取贴图主色画个纯色方块」,
59+
现在是真的按方块模型和贴图渲染。
60+
61+
## macOS 上还需要注意的
62+
63+
**tkinter 不是 Python 自带的。** 如果 `python3 -c "import tkinter"` 报错:
64+
65+
```bash
66+
brew install python-tk # Homebrew 的 Python
67+
# 或者直接装 python.org 的官方安装包,自带 tkinter
68+
```
69+
70+
**pywebview 用的是系统的 WKWebView**,不用额外装浏览器。但 3D 渲染第一次运行需要联网,
71+
因为 deepslate 和 gl-matrix 是从 unpkg CDN 拉的。想离线用就把这两个库下到
72+
`script/JSrender/resource/`,再改 `script/JSrender/viewer.html` 里的两个 `<script src>`
73+
74+
**文件关联**`.app` 的 Info.plist 里声明了 `.litematic` / `.nbt` / `.mcstructure`
75+
所以可以直接双击结构文件打开,也可以把文件拖到 Dock 图标上。
76+
`argv_emulation` 只在 macOS 打包时开启,见 `LitematicaViewer.spec`。)
77+
78+
## 代码里的平台分支
79+
80+
0.9.0 之前有几处写死了 Windows API,在 Mac 上必崩。现在都封装在
81+
`script/lv/config.py` 里:
82+
83+
| 函数 | 干什么 | macOS 上走哪条 |
84+
|---|---|---|
85+
| `set_icon(window)` | 设窗口图标 | `iconbitmap(.ico)` 是 Windows 专有,Mac 上用 PIL 转 PNG 走 `iconphoto` |
86+
| `maximize(window)` | 最大化 | `state("zoomed")``-zoomed` 在 Mac 上都无效,改成按屏幕尺寸设 geometry |
87+
| `reveal(path)` | 打开文件/文件夹 | `os.startfile` 只有 Windows 有,Mac 上用 `open` |
88+
| `user_file(name)` | 日志写哪 | 打包后写在 app 旁边,不写只读的 `_MEIPASS` |

macos/build.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env bash
2+
# 在 macOS 上一条命令打出 LitematicaViewer.app 和 .dmg
3+
#
4+
# chmod +x macos/build.sh
5+
# ./macos/build.sh
6+
#
7+
# 产物在 dist/ 下。
8+
9+
set -euo pipefail
10+
cd "$(dirname "$0")/.."
11+
12+
PYTHON="${PYTHON:-python3}"
13+
14+
echo "==> Python: $($PYTHON --version)"
15+
16+
# tkinter 不是 Python 自带的,官方 python.org 安装包里有,Homebrew 的要单独装
17+
if ! $PYTHON -c "import tkinter" 2>/dev/null; then
18+
echo "!! 这个 Python 没有 tkinter。"
19+
echo " Homebrew 用户: brew install python-tk"
20+
echo " 或者装 python.org 的官方安装包(自带 tkinter)"
21+
exit 1
22+
fi
23+
24+
echo "==> 安装依赖"
25+
$PYTHON -m pip install --upgrade pip
26+
$PYTHON -m pip install -r requirements.txt
27+
$PYTHON -m pip install pyinstaller
28+
29+
echo "==> 生成 .icns 图标"
30+
$PYTHON macos/make_icns.py || echo " 图标生成失败,用默认图标继续"
31+
32+
echo "==> 编译语言文件"
33+
$PYTHON tools/lang_build.py
34+
35+
echo "==> 自检"
36+
$PYTHON tools/lang_check.py
37+
38+
echo "==> 打包"
39+
rm -rf build dist
40+
$PYTHON -m PyInstaller --noconfirm LitematicaViewer.spec
41+
42+
echo "==> 打 dmg"
43+
cd dist
44+
hdiutil create -volname LitematicaViewer \
45+
-srcfolder LitematicaViewer.app \
46+
-ov -format UDZO "LitematicaViewer-$(uname -m).dmg"
47+
48+
echo
49+
echo "完成:"
50+
ls -lh LitematicaViewer*.dmg
51+
echo
52+
echo "第一次打开会被 Gatekeeper 拦(未签名),右键 → 打开,或者执行:"
53+
echo " xattr -dr com.apple.quarantine /Applications/LitematicaViewer.app"

macos/make_icns.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""把 icon.ico 转成 macOS 用的 icon.icns。
2+
3+
python macos/make_icns.py
4+
5+
macOS 上会调用系统自带的 iconutil 生成真正的 .icns。
6+
其它系统上只会生成中间的 icon.iconset/ 目录并提示你 —— 因为 .icns 的打包
7+
必须靠 iconutil,Windows/Linux 上没有这个工具。
8+
没有 .icns 也能打包,只是 Dock 图标会是 PyInstaller 的默认图标。
9+
"""
10+
11+
import os
12+
import subprocess
13+
import sys
14+
15+
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
16+
SOURCE = os.path.join(ROOT, "icon.ico")
17+
ICONSET = os.path.join(ROOT, "icon.iconset")
18+
TARGET = os.path.join(ROOT, "icon.icns")
19+
20+
# .iconset 里的文件名是固定的,iconutil 靠名字识别尺寸
21+
SIZES = [
22+
(16, "icon_16x16.png"), (32, "icon_16x16@2x.png"),
23+
(32, "icon_32x32.png"), (64, "icon_32x32@2x.png"),
24+
(128, "icon_128x128.png"), (256, "icon_128x128@2x.png"),
25+
(256, "icon_256x256.png"), (512, "icon_256x256@2x.png"),
26+
(512, "icon_512x512.png"), (1024, "icon_512x512@2x.png"),
27+
]
28+
29+
30+
def main() -> int:
31+
from PIL import Image
32+
33+
if not os.path.exists(SOURCE):
34+
print(f"找不到 {SOURCE}")
35+
return 1
36+
37+
source = Image.open(SOURCE)
38+
# .ico 里有多张尺寸,挑最大的那张当母版,放大出来才不糊
39+
if getattr(source, "n_frames", 1) > 1:
40+
best, best_area = source, 0
41+
for index in range(source.n_frames):
42+
source.seek(index)
43+
area = source.width * source.height
44+
if area > best_area:
45+
best, best_area = source.copy(), area
46+
source = best
47+
source = source.convert("RGBA")
48+
print(f"母版尺寸 {source.size}")
49+
if min(source.size) < 512:
50+
print(
51+
f" 注意: 母版只有 {source.width}px,放大到 512/1024 会糊。"
52+
f"想要清晰的 Dock 图标,换一张 1024x1024 的 icon.png 做母版。"
53+
)
54+
55+
os.makedirs(ICONSET, exist_ok=True)
56+
for size, name in SIZES:
57+
source.resize((size, size), Image.LANCZOS).save(os.path.join(ICONSET, name))
58+
print(f"已生成 {ICONSET}{len(SIZES)} 个尺寸)")
59+
60+
if sys.platform != "darwin":
61+
print("当前不是 macOS,跳过 iconutil。到 Mac 上再跑一次本脚本即可生成 icon.icns。")
62+
return 0
63+
64+
subprocess.run(["iconutil", "-c", "icns", ICONSET, "-o", TARGET], check=True)
65+
print(f"已生成 {TARGET}")
66+
return 0
67+
68+
69+
if __name__ == "__main__":
70+
sys.exit(main())

0 commit comments

Comments
 (0)