Esempio n. 1
0
class MainController:
    palette: QPalette
    _welcome_gui: QStackedWidget
    _welcome_ui: Ui_Welcome
    _dcm_gui: QMainWindow
    _dcm_ui: Ui_MainWindow
    _about_gui: QDialog
    _about_ui: Ui_aboutWindow
    _about_table: QTableWidget
    _about_header: Dict[str, str]
    _params_gui: QDialog
    _params_ui: Ui_parametersWindow
    _reports_gui: QDialog
    _reports_ui: Ui_ReportsWindow
    _egram_report_gui: QDialog
    _egram_report_ui: Ui_Dialog
    _auth: AuthHandler
    _conn: ConnectionHandler
    _params: ParametersHandler
    _reports: ReportsHandler
    _graphs: GraphsHandler

    def __init__(self):
        # Set theme to dark mode
        self.palette = QPalette()
        self.palette.setColor(QPalette.Window, QColor(53, 53, 53))
        self.palette.setColor(QPalette.WindowText, Qt.white)

        # Setup welcome screen UI from auto-generated file
        self._welcome_gui = QStackedWidget()
        self._welcome_ui = welcomescreen.Ui_Welcome()
        self._welcome_ui.setupUi(self._welcome_gui)

        # Setup dcm screen UI from auto-generated file
        self._dcm_gui = QMainWindow()
        self._dcm_ui = dcm.Ui_MainWindow()
        self._dcm_ui.setupUi(self._dcm_gui)
        for i, button in enumerate(self._dcm_ui.pacing_mode_group.buttons()):
            self._dcm_ui.pacing_mode_group.setId(button, i)

        # Setup about screen UI from auto-generated file
        self._about_gui = QDialog()
        self._about_ui = about.Ui_aboutWindow()
        self._about_ui.setupUi(self._about_gui)
        self._about_table = self._about_ui.tableWidget
        self._about_header = {
            self._about_table.verticalHeaderItem(row).text():
            self._about_table.item(row, 0).text()
            for row in range(self._about_table.rowCount())
        }

        # Setup parameter screen UI from auto-generated file
        self._params_gui = QDialog()
        self._params_ui = parameters.Ui_parametersWindow()
        self._params_ui.setupUi(self._params_gui)

        # Setup reports screen UI from auto-generated file
        self._reports_gui = QDialog()
        self._reports_ui = reports.Ui_ReportsWindow()
        self._reports_ui.setupUi(self._reports_gui)

        # Setup egram screen UI from auto-generated file
        self._egram_report_gui = QDialog()
        self._egram_report_ui = egram_report.Ui_Dialog()
        self._egram_report_ui.setupUi(self._egram_report_gui)

        # Initialize separate handlers for authentication, pacemaker connection, parameters, reports and graphs
        self._auth = AuthHandler(self.show_dcm)
        self._conn = ConnectionHandler()
        self._params = ParametersHandler(self._params_ui.tableWidget)
        self._reports = ReportsHandler(self._egram_report_ui)
        self._graphs = GraphsHandler(self._dcm_ui.atrial_plots,
                                     self._dcm_ui.vent_plots,
                                     data_size=2001)

        # Link elements to actions
        self.link_welcome_buttons()
        self.link_dcm_elements()
        self.link_reports_buttons()
        self.link_params_buttons()

        # Start connection thread
        self._conn.connect_status_change.connect(self.handle_pace_conn)
        self._conn.serial.ecg_data_update.connect(self._graphs.update_data)
        self._conn.serial.params_received.connect(self._show_alert)
        self._conn.start()

        # Update params GUI table to show default pacing mode params
        self._params.update_row_visibility(
            self._dcm_ui.pacing_mode_group.checkedButton().text())

        # Show welcome screen GUI
        self._welcome_gui.show()

    # Link welcome screen ui elements to their respective functions
    def link_welcome_buttons(self) -> None:
        # Welcome screen
        # show register and login screens when those buttons are pressed, respectively
        self._welcome_ui.reg_btn.clicked.connect(
            lambda: self._welcome_gui.setCurrentIndex(1))
        self._welcome_ui.log_btn.clicked.connect(
            lambda: self._welcome_gui.setCurrentIndex(2))

        # Register screen
        # register user and go back to welcome screen when those buttons are pressed, respectively
        self._welcome_ui.reg_submit_btn.clicked.connect(
            lambda: self._auth.register(self._welcome_ui.reg_user.text(),
                                        self._welcome_ui.reg_pass.text()))
        self._welcome_ui.reg_back_btn.clicked.connect(
            lambda: self._welcome_gui.setCurrentIndex(0))

        # Login screen
        # login user and go back to welcome screen when those buttons are pressed, respectively
        self._welcome_ui.log_submit_btn.clicked.connect(
            lambda: self._auth.login(self._welcome_ui.log_user.text(),
                                     self._welcome_ui.log_pass.text()))
        self._welcome_ui.log_back_btn.clicked.connect(
            lambda: self._welcome_gui.setCurrentIndex(0))

    # Link dcm ui elements to their respective functions
    def link_dcm_elements(self) -> None:
        # Buttons
        self._dcm_ui.quit_btn.clicked.connect(
            self._dcm_gui.close
        )  # close dcm and quit program when quit is pressed
        self._dcm_ui.about_btn.clicked.connect(
            self._about_gui.exec_)  # show about screen when about is pressed
        self._dcm_ui.parameters_btn.clicked.connect(
            self._params_gui.exec_
        )  # show params screen when params is pressed
        self._dcm_ui.reports_btn.clicked.connect(
            self._reports_gui.exec_
        )  # show reports screen when reports is pressed
        self._dcm_ui.new_patient_btn.clicked.connect(
            self._conn.register_device)  # register pacemaker on btn press
        # write serial data when btn is pressed
        self._dcm_ui.pace_btn.clicked.connect(
            lambda: self._conn.send_data_to_pacemaker(
                self._params.get_params(self.get_current_pace_index())))
        # update the params GUI table to only show the params for the current pacing mode
        self._dcm_ui.pacing_mode_group.buttonClicked.connect(
            lambda: self._params.update_row_visibility(
                self.get_current_pace_mode()))

        # Checkboxes
        # show or hide the plots, depending on whether or not the checkbox is checked, when it changes state
        self._dcm_ui.atrial_box.stateChanged.connect(
            lambda: self._graphs.atri_vis(self._dcm_ui.atrial_box.isChecked()))
        self._dcm_ui.vent_box.stateChanged.connect(
            lambda: self._graphs.vent_vis(self._dcm_ui.vent_box.isChecked()))

    # Link reports ui elements to their respective functions
    def link_reports_buttons(self) -> None:
        # Get the params based on the pacing mode, and then generate the respective report based on the pressed btn
        self._reports_ui.egram_btn.clicked.connect(self.show_egram_report)
        self._reports_ui.brady_btn.clicked.connect(
            lambda: self._reports.generate_brady(self._about_header,
                                                 self.get_pace_mode_params()))

        # Get the params based on the pacing mode, and then generate the respective report based on the pressed btn
        self._egram_report_ui.export_btn.clicked.connect(
            lambda: self._reports.export_pdf(self._egram_report_gui))

    # Link parameters ui elements to their respective functions
    def link_params_buttons(self) -> None:
        self._params_ui.confirm_btn.clicked.connect(
            self._params.confirm
        )  # update stored params and write them to disk
        self._params_ui.reset_btn.clicked.connect(
            self._params.reset
        )  # reset stored and shown params to GUI defaults

    # Upon successful user registration or login, close the welcome screen, show the dcm and load params for user
    def show_dcm(self, username: str) -> None:
        self._welcome_gui.close()
        self._dcm_gui.show()
        self._params.update_params_on_user_auth(username)

    # Upon successful user registration or login, close the welcome screen, show the dcm and load params for user
    def show_egram_report(self) -> None:
        self._reports.generate_egram(self._about_header,
                                     self._dcm_ui.atrial_plots.grab(),
                                     self._dcm_ui.vent_plots.grab())
        self._egram_report_gui.exec_()

    # Upon successful pacemaker connection, update the status bar animation and the About window table
    def handle_pace_conn(self, conn_state: PacemakerState, msg: str) -> None:
        self._dcm_ui.statusbar.handle_conn_anim(conn_state, msg)
        self._about_header[
            "Device serial number"] = msg if conn_state != PacemakerState.NOT_CONNECTED else "None"
        for row in range(self._about_table.rowCount()):
            self._about_table.item(row, 0).setText(self._about_header[
                self._about_table.verticalHeaderItem(row).text()])

    # Get only the parameters required for the current pacing mode
    def get_pace_mode_params(self) -> Dict[str, str]:
        return self._params.filter_params(self.get_current_pace_mode())

    # Get current pacing mode index in button group
    def get_current_pace_index(self) -> int:
        return self._dcm_ui.pacing_mode_group.checkedId()

    # Get current pacing mode name
    def get_current_pace_mode(self) -> str:
        return self._dcm_ui.pacing_mode_group.checkedButton().text()

    @staticmethod
    def _show_alert(success: bool, msg: str) -> None:
        """
        Displays an error message with the specified text

        :param msg: the text to show
        """
        qm = QMessageBox()
        if success:
            QMessageBox.information(qm, "Connection", msg, QMessageBox.Ok,
                                    QMessageBox.Ok)
        else:
            QMessageBox.critical(qm, "Connection", msg, QMessageBox.Ok,
                                 QMessageBox.Ok)

    # Stop threads spawned by handlers
    def stop_threads(self):
        self._conn.stop()