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

  • RR always has to be present, can only auto calculate InspT, I:E

  • make alarm dismissals all latch and snooze.

jonny todo

  • use loop_counter to check on controller advancement

  • choice between pressure/volume over time and combined P/V plot

  • display flow in SLM (liters per minute)

  • deque for alarm manager logged alarms

  • need confirmation for start button

GUI Object Documentation

Display

Unified monitor & control widgets

Display sensor values, control control values, turns out it’s all the same

Classes

Display(value, update_period, enum_name, …)

param value

Value Object to display

Limits_Plot(style, *args, **kwargs)

Widget to display current value in a bar graph along with alarm limits

class pvp.gui.widgets.display.Display(value: pvp.common.values.Value, update_period: float = 0.5, enum_name: pvp.common.values.ValueName = None, button_orientation: str = 'left', control_type: Union[None, str] = None, style: str = 'dark', *args, **kwargs)[source]
Parameters
  • value (Value) – Value Object to display

  • update_period (float) – time to wait in between updating displayed value

  • enum_name (ValueName) – Value name (not in Value objects)

  • (str (style) – ‘left’ or ‘right’): whether the button should be on the left or right

  • (None, str (control_type) – ‘slider’, ‘record’): whether a slider, a button to record recent values, or None control should be used with this object

  • (str – ‘light’, ‘dark’, or a QtStylesheet string): _style for the display

  • **kwargs (*args,) –

    passed to PySide2.QtWidgets.QWidget

Methods

__init__(value, update_period, enum_name, …)

param value

Value Object to display

_value_changed(new_value)

“outward-directed” Control value changed by components

init_ui()

init_ui_labels()

init_ui_layout()

Basically two methods…

init_ui_limits()

Create widgets to display sensed value alongside set value

init_ui_record()

init_ui_signals()

init_ui_slider()

init_ui_toggle_button()

redraw()

Redraw all graphical elements to ensure internal model matches view

set_locked(locked)

set_units(units)

Set pressure units to display as cmH2O or hPa

timed_update()

toggle_control(state)

toggle_record(state)

update_limits(control)

Update the alarm range and the GUI elements corresponding to it

update_sensor_value(new_value)

update_set_value(new_value)

inward value setting (set from elsewhere)

Attributes

alarm_state

is_set

self.name
self.units
self.abs_range
self.safe_range
self.decimals
self.update_period
self.enum_name
self.orientation
self.control
self._style
self.set_value
self.sensor_value
limits_changed(*args, **kwargs) = <PySide2.QtCore.Signal object>
value_changed(*args, **kwargs) = <PySide2.QtCore.Signal object>
__init__(value: pvp.common.values.Value, update_period: float = 0.5, enum_name: pvp.common.values.ValueName = None, button_orientation: str = 'left', control_type: Union[None, str] = None, style: str = 'dark', *args, **kwargs)[source]
Parameters
  • value (Value) – Value Object to display

  • update_period (float) – time to wait in between updating displayed value

  • enum_name (ValueName) – Value name (not in Value objects)

  • (str (style) – ‘left’ or ‘right’): whether the button should be on the left or right

  • (None, str (control_type) – ‘slider’, ‘record’): whether a slider, a button to record recent values, or None control should be used with this object

  • (str – ‘light’, ‘dark’, or a QtStylesheet string): _style for the display

  • **kwargs (*args,) –

    passed to PySide2.QtWidgets.QWidget

self.name
self.units
self.abs_range
self.safe_range
self.decimals
self.update_period
self.enum_name
self.orientation
self.control
self._style
self.set_value
self.sensor_value
init_ui()[source]
init_ui_labels()[source]
init_ui_toggle_button()[source]
init_ui_limits()[source]

Create widgets to display sensed value alongside set value

init_ui_slider()[source]
init_ui_record()[source]
init_ui_layout()[source]

Basically two methods… lay objects out depending on whether we’re oriented with our button to the left or right

init_ui_signals()[source]
toggle_control(state)[source]
toggle_record(state)[source]
_value_changed(new_value: float)[source]

“outward-directed” Control value changed by components

Parameters
  • new_value (float) –

  • emit (bool) – whether to emit the value_changed signal (default True) – in the case that our value is being changed by someone other than us

update_set_value(new_value: float)[source]

inward value setting (set from elsewhere)

update_sensor_value(new_value: float)[source]
update_limits(control: pvp.common.message.ControlSetting)[source]

Update the alarm range and the GUI elements corresponding to it

Parameters

control (ControlSetting) – control setting with min_value or max_value

redraw()[source]

Redraw all graphical elements to ensure internal model matches view

timed_update()[source]
set_units(units: str)[source]

Set pressure units to display as cmH2O or hPa

Parameters

units ('cmH2O', 'hPa') –

set_locked(locked: bool)[source]
property is_set
property alarm_state
staticMetaObject = <PySide2.QtCore.QMetaObject object>
class pvp.gui.widgets.display.Limits_Plot(style: str = 'light', *args, **kwargs)[source]

Widget to display current value in a bar graph along with alarm limits

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

Methods

init_ui()

