gui

Program Diagram

images/gui_diagram.png

Schematic diagram of major UI components and signals

Design Requirements

  • Display Values

    • Value name, units, absolute range, recommended range, default range

    • VTE

    • FiO2

    • Humidity

    • Temperature

  • Plots

  • Controlled Values

    • PIP

    • PEEP

    • Inspiratory Time

  • Value Dependencies

UI Notes & Todo

  • Jonny add notes from helpful RT in discord!!!

  • Top status Bar

    • Start/stop button

    • Status indicator - a clock that increments with heartbeats, or some other visual indicator that things are alright

    • Status bar - most recent alarm or notification w/ means of clearing

    • Override to give 100% oxygen and silence all alarms

  • API

    • Two queues, input and output. Read from socket and put directly into queue.

    • Input, receive (timestamp, key, value) messages where key and value are names of variables and their values

    • Output, send same format

  • Menus

    • Trigger some testing/calibration routine

    • Log/alarm viewer

    • Wizard to set values?

    • save/load values

  • Alarms

    • Multiple levels

    • Silenced/reset

    • Logging

    • Sounds?

  • General

    • Reduce space given to waveforms

    • Clearer grouping & titling for display values & controls

    • Collapsible range setting

    • Ability to declare dependencies between values

      • Limits - one value’s range logically depends on another

      • Derived - one value is computed from another/others

    • Monitored values should have defaults, warning range, and absolute range

    • Two classes of monitored values – ones with limits and ones without. There seem to be lots and lots of observed values, but only some need limits. might want to make larger drawer of smaller displayed values that don’t need controls

    • Save/load parameters. Autosave, and autorestore if saved <5m ago, otherwise init from defaults.

    • Implement timed updates to plots to limit resource usage

    • Make class for setting values

    • Possible plots

      • Pressure vs. flow

      • flow vs volume

      • volume vs time

  • Performance

    • Cache drawText() calls in range selector by drawing to pixmap

Jonny Questions

  • Which alarm sounds to use?

  • Final final final breakdown on values and ranges plzzz

jonny todo

  • use loop_counter to check on controller advancement

  • implement single values list with properties ‘controllable’ vs not.

GUI Object Documentation

Classes

Vent_Gui(coordinator[, update_period])

The Main GUI window.

Functions

launch_gui(coordinator)

class vent.gui.main.Vent_Gui(coordinator, update_period=0.1)[source]

The Main GUI window.

Only one instance can be created at a time. Uses set_gui_instance() to store a reference to itself. after initialization, use get_gui_instance to retrieve a reference.

Attributes

CONTROL

see gui.defaults.CONTROL

MONITOR

see gui.defaults.SENSOR

PLOTS

see gui.defaults.PLOTS

alarm_state

alarms_updated(*args, **kwargs)

PySide2.QtCore.Signal emitted whenever alarms are updated.

control_width

int([x]) -> integer

gui_closing(*args, **kwargs)

PySide2.QtCore.Signal emitted when the GUI is closing.

main_height

int([x]) -> integer

monitor_width

int([x]) -> integer

plot_width

int([x]) -> integer

status_height

int([x]) -> integer

total_height

computed from status_height+main_height

total_width

computed from monitor_width+plot_width+control_width

update_period

Methods

__init__(coordinator[, update_period])

The Main GUI window.

alarm_state_changed(state)

closeEvent(event)

Emit gui_closing and close!

handle_alarm(alarm)

handle_cleared_alarm(alarm)

init_controls()

on startup, set controls in coordinator to ensure init state is synchronized

init_ui()

Create the UI components for the ventilator screen

init_ui_controls()

init_ui_monitor()

init_ui_plots()

init_ui_signals()

Connect Qt signals and slots

init_ui_status_bar()

set_plot_duration(dur)

set_value(new_value[, value_name])

Set a control value with the coordinator

update_gui()

update_value(value_name, new_value)

param value_name

Name of key in Vent_Gui.monitor and Vent_Gui.plots to update

monitor

Dictionary mapping values.SENSOR keys to widgets.Monitor_Value objects

Type

dict

plots

Dictionary mapping gui.PLOT keys to widgets.Plot objects

Type

dict

controls

Dictionary mapping values.CONTROL keys to widgets.Control objects

Type

dict

coordinator

Some coordinator object that we use to communicate with the controller

Type

vent.coordinator.coordinator.CoordinatorBase

control_module

