English | 日本語
Flowdesk is an early-stage Python project for a Linux-first FlowJo-like flow cytometry analysis application.
For the user-facing workflow and feature reference, see the User Manual.
- Represent FCS samples, channels, compensation matrices, derived parameters, transforms, gates, population trees, and export records.
- Keep scientific execution in GUI-independent core modules.
- Store projects as
.flowdeskdirectory bundles that can be run from GUI, CLI, or Python API. - Provide initial documentation, schemas, agent guidance, and synthetic tests.
- Complete FlowJo compatibility.
- Complete GatingML support.
- Production GUI behavior.
- Production FCS parsing or large-file rendering.
Copyright (c) 2026 Yoshihiko Fujita (yfujita.skgcat@gmail.com).
Flowdesk is distributed under the BSD 3-Clause License. See LICENSE
for the complete license text. The same information is available from
flowdesk --help, flowdesk --credits, and the GUI Help menu.
Python 3.11+, NumPy, Polars or pandas, FlowIO and/or FlowKit, PySide6, pyqtgraph, Datashader, pytest, ruff, and mypy.
direnv allow
# load the virtual environment if not already loaded
. .direnv/python-3.12.13/bin/activate
python -m pip install -e '.[dev]'Optional groups:
python -m pip install -e '.[gui,dev,gui-test]'Flowdesk currently builds native PyInstaller onedir packages. The build
produces both the GUI (flowdesk) and headless CLI (flowdesk-cli) artifacts
under dist/. These are development/portable directory packages; Windows
installers and signed/notarized macOS DMG files are not generated yet.
PyInstaller is not a cross-compiler. Build on the same OS and CPU architecture
that the package will run on. Use a clean virtual environment with Python
3.11 or newer (Python 3.12 is used by the repository's development setup).
The build machine needs Git, a working C/C++ toolchain for any native wheels,
and enough disk space for the temporary build/ directory and dist/
artifacts. On macOS, install Apple's Command Line Tools (xcode-select --install) before creating the environment. On Windows, use a current
64-bit Python installation and PowerShell or Command Prompt.
Install the package and packaging dependencies from the repository root:
python -m venv .venv
# Windows PowerShell: .venv\Scripts\Activate.ps1
# Windows cmd.exe: .venv\Scripts\activate.bat
# macOS/Linux: source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[gui,dev]"Build both artifacts:
python tools/package.py buildOn macOS or Linux, the equivalent Make target is:
make packageOn Windows, run the Python command directly because the repository Makefile is intended for POSIX shells. The resulting directories are:
dist/flowdesk/ GUI application (flowdesk or flowdesk.exe)
dist/flowdesk-cli/ headless CLI (flowdesk-cli or flowdesk-cli.exe)
Run the package smoke test after building. It checks that the GUI starts with the packaged Qt plugins. If a project and FCS file are supplied, it also runs the packaged CLI pipeline:
python tools/package.py smoke
python tools/package.py smoke \
--project path/to/project.flowdesk \
--fcs path/to/sample.fcsSmoke-test reports are written to artifacts/package-smoke/ by default. A
build provenance file can be created with:
python tools/package.py manifest \
--output artifacts/package-smoke/build-manifest.jsonFor a single build-and-smoke operation, use make package-check on macOS or
Linux. The package must be tested on a clean machine for the target OS before
distribution. In particular, verify Japanese and space-containing paths,
project save/load, exports, recovery/log directories, and that GUI population
counts agree with headless PipelineRunner results.
Platform-specific signing and installer steps are not part of the current build entry point:
- Windows:
dist/flowdesk/can be wrapped by an installer such as Inno Setup, but no installer configuration is included yet. - macOS: the onedir output can be assembled into an
.app; Developer ID signing, hardened runtime, notarization, and DMG creation still require a separate release workflow. - Linux: AppImage generation is not included yet.
Do not commit build/, dist/, or package smoke artifacts. Use a native
runner for each supported OS and keep the Python, PySide6, NumPy, and FlowIO
versions recorded in the manifest when sharing a package.
The repository includes .github/workflows/package-windows.yml. It runs on a
native windows-latest runner and builds both the GUI and CLI packages, runs
core tests and the packaged smoke test, writes a build manifest, and uploads
Flowdesk-Windows-x64.zip as an Actions artifact.
Run it from Actions → Package Windows → Run workflow, or push a tag such
as v0.1.0. The workflow creates a portable ZIP, not an installer, and the
artifact is available from the completed workflow run. The package is
unsigned; code signing, SmartScreen reputation, and an Inno Setup installer
require a later release workflow.
The repository also includes package-linux.yml and package-macos.yml.
They run on ubuntu-22.04 and macos-14, respectively, and upload
Flowdesk-Linux-x86_64.tar.gz and Flowdesk-macOS-arm64.zip as separate
artifacts. These are native PyInstaller directory packages. AppImage,
macOS .app/DMG packaging, signing, and notarization are not included yet.
To increment the patch version, first run make upversion. The version source
is src/flowdesk_qt/_version.py; setuptools, the GUI, and the CLI all use it.
After committing the version change, create and push a tag with:
make pushtagThis creates and pushes v<application version>. Existing tags are never
overwritten.
pytestA Makefile is provided for convenience:
make test # Run all tests (pytest -v)
make lint # Run ruff linter on src/ and tests/
make type-check # Run mypy type checker on core, storage, and CLI modules
make check # Run lint + type-check
make fmt # Run ruff formatter on src/ and tests/
make all # Run fmt + check + test
make clean # Remove build artifacts and caches
make help # Show available targetsAfter installing the package (pip install -e .), the flowdesk command is available:
# Run a saved project and export results
flowdesk run path/to/project.flowdesk --output results.tsv
# Opt in to bounded sample-level threads for a multi-sample project
flowdesk run path/to/project.flowdesk --execution-backend thread \
--max-workers 2 --memory-budget-mib 4096
# Inspect FCS file metadata
flowdesk inspect path/to/sample.fcs
# Apply gates to multiple FCS files in batch
flowdesk batch-gate path/to/project.flowdesk --fcs file1.fcs file2.fcsThe CLI run command exports population statistics as TSV by default. Use --csv to produce CSV output.
NaN values (e.g., undefined frequencies) can be controlled via --nan-policy:
string_nan(default): write the literal stringNaNempty: leave the cell emptyzero: write0
The PySide6-based GUI is available as an optional dependency. Install the gui extra:
python -m pip install -e '.[gui,dev]'# Launch with no data
python -m flowdesk_qt
# Launch and auto-load FCS files from a directory
python -m flowdesk_qt --data-dir data/./tools/run-gui-tests.sh
./tools/run-single-gui-test.sh tests/gui/test_gui_workflow.py::test_load_gate_run_and_match_headless
make test-all
./tools/run-gui-debug.sh --data-dir data/GUI tests use the offscreen Qt backend by default. For X11-specific behavior, use
FLOWDESK_GUI_BACKEND=xvfb ./tools/run-gui-tests.sh. When the GUI is launched
normally, logs and debug artifacts are written below the OS-specific user
application-data directory. Use --debug-artifacts-dir to select an explicit
directory during development or CI. Recovery copies use the OS-specific user
cache directory.
+-----------+--------------------------+----------------+
| Samples | Plot Parameters | Gates |
| Browser | +------------------+ | Editor |
| | | 2D Scatter Plot | | |
| - File | | (pyqtgraph) | | - Create Gate |
| list | | | | - Delete |
| | | + Gate overlays | | - Polygon |
| - Channel | +------------------+ +----------------+
| metadata| | |
+-----------+ | |
+----------------+
| Population |
| Results |
| (table) |
+----------------+
- Sample Browser (left): lists loaded FCS files and shows channel metadata.
- Channel Selector (center top): choose X and Y parameters for the 2D plot.
- Plot Widget (center): pyqtgraph-based scatter plot with gate overlays in data coordinates.
- Gate Editor (right top): create rectangle, range, and polygon gates.
- Population Results (right bottom): displays pipeline execution results.
- Load and select a sample, then choose Analysis → Derived Parameters.
- Enter a definition ID, display name, stable output channel ID, expression,
source stage (
compensatedorraw), optional unit, and failure policy. - Select explicit inputs or use Insert parameter to insert a measured or previously defined channel ID at the expression cursor.
- Click Validate to run core syntax/dependency validation. Errors include a diagnostic code and, for syntax errors, line and column.
- Click Preview to evaluate at most 200 events through the same compensation, source-view, dependency, and expression pipeline used by headless execution.
- Confirm the dialog and rerun the pipeline. The stable output channel ID can then be referenced by transforms and gates.
Preview values are only a bounded diagnostic view. Gates, statistics, and
exports always use the full event table through PipelineRunner.
- Load a sample and choose Analysis → Analysis Transforms.
- Create one transform for each parameter that needs formal analysis
coordinates. Choose
linear,log,asinh, or the published Gating-MLlogicle, enter every displayed parameter, and click Preview to inspect the inverse round-trip error on up to 200 finite events. - Confirm the dialog. When a transformed parameter is selected on an axis,
the old display-scale combo is fixed to
linear; the plot, ticks, and newly drawn gate all use the same persisted transform ID. - Draw a rectangle or polygon normally and run the pipeline. The gate's X/Y transform IDs make the same membership reproducible through the CLI and Python runner.
A transform already referenced by a gate cannot be changed or deleted in place. Create a new transform ID, select the old gate in Gate hierarchy, and click Migrate Transform. The preview shows source/candidate event counts plus gained and lost events. Choose Duplicate to preserve the old gate or Migrate to replace it. Polygon migration is explicitly approximate: reprojected vertices do not make straight edges scientifically equivalent between nonlinear coordinate systems.
Migration preview currently stops, rather than using raw-event estimates, when the project includes compensation or derived parameters. Those projects need a future canonical pipeline-stage preview before this analysis-changing action can be enabled safely.
| Shortcut | Action |
|---|---|
Ctrl+O |
Open directory with FCS files |
Ctrl+Shift+O |
Open specific FCS files |
Ctrl+R |
Run analysis pipeline |
Ctrl+G |
Clear all gates |
Ctrl+Q |
Quit application |
Flowdesk evaluates gates as a parent-child population hierarchy. Gate membership is calculated by the GUI-independent pipeline against full event data; displayed/downsampled points are never used for population counts.
- Load one or more FCS files and select a sample in Sample Browser.
- Select the X and Y channels and their axis scales.
- In Gate Editor, select
rectangle,polygon, orrangeas the gate type. - Set Parent population to
All Events. - Click Create Gate.
- For a rectangle, drag over the plot. For a polygon, click each vertex and double-click the final vertex. A range gate is entered in its dialog.
- Run the pipeline with
Ctrl+Ror Run Pipeline.
The resulting population appears in Population Results. Selecting its row
filters the scatter plot or Count histogram to that population's full
membership. Selecting All Events restores the unfiltered view.
To create a hierarchy such as:
All Events
└─ Cells
└─ Singlets
└─ CD45+
create Cells with All Events as its parent. Then select Cells in the
Gate hierarchy tree and click Create Child Gate to create Singlets.
Select Singlets and repeat the operation to create CD45+. The creation
context banner shows the fixed parent id, sample, channels, and scales before
drawing. Run the pipeline again after gate changes.
Selecting a row in Population Results changes the displayed population, but currently does not automatically change the Parent population combo. The parent must be selected explicitly through Create Child Gate or the Parent population combo in Gate Editor.
Select a gate and click Show Gate to navigate to its channel pair and axis scales without changing analysis results. To change an existing parent, choose the new parent in the selected-gate parent control and click Apply Parent. Invalid self/descendant/cyclic relationships are rejected atomically.
Legacy geometric gates record the X and Y scales in which they were created:
linear, log10, or asinh. New formal analysis transforms instead persist
stable per-axis transform IDs. In both cases the headless pipeline applies the
same coordinate definition to full-resolution values before evaluating the
gate.
A gate overlay is shown and editable only when the current channel pair and axis scales match the gate definition. For example, a linear/linear gate is hidden after switching the view to linear/log10, but its definition and population membership are unchanged. Switch back to linear/linear to display and edit it again. Create a separate gate while viewing linear/log10 when that coordinate system is scientifically intended.
Boolean gates combine existing populations without evaluating display points.
- Create the source gates and choose the intended Parent population.
- Select
booleanin the Gate type combo. - Click Create Gate.
- Select
and,or, ornotin the dialog. - Select source populations from the hierarchy tree. Use Ctrl-click to select multiple sources.
- Confirm the dialog and rerun the pipeline.
Operations have the following meanings:
AND: events present in every selected source population.OR: events present in at least one selected source population.NOT: events absent from the selected source population.
The Boolean result is also restricted to its selected parent population. For
example, Parent = Cells, Operation = NOT, and Source = CD45+ produces
events inside Cells that are not in CD45+.
Existing Boolean gates can be selected in the hierarchy and changed with Edit Boolean. Operations, source ids, axes/scales, counts, and frequencies are available from the hierarchy columns and tooltips. Drag-and-drop reparenting is intentionally not enabled; the validated Apply Parent operation prevents partial or cyclic graph updates.
raw FCS events
-> compensation
-> derived parameters
-> transform
-> gate membership
-> population statistics
-> export
flowdesk_core: Scientific logic (GUI-independent).flowdesk_storage: Project bundle I/O (.flowdeskdirectories).flowdesk_cli: CLI entry points (run,inspect,batch-gate).flowdesk_qt: PySide6 GUI (optional dependency).
During routine gating, use the plot-area context menu for display-only appearance
changes, the Gate hierarchy Color swatch for population display colors, and the
Samples pane Ov checkbox/swatch for manual overlays. The Samples pane also supports
comparison sets and Manual + comparison set mode. Active-sample selection remains
separate from overlay selection; missing or incompatible overlay sources are reported
instead of being treated as zero events. The advanced Overlay Sources... and
Plot Presentation... dialogs remain available for explicit population/axis/transform
configuration.
Implemented: core dataclasses, pipeline runner, FCS I/O, compensation, derived parameters, transforms, gates, population statistics, TSV/CSV export, CLI commands, and synthetic tests. PySide6 GUI with sample browser, scatter and histogram plots, hierarchy-tree and Boolean gate editing, population filtering, validated reparenting, and pipeline execution. The current suite has 325 passing tests, and ruff passes for all source and test files.
Not yet implemented: complete FlowJo compatibility, full GatingML support, production GUI behavior, and large-file FCS rendering.