# import system modules import sys # import project classes # import names from dialin.squish import utils, denaliMessages from dialin.squish.denaliMessages import txStates, EResponse # Import PySide2 classes from PySide2.QtUiTools import QUiLoader from PySide2 import QtCore, QtWidgets from PySide2.QtCore import QFile, Slot from PySide2.QtGui import qApp from PySide2.QtCore import QTimer from dialin.common import Ranges class RunTimeWidget: """ the parent class of all the run time loadable widgets """ loader: QUiLoader window: QtWidgets.QWidget def __init__(self, ui_name: str): self.ui_name = ui_name self.loader = QUiLoader() self.__load_ui() def __load_ui(self): """ loads the ui file of the GUI at run time :return: none """ ui_file = QFile(self.ui_name) ui_file.open(QFile.ReadOnly) self.window = self.loader.load(ui_file) # assert if the ui file can't be loaded error = self.loader.errorString() assert len(error) == 0, error ui_file.close() def show(self): """ shows the main container window :return: none """ self.window.show() def __find_child(self, child_type, child_name: str) -> QtWidgets.QWidget: """ finds a child in the loaded ui and returns the reference to it if found otherwise quits the application :param child_type: (var) type of the child :param child_name: (str) name of the child :return: (QtWidgets.QWidget) reference to the child """ child: QtWidgets.QWidget = self.window.findChild(child_type, child_name) assert child is not None, "child name '{}' with type '{}' can't be found.".format(child_name, child_type) return child def find_label(self, name: str) -> QtWidgets.QLabel: """ convenient method of find_child for QLabel :param name: (str) name of the QLabel Object :return: (QLabel) reference to the QLabel """ child = self.__find_child(QtWidgets.QLabel, name) return child def find_button(self, name: str) -> QtWidgets.QPushButton: """ convenient method of find_child for QPushButton :param name: (str) name of the QPushButton Object :return: (QPushButton) reference to the QPushButton """ child = self.__find_child(QtWidgets.QPushButton, name) return child def find_combobox(self, name: str) -> QtWidgets.QComboBox: """ convenient method of find_child for QComboBox :param name: (str) name of the QComboBox Object :return: (QComboBox) reference to the QComboBox """ child = self.__find_child(QtWidgets.QComboBox, name) return child def find_checkox(self, name: str) -> QtWidgets.QCheckBox: """ convenient method of find_child for QCheckBox :param name: (str) name of the QCheckBox Object :return: (QCheckBox) reference to the QComboBox """ child = self.__find_child(QtWidgets.QCheckBox, name) return child def find_spinbox(self, name: str) -> QtWidgets.QSpinBox: """ convenient method of find_child for QSpinBox :param name: (str) name of the QSpinBox Object :return: (QSpinBox) reference to the QSpinBox """ child = self.__find_child(QtWidgets.QSpinBox, name) return child def find_slider(self, name: str) -> QtWidgets.QSlider: """ convenient method of find_child for QSlider :param name: (str) name of the QSlider Object :return: (QSlider) reference to the QSlider """ child = self.__find_child(QtWidgets.QSlider, name) return child def find_table_widget(self, name: str) -> QtWidgets.QTableWidget: """ convenient method of find_child for QTableWidget :param name: (str) name of the QTableWidget Object :return: (QTableWidget) reference to the QTableWidget """ child: QtWidgets.QTableWidget = self.__find_child(QtWidgets.QTableWidget, name) return child class Simulator(RunTimeWidget): """ The simulator class which loads the ui file dynamically and initializes the objects and can be eventually shown. Note: this class is growing fast and seems like needs to be multiple classes """ # global variables declarations ui_file_name = "Simulator.ui" timer: QTimer # checkbox chkRangesBroadcast: QtWidgets.QCheckBox # pushbutton btnSalineAccept: QtWidgets.QPushButton btnSalineReject: QtWidgets.QPushButton btnUfPauseAccept: QtWidgets.QPushButton btnUfPauseReject: QtWidgets.QPushButton btnUfResumeAccept: QtWidgets.QPushButton btnUfResumeReject: QtWidgets.QPushButton btnUfEditAccept: QtWidgets.QPushButton btnUfEditReject: QtWidgets.QPushButton btnAVPressuresAccept: QtWidgets.QPushButton btnAVPressuresReject: QtWidgets.QPushButton # label lblSalineAction: QtWidgets.QLabel lblUfPauseAction: QtWidgets.QLabel lblUfResumeAction: QtWidgets.QLabel lblUfEditAction: QtWidgets.QLabel lblArterialTitle: QtWidgets.QLabel lblVenousTitle: QtWidgets.QLabel lblAVPressuresAction: QtWidgets.QLabel lblArterialLimitLow: QtWidgets.QLabel lblArterialLimitHigh: QtWidgets.QLabel lblVenousLimitLow: QtWidgets.QLabel lblVenousLimitHigh: QtWidgets.QLabel # spinbox spnSalineRejectReason: QtWidgets.QSpinBox spnUfPauseRejectReason: QtWidgets.QSpinBox spnUfResumeRejectReason: QtWidgets.QSpinBox spnUfEditRejectReason: QtWidgets.QSpinBox spnAVPressuresRejectReason: QtWidgets.QSpinBox spnDurationMin: QtWidgets.QSpinBox spnDurationMax: QtWidgets.QSpinBox spnUFVolumeMin: QtWidgets.QSpinBox spnUFVolumeMax: QtWidgets.QSpinBox spnDialysateMin: QtWidgets.QSpinBox spnDialysateMax: QtWidgets.QSpinBox spnArterialLimitLow: QtWidgets.QSpinBox spnArterialLimitHigh: QtWidgets.QSpinBox spnVenousLimitLow: QtWidgets.QSpinBox spnVenousLimitHigh: QtWidgets.QSpinBox # combobox cmbSalineAcceptTarget: QtWidgets.QComboBox # sliders sldSalineTarget: QtWidgets.QSlider sldSalineCumulative: QtWidgets.QSlider sldSalineVolume: QtWidgets.QSlider sldUfVolume: QtWidgets.QSlider sldArterialValue: QtWidgets.QSlider sldVenousValue: QtWidgets.QSlider # tables tblSalineSubMode: QtWidgets.QTableWidget tblSalineUFStates: QtWidgets.QTableWidget tblSalineSalineStates: QtWidgets.QTableWidget # static class variables saline_requested_state: txStates def __init__(self): super().__init__(Simulator.ui_file_name) self.saline_requested_state = txStates.SALINE_BOLUS_STATE_IDLE self.initialize() def setup_ranges(self): """ sets up the treatment ranges timer and 1 sec interval :return: none """ self.spnDurationMin = self.find_spinbox('spnDurationMin') self.spnDurationMax = self.find_spinbox('spnDurationMax') self.spnUFVolumeMin = self.find_spinbox('spnUFVolumeMin') self.spnUFVolumeMax = self.find_spinbox('spnUFVolumeMax') self.spnDialysateMin = self.find_spinbox('spnDialysateMin') self.spnDialysateMax = self.find_spinbox('spnDialysateMax') self.chkRangesBroadcast = self.find_checkox('chkRangesBroadcast') self.timer = QTimer() self.timer.start(1000) self.timer.timeout.connect(self.do_ranges_data) def setup_uf_adjustment(self): """ sets up the treatment Ultrafiltration adjustment GUI section :return: none """ self.btnUfPauseAccept = self.find_button('btnUfPauseAccept') self.btnUfPauseReject = self.find_button('btnUfPauseReject') self.btnUfResumeAccept = self.find_button('btnUfResumeAccept') self.btnUfResumeReject = self.find_button('btnUfResumeReject') self.btnUfEditAccept = self.find_button('btnUfEditAccept') self.btnUfEditReject = self.find_button('btnUfEditReject') self.lblUfPauseAction = self.find_label('lblUfPauseAction') self.lblUfResumeAction = self.find_label('lblUfResumeAction') self.lblUfEditAction = self.find_label('lblUfEditAction') self.spnUfPauseRejectReason = self.find_spinbox('spnUfPauseRejectReason') self.spnUfResumeRejectReason = self.find_spinbox('spnUfResumeRejectReason') self.spnUfEditRejectReason = self.find_spinbox('spnUfEditRejectReason') self.sldUfVolume = self.find_slider('sldUfVolume') self.btnUfPauseAccept.clicked.connect(self.do_uf_pause_accept) self.btnUfPauseReject.clicked.connect(self.do_uf_pause_reject) self.btnUfResumeAccept.clicked.connect(self.do_uf_resume_accept) self.btnUfResumeReject.clicked.connect(self.do_uf_resume_reject) self.btnUfEditAccept.clicked.connect(self.do_uf_edit_accept) self.btnUfEditReject.clicked.connect(self.do_uf_edit_reject) self.sldUfVolume.valueChanged.connect(self.do_uf_volume_data) def setup_saline_adjustment(self): """ sets up the treatment saline bolus adjustment GUI section :return: none """ self.btnSalineAccept = self.find_button('btnSalineAccept') self.btnSalineReject = self.find_button('btnSalineReject') self.lblSalineAction = self.find_label('lblSalineAction') self.spnSalineRejectReason = self.find_spinbox('spnSalineRejectReason') self.cmbSalineAcceptTarget = self.find_combobox('cmbSalineAcceptTarget') self.btnSalineAccept.clicked.connect(self.do_sb_accept) self.btnSalineReject.clicked.connect(self.do_sb_reject) def setup_saline_data(self): """ sets up the treatment saline bolus data sender GUI section :return: none """ self.sldSalineTarget = self.find_slider('sldSalineTarget') self.sldSalineCumulative = self.find_slider('sldSalineCumulative') self.sldSalineVolume = self.find_slider('sldSalineVolume') self.sldSalineTarget.valueChanged.connect(self.do_saline_data) self.sldSalineCumulative.valueChanged.connect(self.do_saline_data) self.sldSalineVolume.valueChanged.connect(self.do_saline_data) def setup_treatment_states(self): """ sets up the treatment saline bolus states GUI section :return: none """ self.tblSalineSubMode = self.find_table_widget('tblSalineSubMode') self.tblSalineUFStates = self.find_table_widget('tblSalineUFStates') self.tblSalineSalineStates = self.find_table_widget('tblSalineSalineStates') self.tblSalineSubMode.setCurrentCell(0, 0) self.tblSalineUFStates.setCurrentCell(0, 0) self.tblSalineSalineStates.setCurrentCell(0, 0) self.tblSalineSubMode.cellClicked.connect(self.do_saline_saline_state) self.tblSalineUFStates.cellClicked.connect(self.do_saline_saline_state) self.tblSalineSalineStates.cellClicked.connect(self.do_saline_saline_state) def setup_pressures_limits(self): """ sets up the treatment pressures :return: none """ self.lblArterialTitle = self.find_label('lblArterialTitle') self.lblVenousTitle = self.find_label('lblVenousTitle') self.lblArterialLimitLow = self.find_label('lblArterialLimitLow') self.lblArterialLimitHigh = self.find_label('lblArterialLimitHigh') self.lblVenousLimitLow = self.find_label('lblVenousLimitLow') self.lblVenousLimitHigh = self.find_label('lblVenousLimitHigh') self.lblAVPressuresAction = self.find_label('lblAVPressuresAction') self.btnAVPressuresAccept = self.find_button('btnAVPressuresAccept') self.btnAVPressuresReject = self.find_button('btnAVPressuresReject') self.spnArterialLimitLow = self.find_spinbox('spnArterialLimitLow') self.spnArterialLimitHigh = self.find_spinbox('spnArterialLimitHigh') self.spnVenousLimitLow = self.find_spinbox('spnVenousLimitLow') self.spnVenousLimitHigh = self.find_spinbox('spnVenousLimitHigh') self.spnAVPressuresRejectReason = self.find_spinbox('spnAVPressuresRejectReason') self.sldArterialValue = self.find_slider('sldArterialValue') self.sldVenousValue = self.find_slider('sldVenousValue') self.lblArterialTitle.setText( f"{self.lblArterialTitle.text()} [{Ranges.ARTERIAL_PRESSURE_MINIMUM},{Ranges.ARTERIAL_PRESSURE_MAXIMUM}]" ) self.lblVenousTitle.setText( f"{self.lblVenousTitle.text()} [{Ranges.VENOUS_PRESSURE_MINIMUM},{Ranges.VENOUS_PRESSURE_MAXIMUM}]" ) self.lblArterialLimitLow.setText(f"{Ranges.ARTERIAL_PRESSURE_LOW_MIN}\n{Ranges.ARTERIAL_PRESSURE_LOW_MAX}") self.lblArterialLimitHigh.setText(f"{Ranges.ARTERIAL_PRESSURE_HIGH_MIN}\n{Ranges.ARTERIAL_PRESSURE_HIGH_MAX}") self.lblVenousLimitLow.setText(f"{Ranges.VENOUS_PRESSURE_LOW_MIN}\n{Ranges.VENOUS_PRESSURE_LOW_MAX}") self.lblVenousLimitHigh.setText(f"{Ranges.VENOUS_PRESSURE_HIGH_MIN}\n{Ranges.VENOUS_PRESSURE_HIGH_MAX}") # sending the CANBus message when slider value changed self.sldArterialValue.valueChanged.connect(self.do_pressures_data) self.sldVenousValue.valueChanged.connect(self.do_pressures_data) # sending the CANBus message when accepted or rejected self.btnAVPressuresAccept.clicked.connect(self.do_pressures_limits_accept) self.btnAVPressuresReject.clicked.connect(self.do_pressures_limits_reject) self.sldArterialValue.setMinimum(Ranges.ARTERIAL_PRESSURE_MINIMUM) self.sldArterialValue.setMaximum(Ranges.ARTERIAL_PRESSURE_MAXIMUM) self.sldArterialValue.setValue(0) self.sldVenousValue.setMinimum(Ranges.VENOUS_PRESSURE_MINIMUM) self.sldVenousValue.setMaximum(Ranges.VENOUS_PRESSURE_MAXIMUM) self.sldVenousValue.setValue(0) self.spnArterialLimitLow.setMinimum(Ranges.ARTERIAL_PRESSURE_LOW_MIN) self.spnArterialLimitLow.setMaximum(Ranges.ARTERIAL_PRESSURE_LOW_MAX) self.spnArterialLimitLow.setValue(Ranges.ARTERIAL_PRESSURE_LOW_DEF) self.spnArterialLimitHigh.setMinimum(Ranges.ARTERIAL_PRESSURE_HIGH_MIN) self.spnArterialLimitHigh.setMaximum(Ranges.ARTERIAL_PRESSURE_HIGH_MAX) self.spnArterialLimitHigh.setValue(Ranges.ARTERIAL_PRESSURE_HIGH_DEF) self.spnVenousLimitLow.setMinimum(Ranges.VENOUS_PRESSURE_LOW_MIN) self.spnVenousLimitLow.setMaximum(Ranges.VENOUS_PRESSURE_LOW_MAX) self.spnVenousLimitLow.setValue(Ranges.VENOUS_PRESSURE_LOW_DEF) self.spnVenousLimitHigh.setMinimum(Ranges.VENOUS_PRESSURE_HIGH_MIN) self.spnVenousLimitHigh.setMaximum(Ranges.VENOUS_PRESSURE_HIGH_MAX) self.spnVenousLimitHigh.setValue(Ranges.VENOUS_PRESSURE_HIGH_DEF) @Slot() def do_sb_accept(self): """ the slot for accept saline bolus button :return: none """ # toggle the saline requested state if self.saline_requested_state == txStates.SALINE_BOLUS_STATE_IN_PROGRESS: self.saline_requested_state = txStates.SALINE_BOLUS_STATE_IDLE else: self.saline_requested_state = txStates.SALINE_BOLUS_STATE_IN_PROGRESS target = self.cmbSalineAcceptTarget.currentText() denaliMessages.setSalineBolusResponse(True, 0, target, self.saline_requested_state) self.lblSalineAction.setText('Accepted ' + target) @Slot() def do_sb_reject(self): """ the slot for accept saline bolus button :return: none """ reason = self.spnSalineRejectReason.value() denaliMessages.setSalineBolusResponse(False, reason, 0, self.saline_requested_state) self.lblSalineAction.setText('Rejected ' + "{}".format(reason)) @Slot() def do_uf_pause_accept(self): """ the slot for accept ultrafiltration pause button :return: none """ denaliMessages.setTreatmentAdjustUltrafiltrationStateResponse( EResponse.Accepted, 0, txStates.UF_PAUSED_STATE) self.lblUfPauseAction.setText('Accepted ') @Slot() def do_uf_pause_reject(self): """ the slot for reject ultrafiltration pause button :return: none """ reason = self.spnUfPauseRejectReason.value() denaliMessages.setTreatmentAdjustUltrafiltrationStateResponse( EResponse.Rejected, reason, txStates.UF_RUNNING_STATE) self.lblUfPauseAction.setText('Rejected ' + "{}".format(reason)) @Slot() def do_uf_resume_accept(self): """ the slot for accept ultrafiltration resume accept :return: none """ denaliMessages.setTreatmentAdjustUltrafiltrationStateResponse( EResponse.Accepted, 0, txStates.UF_RUNNING_STATE) self.lblUfResumeAction.setText('Accepted ') @Slot() def do_uf_resume_reject(self): """ the slot for reject ultrafiltration resume button :return: none """ reason = self.spnUfResumeRejectReason.value() denaliMessages.setTreatmentAdjustUltrafiltrationStateResponse( EResponse.Rejected, reason, txStates.UF_PAUSED_STATE) self.lblUfResumeAction.setText('Rejected ' + "{}".format(reason)) @Slot() def do_uf_edit_accept(self): """ the slot for accept ultrafiltration next button :return: none """ denaliMessages.setTreatmentAdjustUltrafiltrationEditResponse( EResponse.Accepted, 0, 2500, 60, 0, 10, 0, 10) self.lblUfEditAction.setText('Accepted ') @Slot() def do_uf_edit_reject(self): """ the slot for reject ultrafiltration next button :return: none """ reason = self.spnUfEditRejectReason.value() denaliMessages.setTreatmentAdjustUltrafiltrationEditResponse( EResponse.Rejected, reason, 2500, 60, 0, 10, 0, 10) self.lblUfEditAction.setText('Rejected ' + "{}".format(reason)) @Slot() def do_saline_saline_state(self): """ the slot for saline bolus state change :return: none """ sub_mode = self.tblSalineSubMode.verticalHeaderItem(self.tblSalineSubMode.currentRow()).text() uf_state = self.tblSalineUFStates.verticalHeaderItem(self.tblSalineUFStates.currentRow()).text() saline = self.tblSalineSalineStates.verticalHeaderItem(self.tblSalineSalineStates.currentRow()).text() denaliMessages.setTreatmentStatesData(sub_mode, uf_state, saline) @Slot() def do_saline_data(self): """ the slot which is called to send the saline bolus data by calling the denaliMessage API setTreatmentSalineBolusData :return: none """ denaliMessages.setTreatmentSalineBolusData(self.sldSalineTarget.value(), self.sldSalineCumulative.value(), self.sldSalineVolume.value()) @Slot() def do_uf_volume_data(self): """ sends the ultrafiltration delivered volume message :return: none """ denaliMessages.setTreatmentUltrafiltration(self.sldUfVolume.value(), 0, 0, 0, 0, 0, 0) @Slot() def do_ranges_data(self): """ sends the treatment ranges message with given value on the screen :return: none """ if self.chkRangesBroadcast.isChecked(): denaliMessages.setTreatmentParamRanges( self.spnDurationMin.value(), self.spnDurationMax.value(), self.spnUFVolumeMin.value(), self.spnUFVolumeMax.value(), self.spnDialysateMin.value(), self.spnDialysateMax.value() ) @Slot() def do_pressures_data(self): """ sends the pressures values message with given value on the screen :return: none """ denaliMessages.setPressureOcclusionData( self.sldArterialValue.value(), self.sldVenousValue.value(), 0, 0, 0 ) @Slot() def do_pressures_limits_accept(self): """ sends the pressures values message with given value on the screen :return: none """ # vAccepted, vReason, vArterialLow, vArterialHigh, vVenousLow, vVenousHigh denaliMessages.sendTreatmentAdjustPressuresLimitsResponse( EResponse.Accepted, 0, self.spnArterialLimitLow.value(), self.spnArterialLimitHigh.value(), self.spnVenousLimitLow.value(), self.spnVenousLimitHigh.value() ) self.lblAVPressuresAction.setText('Accepted ') @Slot() def do_pressures_limits_reject(self): """ sends the pressures values message with given value on the screen :return: none """ reason = self.spnAVPressuresRejectReason.value() denaliMessages.sendTreatmentAdjustPressuresLimitsResponse( EResponse.Rejected, reason, self.spnArterialLimitLow.value(), self.spnArterialLimitHigh.value(), self.spnVenousLimitLow.value(), self.spnVenousLimitHigh.value() ) self.lblAVPressuresAction.setText('Rejected ' + "{}".format(reason)) def initialize(self): """ initializes the class by calling it's initializer methods to make objects ready :return: none """ self.setup_ranges() self.setup_saline_adjustment() self.setup_saline_data() self.setup_uf_adjustment() self.setup_treatment_states() self.setup_pressures_limits() def main(): """ the main function which initializes the Simulator and starts it. :return: none """ utils.tstStart(__file__) # create qt application QtWidgets.QApplication(sys.argv) simulator = Simulator() simulator.show() utils.tstDone() # start qt application main loop sys.exit(qApp.exec_()) if __name__ == "__main__": QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts) main()