Skip to content

Commit 04bb0bb

Browse files
committed
Package builder update
1 parent 72be0a1 commit 04bb0bb

3 files changed

Lines changed: 94 additions & 37 deletions

File tree

SerialUI.spec

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Add folders you want to bundle
22
import os
33
from PyInstaller.building.build_main import Analysis, PYZ, EXE, COLLECT, Tree
4+
from config import USE_FASTPLOTLIB
45

56
block_cipher = None
67

@@ -10,16 +11,71 @@ proj_root = os.path.abspath(os.getcwd())
1011
# Entry point of your app:
1112
entry_script = os.path.join(proj_root, "SerialUI.py")
1213

14+
datas = [
15+
("assets", "assets"),
16+
("README.md", "."),
17+
]
18+
19+
excludes = [
20+
# Ensure only PyQt6 is collected. The source contains PyQt5 fallback imports,
21+
# but frozen builds should ship exactly one Qt binding.
22+
"PyQt5",
23+
"PyQt5.sip",
24+
"PySide2",
25+
"PySide6",
26+
# Exclude GUI stacks not used by this Qt app.
27+
"IPython",
28+
"ipykernel",
29+
"gi",
30+
"gi.repository",
31+
"tkinter",
32+
"PIL.ImageTk",
33+
# Avoid GTK/Tk backend pull-in from matplotlib in frozen Qt app.
34+
"matplotlib",
35+
"matplotlib.backends.backend_gtk3",
36+
"matplotlib.backends.backend_gtk3agg",
37+
"matplotlib.backends.backend_gtk3cairo",
38+
"matplotlib.backends.backend_gtk4",
39+
"matplotlib.backends.backend_gtk4agg",
40+
"matplotlib.backends.backend_gtk4cairo",
41+
"matplotlib.backends.backend_tkagg",
42+
"matplotlib.backends.backend_tkcairo",
43+
]
44+
45+
# In default config we do not use fastplotlib. Exclude its heavy dependency stack
46+
# to prevent multi-GB Linux bundles from optional CUDA/OpenCV/media packages.
47+
if not USE_FASTPLOTLIB:
48+
excludes += [
49+
"fastplotlib",
50+
"pygfx",
51+
"wgpu",
52+
"rendercanvas",
53+
"imgui_bundle",
54+
"cv2",
55+
"imageio",
56+
"imageio_ffmpeg",
57+
"av",
58+
"uharfbuzz",
59+
"wx",
60+
"vtk",
61+
"pandas",
62+
"scipy",
63+
"dask",
64+
"zarr",
65+
"numcodecs",
66+
"h5py",
67+
"botocore",
68+
"sphinx",
69+
"pytest",
70+
"numba",
71+
"llvmlite",
72+
]
1373

