Skip to content

Linux/Ubuntu port + Qt6 migration + Windows build/installer & UX enhancements#81

Draft
fnsangiul wants to merge 9 commits into
Aharoni-Lab:qt6-portfrom
fnsangiul:master
Draft

Linux/Ubuntu port + Qt6 migration + Windows build/installer & UX enhancements#81
fnsangiul wants to merge 9 commits into
Aharoni-Lab:qt6-portfrom
fnsangiul:master

Conversation

@fnsangiul

Copy link
Copy Markdown
Member

Summary

This PR brings the Miniscope DAQ software to Linux/Ubuntu on top of the existing Qt5 → Qt6 migration and reproducible Windows build/installer work. It targets the qt6-port integration branch so it can be reviewed without landing directly on master.

The work breaks into two layers:

  1. Qt6 migration + Windows packaging + UX — the change set previously in PR Qt6 port + reproducible Windows build/installer + config & UX enhancements #80 (which this PR supersedes).
  2. Linux/Ubuntu port — new, layered on top.

1. Qt6 migration, Windows build & UX (carried over from #80)

  • Qt5 → Qt6 migration: new CMake build; all OpenGL renderers ported to the Qt6 RHI/QML model (videodisplay, tracedisplay, behaviortracker); GLSL shader fixes; QML startup hardening.
  • Reproducible build: environment.yml conda spec pinning the toolchain.
  • Windows packaging: tools/deploy.py + tools/launcher.cpp portable folder, Inno Setup per-user installer, and a GitHub Actions release workflow.
  • UX: in-app user-config generator + Add Device dialog, Scan Devices, display LUT selector, Save-As, resizable aspect-locked windows, closeable BNO/trace window, Qt6 tree-view editor with path browse buttons; version bumped to 1.2.

2. Linux / Ubuntu port (new in this PR)

  • libuvc capture backend (videostreamlibuvc.*, videostreambase.h) for Miniscopes on Linux — live BNO and frame counter working.
  • V4L2 "Scan Devices" support and libuvc device cleanup; aspect-ratio resize on Linux.
  • AppImage packaging (packaging/build-appimage.sh, AppRun.wrapper, .desktop) and a Linux release GitHub Actions workflow (.github/workflows/release-linux.yml).
  • First-run folder prompt for choosing a data directory.
  • udev rule (packaging/99-miniscope.rules) for non-root Miniscope access; guarded USE_PYTHON=OFF build.
  • BUILD_LINUX.md build checklist and README Linux (AppImage) install instructions.
  • Linux test user-configs added.

Notes for review

fnsangiul and others added 9 commits June 21, 2026 20:08
Step-by-step apt-based build for the ubuntu-port branch: deps, configure,
run, expected first-failure points, and the V4L2 hardware-control caveat.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…counter)

On Linux the kernel uvcvideo driver caches UVC control reads and only
re-queries the device on a write. The Miniscope streams data back to the host
by overloading UVC Processing-Unit controls (frame counter on CONTRAST, BNO
quaternion on SATURATION/HUE/GAIN/BRIGHTNESS, ext-trigger on GAMMA), so the
OpenCV/V4L2 backend read stale/frozen values: BNO never updated and the
lost-frame counter ran away negative. Windows DirectShow re-reads fresh each
time, which is why this only affected Linux.

Add VideoStreamLibUVC, which talks to the device directly over libuvc/libusb
(detaching uvcvideo while open) and issues a fresh UVC GET_CUR on every read,
bypassing the cache. It handles YUYV 608x608 streaming, all control writes
(LED/gain/EWL/framerate) via the same I2C-over-UVC packing as before, and the
BNO/frame-counter/ext-trigger reads. Continuous BNO refresh on the DAQ is
enabled with a SATURATION=0x0001 start command sent at stream start (the app
otherwise gates this on the Record button; on Linux it must be live during Run).

- Extract VideoStreamBase abstract interface; VideoStreamOCV implements it.
- VideoDevice selects the backend in its constructor: Linux + Miniscope + a
  live deviceID -> libuvc; everything else (webcams, Windows, video playback)
  stays on OpenCV. Windows/OpenCV path is untouched.
- CMake finds and links libuvc + libusb-1.0 and defines HAVE_LIBUVC on Linux;
  falls back to OpenCV with a warning if libuvc is absent.
- Lower the CMake Qt floor 6.5 -> 6.4 (satisfied by any newer Qt).
- Rewrite BUILD_LINUX.md to document the conda + libuvc build and the backend.

Verified on Miniscope V4-BNO hardware (Ubuntu 24.04, conda Qt 6.11): live video,
BNO roll/pitch/yaw tracks motion with the orientation logo, sane frame counter,
and LED/gain/EWL controls all physically respond.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- NewQuickView: add a cross-platform resizeEvent that snaps height to the
  locked width:height ratio on X11/Wayland/macOS. The ratio lock was already
  wired (setLockedAspectRatio) but only enforced on Windows via WM_SIZING.

- backend.cpp scanVideoDevices(): add a Linux/V4L2 branch (was Windows-only).
  Enumerates /dev/videoN from /sys/class/video4linux, reads each node's name,
  and marks capture vs metadata nodes via VIDIOC_QUERYCAP so users can tell
  which deviceID to use.