update_value(min, max, sensor_value, set_value)

Move the lines! Pass any of the represented values

update_yrange()

init_ui()[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>
update_value(min: float = None, max: float = None, sensor_value: float = None, set_value: float = None)[source]

Move the lines! Pass any of the represented values

Parameters
  • min (float) – new alarm minimum

  • max (float) – new alarm _maximum

  • sensor_value (float) – new value for the sensor bar plot

  • set_value (float) – new value for the set value line

update_yrange()[source]

Control Panel

Classes

Control_Panel()

  • Start/stop button

HeartBeat([update_interval, timeout_dur])

pvp.gui.widgets.control_panel._state

Lock_Button(*args, **kwargs)

Power_Button()

Start_Button(*args, **kwargs)

StopWatch(update_interval, *args, **kwargs)

param update_interval

update clock every n seconds

class pvp.gui.widgets.control_panel.Control_Panel[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

_pressure_units_changed(button)

add_alarm(alarm)

Wraps Alarm_Bar.add_alarm()

clear_alarm(alarm, alarm_type)

Wraps Alarm_Bar.clear_alarm()

init_ui()

pressure_units_changed(*args, **kwargs) = <PySide2.QtCore.Signal object>
cycle_autoset_changed(*args, **kwargs) = <PySide2.QtCore.Signal object>
init_ui()[source]
add_alarm(alarm: pvp.alarm.alarm.Alarm)[source]

Wraps Alarm_Bar.add_alarm()

Parameters

alarm (Alarm) – passed to Alarm_Bar

clear_alarm(alarm: pvp.alarm.alarm.Alarm = None, alarm_type: pvp.alarm.AlarmType = None)[source]

Wraps Alarm_Bar.clear_alarm()

_pressure_units_changed(button)[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>
class pvp.gui.widgets.control_panel.Start_Button(*args, **kwargs)[source]

Methods

load_pixmaps()

set_state(state)

Should only be called by other objects (as there are checks to whether it’s ok to start/stop that we shouldn’t be aware of)

Attributes

states

Built-in mutable sequence.

states = ['OFF', 'ON', 'ALARM']
load_pixmaps()[source]
set_state(state)[source]

Should only be called by other objects (as there are checks to whether it’s ok to start/stop that we shouldn’t be aware of)

Parameters

state (str) – ('OFF', 'ON', 'ALARM')

staticMetaObject = <PySide2.QtCore.QMetaObject object>
class pvp.gui.widgets.control_panel.Lock_Button(*args, **kwargs)[source]

Methods

load_pixmaps()

set_state(state)

Should only be called by other objects (as there are checks to whether it’s ok to start/stop that we shouldn’t be aware of)

Attributes

states

Built-in mutable sequence.

states = ['DISABLED', 'UNLOCKED', 'LOCKED']
load_pixmaps()[source]
set_state(state)[source]

Should only be called by other objects (as there are checks to whether it’s ok to start/stop that we shouldn’t be aware of)

Parameters

state (str) – ('OFF', 'ON', 'ALARM')

staticMetaObject = <PySide2.QtCore.QMetaObject object>
class pvp.gui.widgets.control_panel.HeartBeat(update_interval=100, timeout_dur=5000)[source]

Methods

__init__([update_interval, timeout_dur])

pvp.gui.widgets.control_panel._state

_heartbeat()

Called every (update_interval) milliseconds to set the check the status of the heartbeat.

beatheart(heartbeat_time)

init_ui()

set_indicator([state])

set_state(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

_state

whether the system is running or not

Type

bool

Parameters
  • update_interval (int) – How often to do the heartbeat, in ms

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

timeout(*args, **kwargs) = <PySide2.QtCore.Signal object>
heartbeat(*args, **kwargs) = <PySide2.QtCore.Signal object>
__init__(update_interval=100, timeout_dur=5000)[source]
_state

whether the system is running or not

Type

bool

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]
set_state(state)[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 check the status of the heartbeat.

staticMetaObject = <PySide2.QtCore.QMetaObject object>
class pvp.gui.widgets.control_panel.StopWatch(update_interval: float = 100, *args, **kwargs)[source]
Parameters
  • update_interval (float) – update clock every n seconds

  • *args

  • **kwargs

Methods

__init__(update_interval, *args, **kwargs)

param update_interval

update clock every n seconds

_update_time()

init_ui()

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

__init__(update_interval: float = 100, *args, **kwargs)[source]
Parameters
  • update_interval (float) – update clock every n seconds

  • *args

  • **kwargs

init_ui()[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

_update_time()[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>
class pvp.gui.widgets.control_panel.Power_Button[source]

Methods

init_ui()

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

Plot

Classes

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

param name

Plot_Container(plot_descriptors, …)

Data

PLOT_FREQ

Update frequency of Plot s in Hz

PLOT_TIMER

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

pvp.gui.widgets.plot.PLOT_TIMER = None

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

pvp.gui.widgets.plot.PLOT_FREQ = 5

Update frequency of Plot s in Hz

class pvp.gui.widgets.plot.Plot(name, buffer_size=4092, plot_duration=10, abs_range=None, plot_limits: tuple = None, color=None, units='', **kwargs)[source]
Parameters
  • name

  • buffer_size

  • plot_duration

  • abs_range

  • plot_limits (tuple) – tuple of (ValueName)s for which to make pairs of min and max range lines

  • color

  • units

  • **kwargs

Methods

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

param name

_safe_limits_changed(val)

reset_start_time()

set_duration(dur)

set_safe_limits(limits)

set_units(units)

update_value(new_value)

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

limits_changed(*args, **kwargs) = <PySide2.QtCore.Signal object>
__init__(name, buffer_size=4092, plot_duration=10, abs_range=None, plot_limits: tuple = None, color=None, units='', **kwargs)[source]
Parameters
  • name

  • buffer_size

  • plot_duration

  • abs_range

  • plot_limits (tuple) – tuple of (ValueName)s for which to make pairs of min and max range lines

  • color

  • units

  • **kwargs

set_duration(dur)[source]
update_value(new_value: tuple)[source]

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

_safe_limits_changed(val)[source]
set_safe_limits(limits: pvp.common.message.ControlSetting)[source]
reset_start_time()[source]
set_units(units)[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>
class pvp.gui.widgets.plot.Plot_Container(plot_descriptors: Dict[pvp.common.values.ValueName, pvp.common.values.Value], *args, **kwargs)[source]

Methods

init_ui()

reset_start_time()

set_duration(duration)

set_plot_mode()

set_safe_limits(control)

toggle_plot(state)

update_value(vals)

init_ui()[source]
update_value(vals: pvp.common.message.SensorValues)[source]
toggle_plot(state: bool)[source]
set_safe_limits(control: pvp.common.message.ControlSetting)[source]
set_duration(duration: float)[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>
reset_start_time()[source]
set_plot_mode()[source]

Alarm Bar

Components

Classes

DoubleSlider([decimals])

Slider capable of representing floats

EditableLabel([parent])

Editable label

KeyPressHandler

Custom key press handler

OnOffButton(state_labels, str] =, toggled, …)

Simple extension of toggle button with styling for clearer ‘ON’ vs ‘OFF’

QHLine([parent, color])

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

QVLine([parent, color])

class pvp.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 pvp.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 pvp.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

setEditable(editable)

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

setEditable(editable: bool)[source]
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 pvp.gui.widgets.components.QHLine(parent=None, color='#FFFFFF')[source]

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

Methods

setColor(color)

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

Methods

setColor(color)

setColor(color)[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>
class pvp.gui.widgets.components.OnOffButton(state_labels: Tuple[str, str] = 'ON', 'OFF', toggled: bool = False, *args, **kwargs)[source]

Simple extension of toggle button with styling for clearer ‘ON’ vs ‘OFF’

Parameters
  • state_labels (tuple) – tuple of strings to set when toggled and untoggled

  • toggled (bool) – initialize the button as toggled

  • *args – passed to QPushButton

  • **kwargs – passed to QPushButton

Methods

__init__(state_labels, str] =, toggled, …)

param state_labels

tuple of strings to set when toggled and untoggled

set_state(state)

__init__(state_labels: Tuple[str, str] = 'ON', 'OFF', toggled: bool = False, *args, **kwargs)[source]
Parameters
  • state_labels (tuple) – tuple of strings to set when toggled and untoggled

  • toggled (bool) – initialize the button as toggled

  • *args – passed to QPushButton

  • **kwargs – passed to QPushButton

set_state(state: bool)[source]
staticMetaObject = <PySide2.QtCore.QMetaObject object>

Dialog

Functions

pop_dialog(message, sub_message, modality, …)

Creates a dialog box to display a message.

pvp.gui.widgets.dialog.pop_dialog(message: str, sub_message: str = None, modality: <class 'PySide2.QtCore.Qt.WindowModality'> = PySide2.QtCore.Qt.WindowModality.NonModal, buttons: <class 'PySide2.QtWidgets.QMessageBox.StandardButton'> = PySide2.QtWidgets.QMessageBox.StandardButton.Ok, default_button: <class 'PySide2.QtWidgets.QMessageBox.StandardButton'> = PySide2.QtWidgets.QMessageBox.StandardButton.Ok)[source]

Creates a dialog box to display a message.

Note

This function does not call .exec_ on the dialog so that it can be managed by the caller.

Parameters
  • message (str) – Message to be displayed

  • sub_message (str) – Smaller message displayed below main message (InformativeText)

  • modality (QtCore.Qt.WindowModality) – Modality of dialog box - QtCore.Qt.NonModal (default) is unblocking, QtCore.Qt.WindowModal is blocking

  • buttons (QtWidgets.QMessageBox.StandardButton) – Buttons for the window, can be | ed together

  • default_button (QtWidgets.QMessageBox.StandardButton) – one of buttons , the highlighted button

Returns

QtWidgets.QMessageBox

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.

pvp.gui.styles.MONITOR_UPDATE_INTERVAL = 0.5

inter-update interval (seconds) for Monitor

Type

(float)

pvp.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.