Reference to the control module, retrieved from coordinator

Type

vent.controller.control_module.ControlModuleBase

start_time

Start time as returned by time.time()

Type

float

update_period

The global delay between redraws of the GUI (seconds)

Type

float

alarm_manager
Type

AlarmManager

Parameters
  • update_period (float) – The global delay between redraws of the GUI (seconds)

  • test (bool) – Whether the monitored values and plots should be fed sine waves for visual testing.

gui_closing(*args, **kwargs) = <PySide2.QtCore.Signal object>

PySide2.QtCore.Signal emitted when the GUI is closing.

alarms_updated(*args, **kwargs) = <PySide2.QtCore.Signal object>

PySide2.QtCore.Signal emitted whenever alarms are updated.

Returns the result of self.coordinator.get_active_alarms, so will emit an empty dict if there are no active alarms.

MONITOR = OrderedDict([(<ValueName.FIO2: 8>, <vent.common.values.Value object>), (<ValueName.TEMP: 9>, <vent.common.values.Value object>), (<ValueName.HUMIDITY: 10>, <vent.common.values.Value object>), (<ValueName.VTE: 11>, <vent.common.values.Value object>), (<ValueName.PRESSURE: 12>, <vent.common.values.Value object>), (<ValueName.IE_RATIO: 7>, <vent.common.values.Value object>)])

see gui.defaults.SENSOR

CONTROL = OrderedDict([(<ValueName.PIP: 1>, <vent.common.values.Value object>), (<ValueName.PIP_TIME: 2>, <vent.common.values.Value object>), (<ValueName.INSPIRATION_TIME_SEC: 6>, <vent.common.values.Value object>), (<ValueName.PEEP: 3>, <vent.common.values.Value object>), (<ValueName.PEEP_TIME: 4>, <vent.common.values.Value object>), (<ValueName.BREATHS_PER_MINUTE: 5>, <vent.common.values.Value object>)])

see gui.defaults.CONTROL

PLOTS = OrderedDict([(<ValueName.PRESSURE: 12>, {'name': 'Pressure', 'units': 'mmH2O', 'abs_range': (0, 70), 'safe_range': (0, 60), 'decimals': 1, 'default': None, 'color': '#FF6319'}), (<ValueName.TEMP: 9>, {'name': 'Temp', 'units': '°C', 'abs_range': (35, 40), 'safe_range': (36, 39), 'decimals': 1, 'default': None, 'color': '#EE352E'}), (<ValueName.HUMIDITY: 10>, {'name': 'Humidity', 'units': '%', 'abs_range': (0, 100), 'safe_range': (70, 100), 'decimals': 1, 'default': None, 'color': '#0039A6'})])

see gui.defaults.PLOTS

monitor_width = 2
plot_width = 4
control_width = 2
total_width = 8

computed from monitor_width+plot_width+control_width

status_height = 1
main_height = 5
total_height = 6

computed from status_height+main_height

__init__(coordinator, update_period=0.1)[source]

The Main GUI window.

Only one instance can be created at a time. Uses set_gui_instance() to store a reference to itself. after initialization, use get_gui_instance to retrieve a reference.

monitor

Dictionary mapping values.SENSOR keys to widgets.Monitor_Value objects

Type

dict

plots

Dictionary mapping gui.PLOT keys to widgets.Plot objects

Type

dict

controls

Dictionary mapping values.CONTROL keys to widgets.Control objects

Type

dict

coordinator

Some coordinator object that we use to communicate with the controller

Type

vent.coordinator.coordinator.CoordinatorBase

control_module

Reference to the control module, retrieved from coordinator

Type

vent.controller.control_module.ControlModuleBase

start_time

Start time as returned by time.time()

Type

float

update_period

The global delay between redraws of the GUI (seconds)

Type

float

alarm_manager
Type

AlarmManager

Parameters
  • update_period (float) – The global delay between redraws of the GUI (seconds)

  • test (bool) – Whether the monitored values and plots should be fed sine waves for visual testing.

property update_period
init_controls()[source]

on startup, set controls in coordinator to ensure init state is synchronized

set_value(new_value, value_name=None)[source]

Set a control value with the coordinator

Parameters

new_value (float) – Som

update_gui()[source]
update_value(value_name, new_value)[source]
Parameters
init_ui()[source]

Create the UI components for the ventilator screen