- VideoStreamLibUVC: close the device when the stream loop ends so uvc_close
  re-attaches the kernel uvcvideo driver and /dev/videoN returns after a
  session (previously the worker-thread object's destructor didn't run on
  shutdown, leaving the Miniscope's video nodes detached).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
USE_PYTHON=OFF previously failed to compile: PyObject members and a couple of
NumPy/Python uses (import_array1, getColors) were not #ifdef-guarded. Guard them
so the behaviour tracker (embedded Python / DeepLabCut-Live) becomes truly
opt-in. With USE_PYTHON=OFF the binary links no Python at all — useful for a lean
Linux AppImage. To re-enable DLC later, configure with -DUSE_PYTHON=ON; all the
Python-dependent code lives in the guarded sections.

Also add packaging/99-miniscope.rules: a udev rule granting USB access to the
Cypress FX3 Miniscope DAQ (04b4:00f9) for headless / non-desktop sessions where
the logind ACL isn't applied.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Add a portable Linux AppImage build and a first-run experience that lets the
user choose where their configs and recordings live.

App (cross-platform, env-driven; unset = previous behavior, so Windows is
unaffected):
- New-config default dataDirectory honors $MINISCOPE_DATA_DIR (backend.cpp).
- backend.defaultUserConfigFolderUrl() exposes $MINISCOPE_USERCONFIG_DIR; the
  config open/save dialogs default to it (main.qml).

Packaging (packaging/):
- build-appimage.sh: builds USE_PYTHON=OFF against the conda env and bundles it
  with linuxdeploy + the Qt plugin into a single AppImage. Pulls in the
  transitive deps linuxdeploy drops (OpenCV's libcblas, libusb) and the xcb
  platform plugin; version comes from main.cpp.
- AppRun.wrapper: on first run, asks (zenity) for the user-config and data
  folders (defaults under ~/Documents/Miniscope; falls back if zenity is
  absent/cancelled), persists the choice, seeds the example configs into the
  chosen folder, exports MINISCOPE_USERCONFIG_DIR/MINISCOPE_DATA_DIR, and runs
  from a private working dir holding deviceConfigs + Scripts. Re-prompt by
  deleting ~/.config/MiniscopeDAQ/paths.conf.
- miniscope-daq.desktop: AppImage launcher entry.

CI: .github/workflows/release-linux.yml mirrors the Windows workflow (conda +
libuvc, tag-triggered release via action-gh-release; manual run uploads the
AppImage as an artifact).

.gitignore: scope build-* to directories (was hiding packaging/build-appimage.sh)
and ignore dist/.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Remove the branches: ubuntu-port trigger before merging.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The Linux workflow now triggers only on v* tags and manual dispatch, matching
the Windows workflow. (The branch trigger was only to validate CI from the
feature branch; it's served its purpose.)

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@fnsangiul fnsangiul marked this pull request as draft June 23, 2026 00:23

@sneakers-the-rat sneakers-the-rat left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems good to me, we already talked about PR structure and the standards for maintenance work on deprecated projects in person.

the two things that i think would be worth doing beyond what's already done here are

  • a mild restructuring of the workflows so that they feed into a single release
  • declaring all the deps in cmake so that we don't need OS-specific copy/pastes

other than that, i basically will just take your word about the code changes - that they work, and also by bringing us back to a place where we can reliably compile the code, we can at least work on any bugs that might pop up again.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we discussed - putting these in a single workflow, two jobs, and then a third that combines the artifacts and makes a single release, would probably be one change worth making just for the sake of making "one release with the windows and linux binaries" rather than "two releases, one with windows, one with linux"

; A stable AppId ties every version to the same uninstall entry, so a new
; release upgrades the previous install in place instead of stacking copies.
; Never change this GUID once shipped.
AppId={{B7E6B2D4-3F8A-4C21-9E5D-2A1F6C9B4E70}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double {{ here, is that intentional?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

directory structure - might as well have one top-level directory for packaging artifacts/config and then split out for linux/windows.

/packaging/windows/MiniscopeDAQ.iss
/packaging/linux/99-miniscope.rules
...

Comment thread packaging/AppRun.wrapper
Comment on lines +20 to +23
CFG_DIR="$HOME/.config/MiniscopeDAQ"
SETTINGS="$CFG_DIR/paths.conf"
mkdir -p "$CFG_DIR"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is set outside the main application, is there an analogous concept for windows? or why configure that in a launcher rather than in the application?

Comment on lines +48 to +51
# Square 256x256 icon from the logo (linuxdeploy requires a standard square size).
ffmpeg -y -loglevel error -i "$REPO/source/img/MiniscopeLogo.png" \
-vf "scale=256:256:force_original_aspect_ratio=decrease,pad=256:256:(ow-iw)/2:(oh-ih)/2:color=#00000000,format=rgba" \
"$APPDIR/usr/share/icons/hicolor/256x256/apps/miniscope-daq.png"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might as well just version this statically

Comment thread source/tracedisplay.h
Comment on lines +228 to +229
// Qt6: removed cloned-QMouseEvent members; the drag logic that used them is
// disabled (commented out) in tracedisplay.cpp.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLMs love to leave comments explaining that stuff no longer exists - we can just remove things, having a comment that explains that something is no longer here only serves to be confusing.

Comment on lines +4 to +5
// Qt6: removed unused 'import QtQuick.Controls.Styles 1.4' (module removed in Qt6;
// nothing in this file used a Controls-1 *Style type).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Qt6: removed unused 'import QtQuick.Controls.Styles 1.4' (module removed in Qt6;
// nothing in this file used a Controls-1 *Style type).

Comment thread source/videostreambase.h

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm basically just gonna take your word on this and the below two files - haven't taken the time to read them, i imagine there may be some problems from having multiple backends patched together, but if it works it works.

Comment thread tools/deploy.py

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as comment above - if we can avoid needing to do this by declaring the deps explicitly in cmake, that would b great

Comment thread environment.yml
- numpy=2.4
- cmake
- ninja
- "libblas=*=*openblas" # force OpenBLAS, never MKL (see note above)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

man what is even this specifier syntax

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update to Qt6 add linux support? Python issues compiling in Linux

2 participants