Skip to content

Commit 34fd9f6

Browse files
committed
Rewrite GUI as pywebview + vanilla web frontend, add Linux support
- Replace the Tkinter GUI with a modular pywebview window backed by a vanilla HTML/CSS/JS frontend (webui/), talking to Python through a small Api bridge in Text2Pen.py. - Split the backend into engine.py (drawing), input_controller.py (per-platform pointer control), storage.py, paths.py, text_parser.py and services.py, so the drawing engine no longer depends on Tk. - Add Linux support: uinput-based pointer control, platform-specific app data paths (with sudo/SUDO_USER handling), and a ScreenCast portal-based start-point picker (backend/portal_capture.py) since the real cursor position can't be read reliably on Wayland. - Fix stroke rendering: press/release the pointer once per stroke instead of once per segment, interpolate between sampled points, and widen inter-word spacing so text no longer looks like one continuous connected line. - Add a GitHub Actions workflow to build Text2Pen/Update/Installer for Windows and Linux (Installer stays Windows-only for now; it still imports win32com.client directly).
1 parent 89578ed commit 34fd9f6

27 files changed

Lines changed: 4195 additions & 1398 deletions

‎.github/workflows/build.yml‎

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
name: Build executables
2+
3+
# Workflow 1 of 2: builds the three PyInstaller executables (Text2Pen, Update,
4+
# Installer) for Windows and Linux and uploads them as separate build
5+
# artifacts. It does not touch GitHub Releases — that's workflow 2, which will
6+
# take these artifacts and publish them as individual release assets, the
7+
# same way existing releases already do (so in-place updates via Update.exe
8+
# keep working).
9+
#
10+
# `workflow_call` is included so a future release workflow can trigger a
11+
# fresh build directly instead of reusing old artifacts.
12+
on:
13+
workflow_dispatch:
14+
inputs:
15+
version:
16+
description: "Optional label appended to artifact names (e.g. v2.8)."
17+
required: false
18+
default: ""
19+
workflow_call:
20+
inputs:
21+
version:
22+
description: "Optional label appended to artifact names (e.g. v2.8)."
23+
required: false
24+
default: ""
25+
type: string
26+
27+
jobs:
28+
build:
29+
strategy:
30+
fail-fast: false
31+
matrix:
32+
include:
33+
- os: windows-latest
34+
label: windows
35+
# 22.04 rather than the rolling ubuntu-latest: PyInstaller freezes
36+
# against the build machine's glibc, and an older baseline keeps
37+
# the binary runnable on users' (likely older) systems too.
38+
- os: ubuntu-22.04
39+
label: linux
40+
runs-on: ${{ matrix.os }}
41+
steps:
42+
- uses: actions/checkout@v4
43+
44+
- uses: actions/setup-python@v5
45+
with:
46+
python-version: "3.12"
47+
48+
# pywebview renders through WebKitGTK on Linux, and the new portal
49+
# screen-capture code (backend/portal_capture.py) needs GLib/D-Bus/
50+
# GStreamer bindings. None of this is pip-installable; it has to come
51+
# from apt, on the same interpreter pip installs into (no venv here).
52+
- name: Install Linux system dependencies
53+
if: matrix.label == 'linux'
54+
run: |
55+
sudo apt-get update
56+
sudo apt-get install -y \
57+
python3-gi gir1.2-webkit2-4.1 \
58+
gir1.2-gst-plugins-base-1.0 gstreamer1.0-plugins-base \
59+
gstreamer1.0-plugins-good gstreamer1.0-pipewire \
60+
python3-dbus libgirepository1.0-dev
61+
62+
- name: Install Python dependencies
63+
run: |
64+
python -m pip install --upgrade pip
65+
pip install -r requirements.txt pyinstaller
66+
67+
- name: Build Text2Pen (Windows)
68+
if: matrix.label == 'windows'
69+
run: pyinstaller --onefile --noconsole --add-data "webui;webui" --name Text2Pen Text2Pen.py
70+
71+
- name: Build Text2Pen (Linux)
72+
if: matrix.label == 'linux'
73+
run: pyinstaller --onefile --noconsole --add-data "webui:webui" --name Text2Pen Text2Pen.py
74+
75+
- name: Build Update
76+
run: pyinstaller --onefile --noconsole --name Update Update.py
77+
78+
# Installer.py still does `import win32com.client` and reads
79+
# LOCALAPPDATA/APPDATA at module level — it's Windows-only code, not
80+
# just a Windows-only build target. PyInstaller can't resolve
81+
# win32com.client on Linux at all, so this step is Windows-only until
82+
# Installer.py itself is adapted; drop the `if:` once that's done.
83+
- name: Build Installer (Windows only for now)
84+
if: matrix.label == 'windows'
85+
run: pyinstaller --onefile --noconsole --name Installer Installer.py
86+
87+
- name: Upload Text2Pen artifact
88+
uses: actions/upload-artifact@v4
89+
with:
90+
name: Text2Pen-${{ matrix.label }}${{ inputs.version != '' && format('-{0}', inputs.version) || '' }}
91+
path: dist/Text2Pen${{ matrix.label == 'windows' && '.exe' || '' }}
92+
if-no-files-found: error
93+
94+
- name: Upload Update artifact
95+
uses: actions/upload-artifact@v4
96+
with:
97+
name: Update-${{ matrix.label }}${{ inputs.version != '' && format('-{0}', inputs.version) || '' }}
98+
path: dist/Update${{ matrix.label == 'windows' && '.exe' || '' }}
99+
if-no-files-found: error
100+
101+
- name: Upload Installer artifact
102+
if: matrix.label == 'windows'
103+
uses: actions/upload-artifact@v4
104+
with:
105+
name: Installer-${{ matrix.label }}${{ inputs.version != '' && format('-{0}', inputs.version) || '' }}
106+
path: dist/Installer.exe
107+
if-no-files-found: error

‎.gitignore‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
venv/
2+
.venv/
3+
__pycache__/
4+
*.pyc
5+
.env
6+
.idea/
7+
.vscode/
8+
9+
# PyInstaller build output
10+
build/
11+
dist/
12+
*.spec

‎BuildTutorial.txt‎

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1-
py -3.11 -m PyInstaller --onefile --noconsole Text2Pen.py
1+
Build (Windows)
2+
---------------
3+
The frontend in webui/ is loaded from disk at runtime, so it has to be bundled
4+
explicitly. On Windows the --add-data separator is a semicolon:
5+
6+
py -3.11 -m PyInstaller --onefile --noconsole --add-data "webui;webui" Text2Pen.py
27
py -3.11 -m PyInstaller --onefile --noconsole Installer.py
3-
py -3.11 -m PyInstaller --onefile --noconsole Update.py
8+
py -3.11 -m PyInstaller --onefile --noconsole Update.py
9+
10+
Build (Linux)
11+
-------------
12+
Same thing, but the separator is a colon:
13+
14+
python3 -m PyInstaller --onefile --noconsole --add-data "webui:webui" Text2Pen.py
15+
16+
Notes
17+
-----
18+
- backend/ is imported normally and gets picked up automatically; only webui/
19+
needs --add-data because it is read as data, not imported.
20+
- paths.resource_dir() resolves sys._MEIPASS, so the bundled webui/ is found
21+
in the onefile build as well as when running from source.
22+
- Running from source: ./venv/bin/python Text2Pen.py
23+
- Set TEXT2PEN_DEBUG=1 to open the WebKit inspector (right click -> Inspect).

0 commit comments

Comments
 (0)