TeslasuitDocumentation
Frameworks

Generic GUI (reference template)

Reference PyQt5 GUI template wired to the framework's reusable widget toolkit (fes_framework.gui.app.FesApp, fes_framework.gui.data_adapter.DataAdapter, plus tab classes). No application-specific control logic — copy this when starting a new GUI from scratch.

Source: examples/generic_gui/


What it shows#

The framework ships a small PyQt5 widget toolkit under fes_framework/gui/. It's not a fully-featured GUI — it's a collection of reusable building blocks:

  • FesApp — main application class; manages the QApplication, the DataAdapter, the queue handler, and the tab layout.
  • DataAdapter — attaches to a SharedRingBuffer, decodes the application-defined frame dtype, and exposes rolling buffers for selected fields at 30 Hz.
  • OverviewTab — a generic per-muscle slider/toggle panel.
  • SensorDataTab — bilateral side-by-side plots driven from the DataAdapter.
  • FesWidget — base class for custom tabs. Override build_ui() and refresh().

This example wires them together with a no-op DummyStrategy so you can see the GUI light up without delivering any stimulation.


Project layout#

examples/generic_gui/
├── __init__.py
└── main.py        ── strategy + GUI wiring + launch()

A single file. The toolkit it uses lives in fes_framework/gui/.


Anatomy of main.py#

The file walks through every wiring decision you have to make for a GUI-based application:

# 1. Pick a frame dtype for the SharedRingBuffer.
FRAME_DTYPE = shared_memory_frame   # framework default (biomech + steps)

# 2. Decide which fields the GUI should plot.
BUFFERED_FIELDS = [
    "ShoulderFlexExtR", "ShoulderFlexExtL",
    "ElbowFlexExtR",    "ElbowFlexExtL",
    "KneeFlexExtR",     "KneeFlexExtL",
    "HipFlexExtR",      "HipFlexExtL",
    "StepDetectorRight", "StepDetectorLeft",
]

# 3. Decide which muscles the OverviewTab exposes as sliders.
MUSCLES = [
    "biceps_left",  "biceps_right",
    "triceps_left", "triceps_right",
    # ...
]

# 4. Group sensor fields for the SensorDataTab.
SENSOR_SERIES = [
    ("Shoulder Flex/Ext", "ShoulderFlexExtL", "ShoulderFlexExtR", "deg"),
    ("Knee Flex/Ext",     "KneeFlexExtL",     "KneeFlexExtR",     "deg"),
    # ...
]

# 5. (Optional) define your own tabs.
class ConnectionTab(FesWidget):
    def build_ui(self): ...
    def refresh(self): ...

# 6. Wire the GUI process entry point.
def gui_main(control_queue, utility_queue):
    adapter = DataAdapter(buffer_name="fes_shared_buffer", ...)
    app = FesApp(
        control_queue=control_queue,
        utility_queue=utility_queue,
        data_adapter=adapter,
        title="fes_framework — Generic Example",
        tabs=[
            ("Overview",    partial(OverviewTab, muscles=MUSCLES)),
            ("Sensor Data", partial(SensorDataTab, series=SENSOR_SERIES)),
            ("Connection",  ConnectionTab),
        ],
    )
    app.run()

# 7. Launch.
launch(DummyStrategy, gui_runner=gui_main, lsl_enabled=False)

Each block above is the answer to one question:

  1. What dtype is in shared memory?
  2. Which fields do you plot?
  3. Which muscles get sliders?
  4. How are the sensor plots grouped?
  5. Do you need custom tabs? (Optional)
  6. How are widgets composed?
  7. Run it.

Running it#

python -m examples.generic_gui.main

A two-or-three-tab window appears. The Overview tab has muscle toggles and amplitude sliders; the Sensor Data tab plots joint angles in real time once data is flowing; the Connection tab shows a coloured indicator.

The strategy is a no-op (no stimulation), so flipping muscle toggles in the Overview tab won't actually fire anything. To see stimulation you'd swap DummyStrategy for one of your own.


What you'd change for your own application#

Three edits, no new files needed:

  1. Replace DummyStrategy with your own ControlStrategyBase subclass.
  2. Change FRAME_DTYPE if your strategy needs to expose custom fields to the GUI (the default is biomech + step detector; for an elbow-flexion app you'd add an ElbowAngle field).
  3. Update MUSCLES and SENSOR_SERIES to match what your application actually controls and reads.

If you want a richer GUI (custom auto-tune buttons, calibration panels, plot widgets that aren't bilateral), you'd:

  • Subclass FesWidget for one-off panels.
  • Or compose your own QWidget hierarchy and bypass the toolkit.

When to use this vs. building from scratch#

Use this templateBuild from scratch
Two-tab operator interfaceNeed three+ tabs with rich custom layout
Bilateral side-by-side plotsNeed 3D viz, video, or unusual widgets
Standard amplitude/PW/period sliders per muscleNeed PID gain knobs, FFT plots, etc.
Want consistent look across applicationsNeed bespoke branding

The toolkit is a starting point, not the final destination. Both elbow_flexion/ and walking_fes/ build their own GUIs (with similar patterns to the toolkit but more specialised); they're worth reading after this one.


See also#