Index: simulator/plugins/__init__.py =================================================================== diff -u -r69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1 -r9206e36aeff354823f2c37103d79f548221cd64c --- simulator/plugins/__init__.py (.../__init__.py) (revision 69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1) +++ simulator/plugins/__init__.py (.../__init__.py) (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -8,3 +8,4 @@ from .treatmentranges.loader import Loader as TreatmentRanges from .heparin.loader import Loader as Heparin from .alarms.loader import Loader as Alarms +from .createtreatment.loader import Loader as CreateTreatment Index: simulator/plugins/alarms/loader.py =================================================================== diff -u -r69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1 -r9206e36aeff354823f2c37103d79f548221cd64c --- simulator/plugins/alarms/loader.py (.../loader.py) (revision 69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1) +++ simulator/plugins/alarms/loader.py (.../loader.py) (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -2,15 +2,15 @@ The Alarms ui loader class container file """ import os -from simulator.runtimewidget import RunTimeWidget +from simulator.dynamicloader import DynamicLoader from PySide2 import QtWidgets from PySide2.QtCore import Slot, QObject, Qt, QSize from dialin.squish import denaliMessages from datetime import datetime from dialin.common.prs_defs import AlarmPriority -class Loader(RunTimeWidget): +class Loader(DynamicLoader): """ The Alarms ui loader class """ @@ -67,11 +67,11 @@ def __init__(self): super().__init__(os.path.dirname(__file__)) - self.initialize() - self.init_widgets() - self.init_connections() + # self.initialize() + # self.init_widgets() + # self.init_connections() - def initialize(self): + def _init_loader(self): """ finds and creates widgets :return: none @@ -93,7 +93,7 @@ self.spnAlarmID = self.find_spinbox('spnAlarmID') self.tblTriggeredIDs = self.find_table_widget('tblTriggeredIDs') - def init_widgets(self): + def _init_widgets(self): """ initializes the widgets' properties :return: none @@ -107,7 +107,7 @@ self.tblTriggeredIDs.setColumnWidth(0, 75) self.tblTriggeredIDs.setColumnWidth(1, 45) - def init_connections(self): + def _init_connections(self): """ initializes the widgets connections :return: Index: simulator/plugins/createtreatment/interface.ui =================================================================== diff -u --- simulator/plugins/createtreatment/interface.ui (revision 0) +++ simulator/plugins/createtreatment/interface.ui (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -0,0 +1,165 @@ + + + ui_interface + + + + 0 + 0 + 295 + 115 + + + + + 295 + 115 + + + + + 295 + 115 + + + + Create Treatment + + + + + + + 10 + + + + color: rgb(238, 238, 236); +background-color: rgb(32, 74, 135); + + + Create Treatment + + + Qt::AlignCenter + + + + + + + + + + 0 + 0 + + + + + 10 + + + + QFrame::StyledPanel + + + QFrame::Plain + + + + + + + + + + + 0 + 0 + + + + + 10 + + + + + + + + + 10 + + + + Accept + + + + + + + + 0 + 0 + + + + + 50 + 0 + + + + + 10 + + + + reason + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 10 + + + + Reject + + + + + + + + 10 + + + + color: rgb(238, 238, 236); +background-color: rgb(114, 159, 207); + + + Begin + + + Qt::AlignCenter + + + + + + + + + + Index: simulator/plugins/createtreatment/loader.py =================================================================== diff -u --- simulator/plugins/createtreatment/loader.py (revision 0) +++ simulator/plugins/createtreatment/loader.py (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -0,0 +1,84 @@ +""" +The Heparin ui loader class +""" +import os +from simulator.dynamicloader import DynamicLoader +from PySide2 import QtWidgets +from PySide2.QtCore import Slot +from dialin.squish import denaliMessages +from dialin import HDSimulator + +hd_simulator = HDSimulator() + + +class Loader(DynamicLoader): + """ + The Saline Bolus ui loader class + """ + + btnAccept: QtWidgets.QPushButton + btnReject: QtWidgets.QPushButton + lblAction: QtWidgets.QLabel + spnRejectReason: QtWidgets.QSpinBox + + def __init__(self): + super().__init__(os.path.dirname(__file__)) + # self.initialize() + # self.init_widgets() + # self.init_connections() + + def _init_loader(self): + """ + finds and creates widgets + :return: none + """ + # saline adjustment + self.btnAccept = self.find_button('btnAccept') + self.btnReject = self.find_button('btnReject') + self.lblAction = self.find_label('lblAction') + self.spnRejectReason = self.find_spinbox('spnRejectReason') + + def _init_widgets(self): + """ + initializes the widgets' properties + :return: none + """ + pass + + def _init_connections(self): + """ + initializes the widgets connections + :return: + """ + self.btnAccept.clicked.connect(self.do_accept) + self.btnReject.clicked.connect(self.do_reject) + + @Slot() + def do_accept(self): + """ + the slot for accept button + :return: none + """ + hd_simulator.cmd_send_start_treatment_response(1, 0) + self.lblAction.setText('Accepted ') + + @Slot() + def do_reject(self): + """ + the slot for accept saline bolus button + :return: none + """ + reason = self.spnRejectReason.value() + denaliMessages.setHeparinResponse(False, reason, self.requested_state) + self.lblAction.setText('Rejected ' + "{}".format(reason)) + + @Slot() + def do_data(self, value): + """ + the slot which is called to send the data + by calling the denaliMessage API setTreatmentHeparinData + :return: none + """ + volume = value * 0.1 + denaliMessages.setTreatmentHeparinData(volume) + self.lblCumulative.setNum(volume) Index: simulator/plugins/heparin/loader.py =================================================================== diff -u -r69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1 -r9206e36aeff354823f2c37103d79f548221cd64c --- simulator/plugins/heparin/loader.py (.../loader.py) (revision 69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1) +++ simulator/plugins/heparin/loader.py (.../loader.py) (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -2,14 +2,14 @@ The Heparin ui loader class """ import os -from simulator.runtimewidget import RunTimeWidget +from simulator.dynamicloader import DynamicLoader from PySide2 import QtWidgets from PySide2.QtCore import Slot from dialin.squish import denaliMessages from dialin.squish.denaliMessages import txStates -class Loader(RunTimeWidget): +class Loader(DynamicLoader): """ The Saline Bolus ui loader class """ @@ -26,11 +26,11 @@ def __init__(self): super().__init__(os.path.dirname(__file__)) self.requested_state = txStates.HEPARIN_STATE_PAUSED - self.initialize() - self.init_widgets() - self.init_connections() + # self.initialize() + # self.init_widgets() + # self.init_connections() - def initialize(self): + def _init_loader(self): """ finds and creates widgets :return: none @@ -44,14 +44,14 @@ # saline data self.sldCumulative = self.find_slider('sldCumulative') - def init_widgets(self): + def _init_widgets(self): """ initializes the widgets' properties :return: none """ pass - def init_connections(self): + def _init_connections(self): """ initializes the widgets connections :return: Index: simulator/plugins/inlinebloodpressures/loader.py =================================================================== diff -u -r69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1 -r9206e36aeff354823f2c37103d79f548221cd64c --- simulator/plugins/inlinebloodpressures/loader.py (.../loader.py) (revision 69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1) +++ simulator/plugins/inlinebloodpressures/loader.py (.../loader.py) (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -2,15 +2,15 @@ The In-Line Blood Pressure ui class """ import os -from simulator.runtimewidget import RunTimeWidget +from simulator.dynamicloader import DynamicLoader from PySide2 import QtWidgets from PySide2.QtCore import Slot from dialin.squish import denaliMessages from dialin.squish.denaliMessages import txStates, EResponse from dialin.common import Ranges -class Loader(RunTimeWidget): +class Loader(DynamicLoader): """ The In-Line Blood Pressure ui class """ @@ -37,11 +37,11 @@ def __init__(self): super().__init__(os.path.dirname(__file__)) self.saline_requested_state = txStates.SALINE_BOLUS_STATE_IDLE - self.initialize() - self.init_widgets() - self.init_connections() + # self.initialize() + # self.init_widgets() + # self.init_connections() - def initialize(self): + def _init_loader(self): """ finds and creates widgets :return: none @@ -66,7 +66,7 @@ self.sldArterialValue = self.find_slider('sldArterialValue') self.sldVenousValue = self.find_slider('sldVenousValue') - def init_widgets(self): + def _init_widgets(self): """ initializes the widgets' properties :return: none @@ -106,7 +106,7 @@ self.spnVenousLimitHigh.setMaximum(Ranges.VENOUS_PRESSURE_HIGH_MAX) self.spnVenousLimitHigh.setValue(Ranges.VENOUS_PRESSURE_HIGH_DEF) - def init_connections(self): + def _init_connections(self): """ initializes the widgets connections :return: none Index: simulator/plugins/salinebolus/loader.py =================================================================== diff -u -r69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1 -r9206e36aeff354823f2c37103d79f548221cd64c --- simulator/plugins/salinebolus/loader.py (.../loader.py) (revision 69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1) +++ simulator/plugins/salinebolus/loader.py (.../loader.py) (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -2,14 +2,14 @@ The Saline Bolus ui loader class """ import os -from simulator.runtimewidget import RunTimeWidget +from simulator.dynamicloader import DynamicLoader from PySide2 import QtWidgets from PySide2.QtCore import Slot from dialin.squish import denaliMessages from dialin.squish.denaliMessages import txStates -class Loader(RunTimeWidget): +class Loader(DynamicLoader): """ The Saline Bolus ui loader class """ @@ -28,11 +28,11 @@ def __init__(self): super().__init__(os.path.dirname(__file__)) self.saline_requested_state = txStates.SALINE_BOLUS_STATE_IDLE - self.initialize() - self.init_widgets() - self.init_connections() + # self.initialize() + # self.init_widgets() + # self.init_connections() - def initialize(self): + def _init_loader(self): """ finds and creates widgets :return: none @@ -48,14 +48,14 @@ self.sldSalineCumulative = self.find_slider('sldSalineCumulative') self.sldSalineVolume = self.find_slider('sldSalineVolume') - def init_widgets(self): + def _init_widgets(self): """ initializes the widgets' properties :return: none """ pass - def init_connections(self): + def _init_connections(self): """ initializes the widgets connections :return: Index: simulator/plugins/treatmentranges/loader.py =================================================================== diff -u -r69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1 -r9206e36aeff354823f2c37103d79f548221cd64c --- simulator/plugins/treatmentranges/loader.py (.../loader.py) (revision 69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1) +++ simulator/plugins/treatmentranges/loader.py (.../loader.py) (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -2,14 +2,14 @@ The Treatment Ranges ui loader """ import os -from simulator.runtimewidget import RunTimeWidget +from simulator.dynamicloader import DynamicLoader from PySide2 import QtWidgets from PySide2.QtCore import QTimer from PySide2.QtCore import Slot from dialin.squish import denaliMessages -class Loader(RunTimeWidget): +class Loader(DynamicLoader): """ The Treatment Ranges ui loader """ @@ -26,11 +26,11 @@ def __init__(self): super().__init__(os.path.dirname(__file__)) - self.initialize() - self.init_widgets() - self.init_connections() + # self.initialize() + # self.init_widgets() + # self.init_connections() - def initialize(self): + def _init_loader(self): """ finds and creates widgets :return: none @@ -44,15 +44,15 @@ self.spnDialysateMax = self.find_spinbox('spnDialysateMax') self.chkRangesBroadcast = self.find_checkbox('chkRangesBroadcast') - def init_connections(self): + def _init_connections(self): """ initializes the widgets connections :return: none """ self.timer.timeout.connect(self.do_ranges_data) self.sldDurationValue.valueChanged.connect(self.do_duration_data) - def init_widgets(self): + def _init_widgets(self): """ initializes the widgets' properties :return: none Index: simulator/plugins/treatmentstates/loader.py =================================================================== diff -u -r69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1 -r9206e36aeff354823f2c37103d79f548221cd64c --- simulator/plugins/treatmentstates/loader.py (.../loader.py) (revision 69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1) +++ simulator/plugins/treatmentstates/loader.py (.../loader.py) (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -2,14 +2,14 @@ The Ultrafiltration ui loader """ import os -from simulator.runtimewidget import RunTimeWidget +from simulator.dynamicloader import DynamicLoader from PySide2 import QtWidgets from PySide2.QtCore import Slot from dialin.squish import denaliMessages from dialin.squish.denaliMessages import txStates -class Loader(RunTimeWidget): +class Loader(DynamicLoader): """ The Ultrafiltration ui loader """ @@ -20,14 +20,11 @@ def __init__(self): super().__init__(os.path.dirname(__file__)) - self.initialize() - self.init_widgets() - self.init_connections() # apply/send the initial states self.do_treatment_states() - def initialize(self): + def _init_loader(self): """ finds and creates widgets :return: none @@ -37,7 +34,7 @@ self.tblSalineStates = self.find_table_widget('tblSalineStates') self.tblHeparinStates = self.find_table_widget('tblHeparinStates') - def init_connections(self): + def _init_connections(self): """ initializes the widgets connections :return: none @@ -47,7 +44,7 @@ self.tblSalineStates.cellClicked.connect(self.do_treatment_states) self.tblHeparinStates.cellClicked.connect(self.do_treatment_states) - def init_widgets(self): + def _init_widgets(self): """ initializes the widgets' properties :return: none Index: simulator/plugins/ultrafiltration/loader.py =================================================================== diff -u -r69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1 -r9206e36aeff354823f2c37103d79f548221cd64c --- simulator/plugins/ultrafiltration/loader.py (.../loader.py) (revision 69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1) +++ simulator/plugins/ultrafiltration/loader.py (.../loader.py) (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -2,14 +2,14 @@ The Ultrafiltration ui loader """ import os -from simulator.runtimewidget import RunTimeWidget +from simulator.dynamicloader import DynamicLoader from PySide2 import QtWidgets from PySide2.QtCore import Slot from dialin.squish import denaliMessages from dialin.squish.denaliMessages import txStates, EResponse -class Loader(RunTimeWidget): +class Loader(DynamicLoader): """ The Ultrafiltration ui loader """ @@ -33,11 +33,8 @@ def __init__(self): super().__init__(os.path.dirname(__file__)) - self.initialize() - self.init_widgets() - self.init_connections() - def initialize(self): + def _init_loader(self): """ finds and creates widgets :return: none @@ -56,14 +53,14 @@ self.spnUfEditRejectReason = self.find_spinbox('spnUfEditRejectReason') self.sldUfVolume = self.find_slider('sldUfVolume') - def init_widgets(self): + def _init_widgets(self): """ initializes the widgets' properties :return: none """ pass - def init_connections(self): + def _init_connections(self): """ initializes the widgets connections :return: none Index: simulator/simulator/dynamicloader.py =================================================================== diff -u --- simulator/simulator/dynamicloader.py (revision 0) +++ simulator/simulator/dynamicloader.py (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -0,0 +1,172 @@ +""" + find and loads the ui objects +""" +import os +from PySide2.QtUiTools import QUiLoader +from PySide2.QtCore import QFile, QObject +from PySide2 import QtWidgets + + +class DynamicLoader(QObject): + """ + the parent class of all the run time loadable widgets + """ + + loader: QUiLoader + window: QtWidgets.QWidget + + def __init__(self, location: str): + super().__init__(None) + self.location = location + self.loader = QUiLoader() + self.__load_ui() + self._init_loader() + self._init_widgets() + self._init_connections() + + def _init_loader(self): + """ + Virtual method to be implemented in the child class and should be called in order + Main purpose is to find/create the widgets objects in this method + :return: None + """ + raise NotImplementedError() + + def _init_widgets(self): + """ + Virtual method to be implemented in the child class and should be called in order + Main purpose is to setup/initialize widgets properties in this method + :return: None + """ + raise NotImplementedError() + + def _init_connections(self): + """ + Virtual method to be implemented in the child class and should be called in order + Main purpose is to initial the widgets signal/slots connections in this method + :return: None + """ + raise NotImplementedError() + + def __load_ui(self): + """ + loads the ui file of the GUI at run time + :return: none + """ + ui_file = QFile(self.location + "/interface.ui") + 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_widget(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_action(self, name: str) -> QtWidgets.QAction: + """ + convenient method of find_widget for QAction + :param name: (str) name of the QLabel Object + :return: (QAction) reference to the QAction + """ + child = self.find_widget(QtWidgets.QAction, name) + return child + + def find_label(self, name: str) -> QtWidgets.QLabel: + """ + convenient method of find_widget for QLabel + :param name: (str) name of the QLabel Object + :return: (QLabel) reference to the QLabel + """ + child = self.find_widget(QtWidgets.QLabel, name) + return child + + def find_button(self, name: str) -> QtWidgets.QPushButton: + """ + convenient method of find_widget for QPushButton + :param name: (str) name of the QPushButton Object + :return: (QPushButton) reference to the QPushButton + """ + child = self.find_widget(QtWidgets.QPushButton, name) + return child + + def find_combobox(self, name: str) -> QtWidgets.QComboBox: + """ + convenient method of find_widget for QComboBox + :param name: (str) name of the QComboBox Object + :return: (QComboBox) reference to the QComboBox + """ + child = self.find_widget(QtWidgets.QComboBox, name) + return child + + def find_checkbox(self, name: str) -> QtWidgets.QCheckBox: + """ + convenient method of find_widget for QCheckBox + :param name: (str) name of the QCheckBox Object + :return: (QCheckBox) reference to the QComboBox + """ + child = self.find_widget(QtWidgets.QCheckBox, name) + return child + + def find_spinbox(self, name: str) -> QtWidgets.QSpinBox: + """ + convenient method of find_widget for QSpinBox + :param name: (str) name of the QSpinBox Object + :return: (QSpinBox) reference to the QSpinBox + """ + child = self.find_widget(QtWidgets.QSpinBox, name) + return child + + def find_slider(self, name: str) -> QtWidgets.QSlider: + """ + convenient method of find_widget for QSlider + :param name: (str) name of the QSlider Object + :return: (QSlider) reference to the QSlider + """ + child = self.find_widget(QtWidgets.QSlider, name) + return child + + def find_table_widget(self, name: str) -> QtWidgets.QTableWidget: + """ + convenient method of find_widget for QTableWidget + :param name: (str) name of the QTableWidget Object + :return: (QTableWidget) reference to the QTableWidget + """ + child: QtWidgets.QTableWidget = self.find_widget(QtWidgets.QTableWidget, name) + return child + + def find_list_widget(self, name: str) -> QtWidgets.QListWidget: + """ + convenient method of find_widget for QListWidget + :param name: (str) name of the QListWidget Object + :return: (QListWidget) reference to the QListWidget + """ + child: QtWidgets.QListWidget = self.find_widget(QtWidgets.QListWidget, name) + return child + + def find_tool_button(self, name: str) -> QtWidgets.QToolButton: + """ + convenient method of find_widget for QToolButton + :param name: (str) name of the QToolButton Object + :return: (QToolButton) reference to the QToolButton + """ + child: QtWidgets.QToolButton = self.find_widget(QtWidgets.QToolButton, name) + return child + Index: simulator/simulator/interface.ui =================================================================== diff -u -r69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1 -r9206e36aeff354823f2c37103d79f548221cd64c --- simulator/simulator/interface.ui (.../interface.ui) (revision 69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1) +++ simulator/simulator/interface.ui (.../interface.ui) (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -27,6 +27,9 @@ 0 + + 0 + Index: simulator/simulator/loader.py =================================================================== diff -u -r69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1 -r9206e36aeff354823f2c37103d79f548221cd64c --- simulator/simulator/loader.py (.../loader.py) (revision 69b2aacf31c5fdc28e078f87dbdee5c3ab672dd1) +++ simulator/simulator/loader.py (.../loader.py) (revision 9206e36aeff354823f2c37103d79f548221cd64c) @@ -9,14 +9,14 @@ from PySide2 import QtWidgets from PySide2.QtCore import Qt -from simulator.runtimewidget import RunTimeWidget +from simulator.dynamicloader import DynamicLoader # later it's planed to load all the classes in plugin folder dynamically # so used * in here to load all from plugins import * -class Simulator(RunTimeWidget): +class Simulator(DynamicLoader): """ The simulator class which loads the ui file dynamically and initializes the objects and can be eventually shown. @@ -32,12 +32,11 @@ def __init__(self): super().__init__(os.path.dirname(__file__)) self.saline_requested_state = txStates.SALINE_BOLUS_STATE_IDLE - self.__initialize() if self.__check_can_bus(): self.__init_plugins() - self.__init_connections() + self.__init_actions() - def __initialize(self): + def _init_loader(self): """ initializes the class by calling it's initializer methods to make objects ready :return: none @@ -48,6 +47,12 @@ self.menuView = self.find_widget(QtWidgets.QMenu, 'menuView') self.action_show_all = self.find_action('actionShowAll') + def _init_widgets(self): + pass + + def _init_connections(self): + pass + def __init_plugins(self): """ initializes the widgets' properties @@ -60,9 +65,10 @@ self.__register_plugin('InlineBloodPressures', InlineBloodPressures()) self.__register_plugin('SalineBolus', SalineBolus()) self.__register_plugin('Heparin', Heparin(), True) - self.__register_plugin('Alarms', Alarms(), maximize=True) + self.__register_plugin('Alarms', Alarms(), True, maximize=True) + self.__register_plugin('CreateTreatment', CreateTreatment()) - def __init_connections(self): + def __init_actions(self): """ initializes the widgets connections :return: none @@ -84,7 +90,7 @@ act.toggled.connect(window.setVisible) self.action_show_all.toggled.connect(act.setChecked) - def __register_plugin(self, name: str, obj: RunTimeWidget, + def __register_plugin(self, name: str, obj: DynamicLoader, add_separator: bool = False, visible: bool = False, maximize: bool = False): """ creates the plugin object and adds it to the mdi and creates an action menu for it @@ -111,6 +117,7 @@ """ for sub in self.mdiArea.subWindowList(): sub.setVisible(visible) + self.mdiArea.cascadeSubWindows() def __check_can_bus(self): """ Fisheye: Tag 9206e36aeff354823f2c37103d79f548221cd64c refers to a dead (removed) revision in file `simulator/simulator/runtimewidget.py'. Fisheye: No comparison available. Pass `N' to diff?