by Moritz Wunderwald, 2025-2026
Interactive visual toolbox for windowed cross-correlation analysis, optimized for IBI and EDA biosignal data. Load dyad recordings, tune parameters, inspect results, and export or batch-process datasets.
- Python 3.10+
cd app
pip install -r requirements.txtUsing a virtual environment is optional but recommended to keep dependencies isolated:
cd app
python -m venv .venv
# activate (macOS / Linux)
source .venv/bin/activate
# activate (Windows)
.venv\Scripts\activate
pip install -r requirements.txtThe environment only needs to be created once. On subsequent sessions, just re-run the activate line before launching the app.
python app.pyBioSync ships a build.py script that wraps PyInstaller. Run it from inside the app/ directory:
python build.pyYou will be prompted to choose a build method and an app name. The finished executable is placed in dist/.
| Method | When to use |
|---|---|
| Smart auto-detect (recommended) | Standard builds — PyInstaller analyses imports automatically |
| Brute force | When the smart build produces a broken app due to hidden imports |
| Minimal | Quick test builds; may be missing some dependencies at runtime |
app.py (~80 lines) — orchestrator only. Creates the CTk window, then calls into the modules below in order:
state → validation → callbacks → layout → gui_updates → corr_plot → mainloop.
| Module | Responsibility |
|---|---|
state.py |
All tk.Var declarations and data containers (dat_workbook_data, dat_physiological_data, dat_correlation_data, dat_plot_data). Also holds screen metrics and the PARAMS_CHANGED() trigger. Initialised via init_state() after the CTk window exists. |
validation.py |
Pure input-validation logic — check_window_size, check_max_lag, check_lag_filter, etc. No widget references; reads/writes only from state. |
callbacks.py |
All user-event handlers: entry/checkbox/dropdown on_* callbacks, file pickers, XLSX data loading, signal pre-processing, export handlers, batch processing, and random-pair analysis. Widget references needed for dropdown updates are injected via register_dropdowns() / register_tabview() after the layout is built. |
layout.py |
Builds every widget across the three tabs (Input Data, Correlation, Export & Batch) inside a single build_layout() call. Returns a dict of widget references consumed by gui_updates and corr_plot. |
gui_updates.py |
Reactive layer — functions that update widget appearance (border colours, error labels, show/hide panels, button enable/disable) in response to state-variable changes. Registered as trace_add callbacks via setup_traces(). Widget references are injected via register_widgets(). |
corr_plot.py |
Runs windowed or standard cross-correlation + DFA on every PARAMS_CHANGED event, updates the matplotlib figure, and refreshes the canvas. Separated into _update_wxcorr_data, _update_sxcorr_data, update_plot, update_canvas, and the top-level UPDATE() loop. |
| Module | Responsibility |
|---|---|
signal_processing.py |
Resampling and alignment of raw IBI / EDA signals (preprocess_dyad). |
cross_correlation.py |
windowed_cross_correlation and standard_cross_correlation algorithms. |
dfa.py |
Detrended Fluctuation Analysis — dfa, dfa_wxcorr, dfa_wxcorr_window_averages. |
plot.py |
Matplotlib figure factories for preprocessing preview, windowed xcorr, and standard xcorr plots. |
export.py |
Writes XLSX result files for wxcorr, sxcorr, and random-pair analysis. Always exports raw (non-sigmoid) correlation values. |
batch_processing.py |
Iterates a folder of dyads, runs the full analysis pipeline on each, and saves per-dyad XLSX + PNG output. Also contains random_pair_analysis. |
xlsx.py |
Thin wrappers around openpyxl for reading and writing Excel files. |
utils.py |
Small helpers (is_numeric_array, count_subdirectories, …). |
tk theme (no window needed)
└─ app = tk.CTk()
└─ state.init_state() # tk.Vars require a live window
└─ validation.make_validator(app)
└─ callbacks.setup_traces()
└─ layout.build_layout() # creates all widgets; injects refs into callbacks
└─ gui_updates.register_widgets() + setup_traces()
└─ corr_plot.setup() # creates matplotlib canvas
└─ UPDATE() + mainloop()
- Late-registration pattern:
callbacks.pyandgui_updates.pyhold_dropdowns/_widgetsdicts that are populated bylayout.pyafter widgets are created. This avoids circular imports while keeping logic decoupled from widget construction. - Sigmoid is display-only: the sigmoid-scaled view is toggled in the Visualisation section of the Correlation tab and affects only the on-screen plot. All XLSX exports always contain raw correlation values.
- Single update trigger:
state.PARAMS_CHANGED()incrementsval_UPDATE_COUNT, which triggerscorr_plot.UPDATE()via a singletrace_add. Everything re-renders from one place.