From e5b38e8893f5fa3668923667f85f59e29afbbcbb Mon Sep 17 00:00:00 2001 From: Miaoqi Chu Date: Tue, 14 Jul 2026 23:51:34 -0500 Subject: [PATCH 1/8] fix: use Fusion style on Windows/Linux, keep native style on macOS Qt's default style looks inconsistent/dated on Windows and Linux; Fusion gives a more uniform look there while macOS keeps its native appearance. Co-Authored-By: Claude Sonnet 5 --- src/rixsviewer/rixsviewer_gui.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rixsviewer/rixsviewer_gui.py b/src/rixsviewer/rixsviewer_gui.py index a929bc4..b2ec1dc 100644 --- a/src/rixsviewer/rixsviewer_gui.py +++ b/src/rixsviewer/rixsviewer_gui.py @@ -772,6 +772,11 @@ def main(): # Create QApplication with remaining arguments app = QApplication([sys.argv[0]] + qt_args) + if sys.platform == "darwin": + pass # keep native macOS style + else: + app.setStyle("Fusion") # Windows / Linux + icon_path = Path(__file__).parent / "assets" / "icon.png" if icon_path.exists(): app.setWindowIcon(QIcon(str(icon_path))) From fa4ac152bbb95d4a7430093a623241336ee00774 Mon Sep 17 00:00:00 2001 From: Miaoqi Chu Date: Fri, 24 Jul 2026 09:57:28 -0500 Subject: [PATCH 2/8] build: add Windows packaging entrypoint and icon --- .gitignore | 1 + packaging/windows/entrypoint.py | 4 ++++ packaging/windows/icon.ico | Bin 0 -> 44143 bytes 3 files changed, 5 insertions(+) create mode 100644 packaging/windows/entrypoint.py create mode 100644 packaging/windows/icon.ico diff --git a/.gitignore b/.gitignore index 82cd58c..698e4ac 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ MANIFEST # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec +!packaging/windows/*.spec # Installer logs pip-log.txt diff --git a/packaging/windows/entrypoint.py b/packaging/windows/entrypoint.py new file mode 100644 index 0000000..809b3ee --- /dev/null +++ b/packaging/windows/entrypoint.py @@ -0,0 +1,4 @@ +from rixsviewer.rixsviewer_gui import main + +if __name__ == "__main__": + main() diff --git a/packaging/windows/icon.ico b/packaging/windows/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..55396ba562b1099454cac5e4d910a152c70a4fed GIT binary patch literal 44143 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 literal 0 HcmV?d00001 From b4e8723672443cdad70ebed2cdc0d5e400820931 Mon Sep 17 00:00:00 2001 From: Miaoqi Chu Date: Fri, 24 Jul 2026 10:05:43 -0500 Subject: [PATCH 3/8] build: add PyInstaller spec for Windows executable --- packaging/windows/rixsviewer.spec | 45 +++++++++++++++++++++++++++++++ pyproject.toml | 1 + 2 files changed, 46 insertions(+) create mode 100644 packaging/windows/rixsviewer.spec diff --git a/packaging/windows/rixsviewer.spec b/packaging/windows/rixsviewer.spec new file mode 100644 index 0000000..13b63fb --- /dev/null +++ b/packaging/windows/rixsviewer.spec @@ -0,0 +1,45 @@ +# packaging/windows/rixsviewer.spec +# -*- mode: python ; coding: utf-8 -*- +from pathlib import Path + +spec_dir = Path(SPECPATH) +repo_root = spec_dir.parent.parent + +a = Analysis( + [str(spec_dir / 'entrypoint.py')], + pathex=[], + binaries=[], + datas=[ + (str(repo_root / 'src' / 'rixsviewer' / 'assets'), 'rixsviewer/assets'), + ], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=['epics'], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.datas, + [], + name='rixsviewer', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=False, + upx_exclude=[], + runtime_tmpdir=None, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=str(spec_dir / 'icon.ico'), +) diff --git a/pyproject.toml b/pyproject.toml index da56bce..d18f361 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ dependencies = [ epics = ["pyepics"] dev = ["pytest>=7.0.0", "black>=22.0.0", "flake8>=4.0.0"] docs = ["sphinx>=6.0.0", "sphinx-rtd-theme>=1.0.0", "numpydoc>=1.5.0"] +pyinstaller = ["pyinstaller>=6.21"] [project.scripts] rixsviewer = "rixsviewer.rixsviewer_gui:main" From e8277a5891858bc870783abf72143892be41f1b2 Mon Sep 17 00:00:00 2001 From: Miaoqi Chu Date: Fri, 24 Jul 2026 10:09:18 -0500 Subject: [PATCH 4/8] ci: build and release a portable Windows executable on tag push --- .github/workflows/publish.yml | 65 +++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2fb1791..3cfcbbf 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -6,6 +6,7 @@ on: push: tags: - "v*" + workflow_dispatch: {} jobs: build: @@ -38,6 +39,7 @@ jobs: publish: name: Publish to PyPI needs: build + if: github.event_name == 'push' runs-on: ubuntu-latest environment: name: pypi @@ -53,3 +55,66 @@ jobs: - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 + + windows-build: + name: Build Windows executable + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install project and PyInstaller + run: python -m pip install ".[pyinstaller]" + + - name: Build executable + run: pyinstaller packaging/windows/rixsviewer.spec + + - name: Rename executable with version + shell: bash + run: mv dist/rixsviewer.exe "dist/rixsviewer-${{ github.ref_name }}-windows.exe" + + - name: Smoke test executable launches + shell: pwsh + env: + QT_QPA_PLATFORM: offscreen + run: | + $exe = Get-ChildItem dist -Filter "rixsviewer-*-windows.exe" | Select-Object -First 1 + $proc = Start-Process -FilePath $exe.FullName -PassThru + Start-Sleep -Seconds 8 + if ($proc.HasExited) { + Write-Error "Executable exited early with code $($proc.ExitCode)" + exit 1 + } + Write-Host "Executable is running after 8s, smoke test passed" + Stop-Process -Id $proc.Id -Force + + - name: Upload Windows executable artifact + uses: actions/upload-artifact@v4 + with: + name: windows-exe + path: dist/rixsviewer-*-windows.exe + + github-release: + name: Publish GitHub Release + needs: windows-build + if: github.event_name == 'push' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Download Windows executable artifact + uses: actions/download-artifact@v4 + with: + name: windows-exe + path: dist/ + + - name: Create/update GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.ref_name }} + files: dist/rixsviewer-*-windows.exe From c5886c90c0bfc94847f793dc03cd467ca65d1f2c Mon Sep 17 00:00:00 2001 From: Miaoqi Chu Date: Fri, 24 Jul 2026 10:12:42 -0500 Subject: [PATCH 5/8] build: add Linux AppImage packaging scaffolding --- packaging/linux/AppRun | 3 +++ packaging/linux/entrypoint.py | 4 ++++ packaging/linux/rixsviewer.desktop | 7 +++++++ 3 files changed, 14 insertions(+) create mode 100755 packaging/linux/AppRun create mode 100644 packaging/linux/entrypoint.py create mode 100644 packaging/linux/rixsviewer.desktop diff --git a/packaging/linux/AppRun b/packaging/linux/AppRun new file mode 100755 index 0000000..6a1992b --- /dev/null +++ b/packaging/linux/AppRun @@ -0,0 +1,3 @@ +#!/bin/bash +HERE="$(dirname "$(readlink -f "${0}")")" +exec "${HERE}/usr/bin/rixsviewer/rixsviewer" "$@" diff --git a/packaging/linux/entrypoint.py b/packaging/linux/entrypoint.py new file mode 100644 index 0000000..809b3ee --- /dev/null +++ b/packaging/linux/entrypoint.py @@ -0,0 +1,4 @@ +from rixsviewer.rixsviewer_gui import main + +if __name__ == "__main__": + main() diff --git a/packaging/linux/rixsviewer.desktop b/packaging/linux/rixsviewer.desktop new file mode 100644 index 0000000..6bfb300 --- /dev/null +++ b/packaging/linux/rixsviewer.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=RixsViewer +Exec=rixsviewer +Icon=rixsviewer +Categories=Science; +Terminal=false From 19bcaca394dd726612a9050729a4a59a393255cb Mon Sep 17 00:00:00 2001 From: Miaoqi Chu Date: Fri, 24 Jul 2026 10:17:09 -0500 Subject: [PATCH 6/8] build: add PyInstaller spec for Linux AppImage --- .gitignore | 1 + packaging/linux/rixsviewer.spec | 50 +++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 packaging/linux/rixsviewer.spec diff --git a/.gitignore b/.gitignore index 698e4ac..12bdd00 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ MANIFEST *.manifest *.spec !packaging/windows/*.spec +!packaging/linux/*.spec # Installer logs pip-log.txt diff --git a/packaging/linux/rixsviewer.spec b/packaging/linux/rixsviewer.spec new file mode 100644 index 0000000..9642e79 --- /dev/null +++ b/packaging/linux/rixsviewer.spec @@ -0,0 +1,50 @@ +# packaging/linux/rixsviewer.spec +# -*- mode: python ; coding: utf-8 -*- +from pathlib import Path + +spec_dir = Path(SPECPATH) +repo_root = spec_dir.parent.parent + +a = Analysis( + [str(spec_dir / 'entrypoint.py')], + pathex=[], + binaries=[], + datas=[ + (str(repo_root / 'src' / 'rixsviewer' / 'assets'), 'rixsviewer/assets'), + ], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=['epics'], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='rixsviewer', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=False, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=False, + upx_exclude=[], + name='rixsviewer', +) From a7738099dd8c7fe1bd95e7fad9a53cbd2509dbb9 Mon Sep 17 00:00:00 2001 From: Miaoqi Chu Date: Fri, 24 Jul 2026 10:23:14 -0500 Subject: [PATCH 7/8] build: add AppDir assembly and appimagetool packaging script --- packaging/linux/build-appimage.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 packaging/linux/build-appimage.sh diff --git a/packaging/linux/build-appimage.sh b/packaging/linux/build-appimage.sh new file mode 100755 index 0000000..8c85594 --- /dev/null +++ b/packaging/linux/build-appimage.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# packaging/linux/build-appimage.sh +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")/../.." +VERSION="${1:?Usage: build-appimage.sh }" + +rm -rf build dist/rixsviewer dist/AppDir +python3.11 -m PyInstaller packaging/linux/rixsviewer.spec + +mkdir -p dist/AppDir/usr/bin +cp -r dist/rixsviewer dist/AppDir/usr/bin/rixsviewer +install -m 755 packaging/linux/AppRun dist/AppDir/AppRun +cp packaging/linux/rixsviewer.desktop dist/AppDir/rixsviewer.desktop +cp src/rixsviewer/assets/icon.png dist/AppDir/rixsviewer.png + +curl -sL -o dist/appimagetool https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage +chmod +x dist/appimagetool + +dist/appimagetool --appimage-extract-and-run dist/AppDir "dist/rixsviewer-${VERSION}-x86_64.AppImage" From 592c316b6e7a30ae767ebaae18c07cda73c5d2cd Mon Sep 17 00:00:00 2001 From: Miaoqi Chu Date: Fri, 24 Jul 2026 10:25:55 -0500 Subject: [PATCH 8/8] ci: build and release a Linux AppImage on tag push --- .github/workflows/publish.yml | 62 +++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3cfcbbf..3105f6b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -99,9 +99,59 @@ jobs: name: windows-exe path: dist/rixsviewer-*-windows.exe + linux-appimage-build: + name: Build Linux AppImage + runs-on: ubuntu-latest + container: rockylinux:9 + steps: + - name: Install git + run: dnf install -y git + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install system dependencies + run: | + dnf install -y python3.11 python3.11-pip \ + mesa-libGL mesa-libEGL libxkbcommon libxkbcommon-x11 \ + xcb-util-wm xcb-util-image xcb-util-keysyms xcb-util-renderutil \ + dbus-libs fontconfig freetype file + + - name: Install project and PyInstaller + run: python3.11 -m pip install ".[pyinstaller]" + + - name: Build AppImage + run: bash packaging/linux/build-appimage.sh "${{ github.ref_name }}" + + - name: Smoke test AppImage launches + env: + QT_QPA_PLATFORM: offscreen + run: | + APPIMAGE=$(ls dist/rixsviewer-*-x86_64.AppImage) + chmod +x "$APPIMAGE" + timeout 8 "$APPIMAGE" --appimage-extract-and-run > /tmp/run.log 2>&1 & + RPID=$! + sleep 6 + if kill -0 $RPID 2>/dev/null; then + echo "Smoke test passed: AppImage is running after 6s" + kill $RPID + else + wait $RPID || true + echo "Smoke test FAILED: AppImage exited early" + cat /tmp/run.log + exit 1 + fi + + - name: Upload Linux AppImage artifact + uses: actions/upload-artifact@v4 + with: + name: linux-appimage + path: dist/rixsviewer-*-x86_64.AppImage + github-release: name: Publish GitHub Release - needs: windows-build + needs: [windows-build, linux-appimage-build] if: github.event_name == 'push' runs-on: ubuntu-latest permissions: @@ -113,8 +163,16 @@ jobs: name: windows-exe path: dist/ + - name: Download Linux AppImage artifact + uses: actions/download-artifact@v4 + with: + name: linux-appimage + path: dist/ + - name: Create/update GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ github.ref_name }} - files: dist/rixsviewer-*-windows.exe + files: | + dist/rixsviewer-*-windows.exe + dist/rixsviewer-*-x86_64.AppImage