init_ui_status_bar()[source]
init_ui_monitor()[source]
init_ui_plots()[source]
init_ui_controls()[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>
init_ui_signals()[source]

Connect Qt signals and slots

handle_alarm(alarm)[source]
handle_cleared_alarm(alarm)[source]
property alarm_state
alarm_state_changed(state)[source]
set_plot_duration(dur)[source]
closeEvent(event)[source]

Emit gui_closing and close!

vent.gui.main.launch_gui(coordinator)[source]

Control

Classes

Control(value)

class vent.gui.widgets.control.Control(value)[source]

Methods

init_ui()

toggle_control(state)

update_value(new_value)

value_changed(*args, **kwargs) = <PySide2.QtCore.Signal object>
init_ui()[source]
toggle_control(state)[source]
update_value(new_value)[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>

Monitor

Classes

Monitor(value[, update_period, enum_name])

param value

class vent.gui.widgets.monitor.Monitor(value, update_period=0.5, enum_name=None)[source]
Parameters
  • value (Value) –

  • update_period (float) – update period of monitor in s

Methods

__init__(value[, update_period, enum_name])

param value

_limits_changed(val)

check_alarm([value])

init_ui()

set_alarm(alarm)

Simple wrapper to set alarm state from a qt signal

timed_update()

toggle_alarm()

toggle_control(state)

update_boxes(new_values)

update_limits(new_limits)

update_value(new_value)

Attributes

alarm_state

alarm(*args, **kwargs) = <PySide2.QtCore.Signal object>
limits_changed(*args, **kwargs) = <PySide2.QtCore.Signal object>
__init__(value, update_period=0.5, enum_name=None)[source]
Parameters
  • value (Value) –

  • update_period (float) – update period of monitor in s

init_ui()[source]
toggle_control(state)[source]
update_boxes(new_values)[source]
update_value(new_value)[source]
update_limits(new_limits)[source]
timed_update()[source]
_limits_changed(val)[source]
property alarm_state
set_alarm(alarm)[source]

Simple wrapper to set alarm state from a qt signal

Parameters

alarm (bool) – Whether to set as alarm state or not

toggle_alarm()[source]
check_alarm(value=None)[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>

Plot

Data

PLOT_FREQ

Update frequency of Plot s in Hz

PLOT_TIMER

A QTimer that updates :class:`.TimedPlotCurveItem`s

Classes

Plot(name[, buffer_size, plot_duration, …])

When initializing PlotWidget, parent and background are passed to GraphicsWidget.__init__() and all others are passed to PlotItem.__init__().

vent.gui.widgets.plot.PLOT_TIMER = None

A QTimer that updates :class:`.TimedPlotCurveItem`s

vent.gui.widgets.plot.PLOT_FREQ = 5

Update frequency of Plot s in Hz

class vent.gui.widgets.plot.Plot(name, buffer_size=4092, plot_duration=5, abs_range=None, safe_range=None, color=None, units='', **kwargs)[source]

When initializing PlotWidget, parent and background are passed to GraphicsWidget.__init__() and all others are passed to PlotItem.__init__().

Methods

_safe_limits_changed(val)

set_duration(dur)

set_safe_limits(limits)

update_value(new_value)

new_value: (timestamp from time.time(), value)

limits_changed(*args, **kwargs) = <PySide2.QtCore.Signal object>
set_duration(dur)[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>
update_value(new_value)[source]

new_value: (timestamp from time.time(), value)

_safe_limits_changed(val)[source]
set_safe_limits(limits)[source]

Status Bar

Classes

HeartBeat([update_interval, timeout_dur])

param update_interval

How often to do the heartbeat, in ms

Message_Display()

Power_Button()

Status_Bar()

  • Start/stop button

class vent.gui.widgets.status_bar.Status_Bar[source]
  • Start/stop button

  • Status indicator - a clock that increments with heartbeats,

    or some other visual indicator that things are alright

  • Status bar - most recent alarm or notification w/ means of clearing

  • Override to give 100% oxygen and silence all alarms

Methods

init_ui()

init_ui()[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>
class vent.gui.widgets.status_bar.Message_Display[source]

Attributes

alarm_level

Methods

clear_message()

draw_state([state])

init_ui()

make_icons()

update_message(alarm)

param alarm

message_cleared(*args, **kwargs) = <PySide2.QtCore.Signal object>
level_changed(*args, **kwargs) = <PySide2.QtCore.Signal object>
make_icons()[source]
init_ui()[source]
draw_state(state=None)[source]
update_message(alarm)[source]
Parameters

alarm (Alarm) –

clear_message()[source]
property alarm_level
staticMetaObject = <PySide2.QtCore.QMetaObject object>
class vent.gui.widgets.status_bar.HeartBeat(update_interval=100, timeout_dur=5000)[source]
Parameters
  • update_interval (int) – How often to do the heartbeat, in ms

  • timeout (int) – how long to wait before hearing from control process

Methods

__init__([update_interval, timeout_dur])

param update_interval

How often to do the heartbeat, in ms

_heartbeat()

Called every (update_interval) milliseconds to set the text of the timer.

beatheart(heartbeat_time)

check_timeout()

init_ui()

set_indicator([state])

start_timer([update_interval])

param update_interval

How often (in ms) the timer should be updated.

stop_timer()

you can read the sign ya punk

timeout(*args, **kwargs) = <PySide2.QtCore.Signal object>
heartbeat(*args, **kwargs) = <PySide2.QtCore.Signal object>
__init__(update_interval=100, timeout_dur=5000)[source]
Parameters
  • update_interval (int) – How often to do the heartbeat, in ms

  • timeout (int) – how long to wait before hearing from control process

init_ui()[source]
check_timeout()[source]
set_indicator(state=None)[source]
start_timer(update_interval=None)[source]
Parameters

update_interval (float) – How often (in ms) the timer should be updated.

stop_timer()[source]

you can read the sign ya punk

beatheart(heartbeat_time)[source]
_heartbeat()[source]

Called every (update_interval) milliseconds to set the text of the timer.

staticMetaObject = <PySide2.QtCore.QMetaObject object>
class vent.gui.widgets.status_bar.Power_Button[source]

Methods

init_ui()

init_ui()[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>

Components

Classes

DoubleSlider([decimals])

Slider capable of representing floats

EditableLabel([parent])

Editable label

KeyPressHandler

Custom key press handler

QHLine([parent, color])

with respct to https://stackoverflow.com/a/51057516

RangeSlider(abs_range, safe_range[, decimals])

Slider with two handles that sets a range

class vent.gui.widgets.components.DoubleSlider(decimals=1, *args, **kargs)[source]

Slider capable of representing floats

Ripped off from and https://stackoverflow.com/a/50300848 ,

Thank you!!!

Methods

_maximum()

_minimum()

_singleStep()

emitDoubleValueChanged()

maximum(self)

minimum(self)

setDecimals(decimals)

setMaximum(self, arg__1)

setMinimum(self, arg__1)

setSingleStep(self, arg__1)

setValue(self, arg__1)

singleStep(self)

value(self)

doubleValueChanged(*args, **kwargs) = <PySide2.QtCore.Signal object>
setDecimals(decimals)[source]
emitDoubleValueChanged()[source]
value(self)int[source]
setMinimum(self, arg__1: int)[source]
setMaximum(self, arg__1: int)[source]
minimum(self)int[source]
_minimum()[source]
maximum(self)int[source]
_maximum()[source]
setSingleStep(self, arg__1: int)[source]
singleStep(self)int[source]
_singleStep()[source]
setValue(self, arg__1: int)[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>
class vent.gui.widgets.components.RangeSlider(abs_range, safe_range, decimals=1, *args, **kwargs)[source]

Slider with two handles that sets a range

Parameters
  • abs_range (tuple) – absolute range of slider

  • safe_range (tuple) – default set values for handles of slider

  • decimals (int) – number of decimals of precision

  • *args

  • **kwargs

Methods

__pick(pt)

__pixelPosToRangeValue(pos)

__init__(abs_range, safe_range[, decimals])

Slider with two handles that sets a range

generate_labels()

Generate the text labels for the slider.

mouseMoveEvent(self, ev)

mousePressEvent(self, ev)

paintEvent(self, ev)

resizeEvent(self, event)

setHigh(high)

setLow(low)

setValue(value)

param value

(low, high) to set

value(self)

Attributes

high

low

valueChanged(*args, **kwargs)

(tuple): (low, high) set range of floats

valueChanged(*args, **kwargs) = <PySide2.QtCore.Signal object>

(low, high) set range of floats

Type

(tuple)

__init__(abs_range, safe_range, decimals=1, *args, **kwargs)[source]

Slider with two handles that sets a range

Parameters
  • abs_range (tuple) – absolute range of slider

  • safe_range (tuple) – default set values for handles of slider

  • decimals (int) – number of decimals of precision

  • *args

  • **kwargs

property low
property high
setLow(low)[source]
setHigh(high)[source]
setValue(value)[source]
Parameters

value (tuple) – (low, high) to set

value(self)int[source]
generate_labels()[source]

Generate the text labels for the slider.

Called on init and on resizeEvent

paintEvent(self, ev: PySide2.QtGui.QPaintEvent)[source]
mousePressEvent(self, ev: PySide2.QtGui.QMouseEvent)[source]
mouseMoveEvent(self, ev: PySide2.QtGui.QMouseEvent)[source]
__pick(pt)
__pixelPosToRangeValue(pos)
resizeEvent(self, event: PySide2.QtGui.QResizeEvent)[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>
class vent.gui.widgets.components.KeyPressHandler[source]

Custom key press handler https://gist.github.com/mfessenden/baa2b87b8addb0b60e54a11c1da48046

Methods

eventFilter(self, watched, event)

escapePressed(*args, **kwargs) = <PySide2.QtCore.Signal object>
returnPressed(*args, **kwargs) = <PySide2.QtCore.Signal object>
eventFilter(self, watched: PySide2.QtCore.QObject, event: PySide2.QtCore.QEvent)bool[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>
class vent.gui.widgets.components.EditableLabel(parent=None, **kwargs)[source]

Editable label https://gist.github.com/mfessenden/baa2b87b8addb0b60e54a11c1da48046

Methods

create_signals()

escapePressedAction()

Escape event handler

labelPressedEvent(event)

Set editable if the left mouse button is clicked

labelUpdatedAction()

Indicates the widget text has been updated

returnPressedAction()

Return/enter event handler

setLabelEditableAction()

Action to make the widget editable

setText(text)

Standard QLabel text setter

text()

Standard QLabel text getter

textChanged(*args, **kwargs) = <PySide2.QtCore.Signal object>
create_signals()[source]
text()[source]

Standard QLabel text getter

setText(text)[source]

Standard QLabel text setter

labelPressedEvent(event)[source]

Set editable if the left mouse button is clicked

setLabelEditableAction()[source]

Action to make the widget editable

labelUpdatedAction()[source]

Indicates the widget text has been updated

returnPressedAction()[source]

Return/enter event handler

escapePressedAction()[source]

Escape event handler

staticMetaObject = <PySide2.QtCore.QMetaObject object>
class vent.gui.widgets.components.QHLine(parent=None, color='#FFFFFF')[source]

with respct to https://stackoverflow.com/a/51057516

Methods

setColor(color)

staticMetaObject = <PySide2.QtCore.QMetaObject object>
setColor(color)[source]

GUI Stylesheets

Data

MONITOR_UPDATE_INTERVAL

(float): inter-update interval (seconds) for Monitor

Functions

set_dark_palette(app)

Apply Dark Theme to the Qt application instance.

vent.gui.styles.MONITOR_UPDATE_INTERVAL = 0.5

inter-update interval (seconds) for Monitor

Type

(float)

vent.gui.styles.set_dark_palette(app)[source]

Apply Dark Theme to the Qt application instance.

borrowed from https://github.com/gmarull/qtmodern/blob/master/qtmodern/styles.py
Args:

app (QApplication): QApplication instance.

GUI Alarm Manager

Classes

AlarmManager()

Functions

get_alarm_manager()

vent.gui.alarm_manager.get_alarm_manager()[source]
class vent.gui.alarm_manager.AlarmManager[source]

Methods

monitor_alarm(alarm)

Parse a tentative alarm from a monitor – we should have already gotten an alarm from the controller, so this largely serves as a double check.

parse_message(alarm)

If an alarm doesn’t have a message attr, make one for it.

update_alarms(alarms)

new_alarm(*args, **kwargs) = <PySide2.QtCore.Signal object>
update_alarms(alarms)[source]
monitor_alarm(alarm)[source]

Parse a tentative alarm from a monitor – we should have already gotten an alarm from the controller, so this largely serves as a double check.

Doesn’t use the Alarm class because creating a new alarm increments the counter.

Parameters

alarm (tuple) – (monitor_name, monitor_value, timestamp)

parse_message(alarm)[source]

If an alarm doesn’t have a message attr, make one for it.

staticMetaObject = <PySide2.QtCore.QMetaObject object>