1474
a = Analysis(
1575
[entry_script],
1676
pathex=[proj_root],
1777
binaries=[],
18-
datas=[
19-
("assets", "assets"),
20-
("docs", "docs"),
21-
("helpers", "helpers"),
22-
],
78+
datas=datas,
2379
hiddenimports=[
2480
# Add any modules here that PyInstaller might miss, e.g.:
2581
# "pkg_resources.py2_warn",
@@ -33,30 +89,7 @@ a = Analysis(
3389
"gi": {"icons": [], "themes": [], "languages": []},
3490
},
3591
runtime_hooks=[],
36-
# Ensure only PyQt6 is collected. The source contains PyQt5 fallback imports,
37-
# but frozen builds should ship exactly one Qt binding.
38-
excludes=[
39-
"PyQt5",
40-
"PyQt5.sip",
41-
"PySide2",
42-
"PySide6",
43-
# Exclude GUI stacks not used by this Qt app.
44-
"IPython",
45-
"ipykernel",
46-
"gi",
47-
"gi.repository",
48-
"tkinter",
49-
"PIL.ImageTk",
50-
# Avoid GTK backend pull-in from matplotlib in frozen Qt app.
51-
"matplotlib.backends.backend_gtk3",
52-
"matplotlib.backends.backend_gtk3agg",
53-
"matplotlib.backends.backend_gtk3cairo",
54-
"matplotlib.backends.backend_gtk4",
55-
"matplotlib.backends.backend_gtk4agg",
56-
"matplotlib.backends.backend_gtk4cairo",
57-
"matplotlib.backends.backend_tkagg",
58-
"matplotlib.backends.backend_tkcairo",
59-
],
92+
excludes=excludes,
6093
win_no_prefer_redirects=False,
6194
win_private_assemblies=False,
6295
cipher=block_cipher,
@@ -77,7 +110,7 @@ exe = EXE(
77110
name="SerialUI",
78111
debug=False,
79112
bootloader_ignore_signals=False,
80-
strip=False,
113+
strip=True,
81114
upx=True,
82115
console=False, # set False if you want a pure GUI app (no console window)
83116
icon=os.path.join("assets", "icon_96.ico"),
@@ -88,7 +121,7 @@ coll = COLLECT(
88121
a.binaries,
89122
a.zipfiles,
90123
a.datas,
91-
strip=False,
124+
strip=True,
92125
upx=True,
93126
upx_exclude=[],
94127
name="SerialUI",

build_release.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,24 @@ require_dir "${HELPERS_DIR}"
5959
require_file "${ROOT_DIR}/SerialUI.spec"
6060
require_file "${HELPERS_DIR}/setup.py"
6161

62+
log "Checking Python environment isolation"
63+
if "${PYTHON_BIN}" - <<'PY'
64+
import site, sys
65+
prefix = sys.prefix.rstrip("/")
66+
base = getattr(sys, "base_prefix", "").rstrip("/")
67+
paths = [p for p in sys.path if "site-packages" in p or "dist-packages" in p]
68+
uses_system = any(
69+
p.startswith("/usr/lib") or p.startswith("/usr/local/lib")
70+
for p in paths
71+
)
72+
raise SystemExit(0 if uses_system else 1)
73+
PY
74+
then
75+
echo "WARNING: Python environment includes system site-packages."
76+
echo " This can make PyInstaller bundles very large by pulling unrelated packages."
77+
echo " Recommended: build in a clean venv with include-system-site-packages = false."
78+
fi
79+
6280
log "Installing/upgrading build tools"
6381
run "${PYTHON_BIN}" -m pip install --upgrade pip build twine pyinstaller pybind11 setuptools wheel
6482

helpers/Qgraph_helper.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import re
3535
import textwrap
3636
import sys
37+
import importlib
3738
from math import pi, floor, ceil, isfinite, floor, log10, isclose
3839
from typing import Optional
3940
import numpy as np
@@ -83,13 +84,17 @@ def _wrap(func):
8384
#
8485
# Fastplotlib
8586
# ----------------------------------------
87+
fpl = None
88+
Legend = None
89+
pygfx = None
8690
if USE_FASTPLOTLIB:
8791
if DEBUGFASTPLOTLIB:
8892
tic = time.perf_counter()
8993
try:
90-
import fastplotlib as fpl
91-
from fastplotlib.legends import Legend
92-
import pygfx
94+
# Keep optional fastplotlib stack dynamic to avoid freezing unused heavy deps.
95+
fpl = importlib.import_module("fastplotlib")
96+
Legend = importlib.import_module("fastplotlib.legends").Legend
97+
pygfx = importlib.import_module("pygfx")
9398
except Exception:
9499
USE_FASTPLOTLIB = False
95100
if DEBUGFASTPLOTLIB:
@@ -101,7 +106,8 @@ def _wrap(func):
101106
tic = time.perf_counter()
102107
import pyqtgraph as pg
103108
from pyqtgraph import PlotWidget
104-
import pyqtgraph.exporters as pgxr
109+
from pyqtgraph.exporters.ImageExporter import ImageExporter
110+
from pyqtgraph.exporters.SVGExporter import SVGExporter
105111
from pyqtgraph.graphicsItems.PlotDataItem import PlotDataItem
106112
from pyqtgraph.graphicsItems.GraphicsObject import GraphicsObject
107113
VALID_PG_LEGENDITEM = (PlotDataItem, GraphicsObject)
@@ -3397,9 +3403,9 @@ def on_pushButton_ChartSaveFigure(self) -> None:
33973403
# PyQtGraph: choose vector vs raster exporter
33983404
ext = file_path.suffix.lower()
33993405
if ext == ".svg":
3400-
exporter = pgxr.SVGExporter(self.chartWidgetPG.getPlotItem())
3406+
exporter = SVGExporter(self.chartWidgetPG.getPlotItem())
34013407
else:
3402-
exporter = pgxr.ImageExporter(self.chartWidgetPG.getPlotItem())
3408+
exporter = ImageExporter(self.chartWidgetPG.getPlotItem())
34033409
exporter.export(str(file_path))
34043410
else:
34053411
# Use FastPlotLib exporter, needs imageio

0 commit comments

Comments
 (0)