Пример #1
0
class Window(QOpenGLWindow):
    def __init__(self):
        super(Window, self).__init__()
        self.timer = QElapsedTimer()
        self.timer.start()

    def initializeGL(self):
        super(Window, self).initializeGL()

        gl.glEnable(gl.GL_BLEND)
        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        gl.glEnable(gl.GL_PROGRAM_POINT_SIZE)
        gl.glEnable(gl.GL_DEPTH_TEST)

        self.square = SquareObject(parent=self.context())

    def paintGL(self):
        retinaScale = self.devicePixelRatio()
        gl.glViewport(0, 0, int(self.width() * retinaScale),
                      int(self.height() * retinaScale))

        gl.glClearColor(0.2, 0.3, 0.3, 1.0)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)

        transform = QMatrix4x4()
        transform.setToIdentity()
        transform.rotate(self.timer.elapsed() / 10, QVector3D(0, 0, 1))
        transform.translate(QVector3D(0.25, 0.25, -2.))

        projection = QMatrix4x4()
        projection.setToIdentity()
        projection.perspective(45.,
                               float(self.width()) / self.height(), 0.1, 100.)

        self.square.render(projection * transform)
Пример #2
0
 def __init__(self, totaltime=0):
     super().__init__()
     # 在主窗口左侧添加题干和选项
     lsplitter = QSplitter(Qt.Vertical)
     self.question_panel = Question_panel()
     lsplitter.addWidget(self.question_panel)
     # 在主窗口右侧添加绘图和文本编辑,并把比例设置为3比1
     rsplitter = QSplitter(Qt.Vertical)
     self.painter = QGraphicsView(rsplitter)
     self.note = QTextEdit(rsplitter)
     rsplitter.setStretchFactor(0, 3)
     rsplitter.setStretchFactor(1, 1)
     # 添加插件的顺序会导致左右不同
     mainSplitter = QSplitter(Qt.Horizontal)
     mainSplitter.addWidget(lsplitter)
     mainSplitter.addWidget(rsplitter)
     self.setCentralWidget(mainSplitter)
     # 点击暂停按钮切换图标和停继时间
     self.question_panel.ui.pushButtonPause.clicked.connect(
         self.toggle_play_and_pause)
     self.totaltime = totaltime  # 当前试卷答题总时间
     self.elapsed_time = QElapsedTimer()  # 答题总时间的计时器
     self.paused = False  # 默认刚打开时,还未暂停时间
     self.setTime()  # 更新时间显示
     self.timer = QTimer()
     self.timer.timeout.connect(self.setTime)  # 每秒更新时间显示的定时器
Пример #3
0
class InUse(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        self.ui = UiInUse()
        self.ui.setupUi(self)

        self.db = DataBase()
        self.time = QTime(0, 0)
        self.elapsed_timer = QElapsedTimer()
        self.timer = QTimer(self)
        self.info = {}

    def update_time(self):
        # TODO: Is there more clean way to show timer...?
        self.ui.label_time.setText(
            (self.time.addMSecs(self.elapsed_timer.elapsed())).toString("hh:mm:ss")
        )

    def set_page(self, contact):
        self.info = self.db.get_info(contact)

        self.elapsed_timer.start()
        self.timer.timeout.connect(self.update_time)
        self.timer.start(500)
        self.ui.label_time.setText(self.time.toString("hh:mm:ss"))
        self.ui.label_name.setText(self.info["name"] + " 님")

    def clear_page(self):
        self.timer.stop()
        self.ui.label_time.setText("00:00:00")
        self.ui.label_name.setText("???")
Пример #4
0
    def __init__(self, window, G, options, main=None):
        super().__init__()
        self.timer = QTimer()
        self.alive_timer = QElapsedTimer()
        self.g = G
        self.window = window
        self.view_menu = QMenu()
        self.file_menu = QMenu()
        self.forces_menu = QMenu()
        self.main_widget = window
        self.docks = {}
        self.widgets = {}
        self.widgets_by_type = {}

        available_geometry = QApplication.desktop().availableGeometry()
        window.move((available_geometry.width() - window.width()) / 2,
                    (available_geometry.height() - window.height()) / 2)
        self.__initialize_file_menu()
        viewMenu = window.menuBar().addMenu(window.tr("&View"))
        forcesMenu = window.menuBar().addMenu(window.tr("&Forces"))
        actionsMenu = window.menuBar().addMenu(window.tr("&Actions"))
        restart_action = actionsMenu.addAction("Restart")

        self.__initialize_views(options, main)
        self.alive_timer.start()
        self.timer.start(500)
Пример #5
0
    def __init__(self, parent=None):
        super(TabDisplays, self).__init__(parent)

        # Initialize logging
        logging_conf_file = os.path.join(os.path.dirname(__file__),
                                         'cfg/aecgviewer_aecg_logging.conf')
        logging.config.fileConfig(logging_conf_file)
        self.logger = logging.getLogger(__name__)

        self.studyindex_info = aecg.tools.indexer.StudyInfo()

        self.validator = QWidget()

        self.studyinfo = QWidget()

        self.statistics = QWidget()

        self.waveforms = QWidget()

        self.waveforms.setAccessibleName("Waveforms")

        self.scatterplot = QWidget()

        self.histogram = QWidget()

        self.trends = QWidget()

        self.xmlviewer = QWidget()
        self.xml_display = QTextEdit(self.xmlviewer)

        self.options = QWidget()

        self.aecg_display_area = QScrollArea()
        self.cbECGLayout = QComboBox()
        self.aecg_display = EcgDisplayWidget(self.aecg_display_area)
        self.aecg_display_area.setWidget(self.aecg_display)

        self.addTab(self.validator, "Study information")
        self.addTab(self.waveforms, "Waveforms")
        self.addTab(self.xmlviewer, "XML")
        self.addTab(self.options, "Options")

        self.setup_validator()
        self.setup_waveforms()
        self.setup_xmlviewer()
        self.setup_options()

        size = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        size.setHeightForWidth(False)
        self.setSizePolicy(size)

        # Initialized a threpool with 2 threads 1 for the GUI, 1 for long
        # tasks, so GUI remains responsive
        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(2)
        self.validator_worker = None
        self.indexing_timer = QElapsedTimer()
Пример #6
0
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        self.ui = UiInUse()
        self.ui.setupUi(self)

        self.db = DataBase()
        self.time = QTime(0, 0)
        self.elapsed_timer = QElapsedTimer()
        self.timer = QTimer(self)
        self.info = {}
Пример #7
0
    def __init__(self):
        super(Window, self).__init__()
        self.timer = QElapsedTimer()
        self.timer.start()

        self.cubePositions = [
            QVector3D( 0.0,  0.0,   0.0),
            QVector3D( 2.0,  5.0, -15.0),
            QVector3D(-1.5, -2.2, - 2.5),
            QVector3D(-3.8, -2.0, -12.3),
            QVector3D( 2.4, -0.4, - 3.5),
            QVector3D(-1.7,  3.0, - 7.5),
            QVector3D( 1.3, -2.0, - 2.5),
            QVector3D( 1.5,  2.0, - 2.5),
            QVector3D( 1.5,  0.2, - 1.5),
            QVector3D(-1.3,  1.0, - 1.5)
        ]
Пример #8
0
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        # UI for the page
        self.ui = Ui_InUse()
        self.ui.setupUi(self)

        # reader
        self.reader = TestDatabaseReader()
        self.userData = dict()
        self.userTime = QTime(0, 0)

        # Set timer
        self.elapsedTimer = QElapsedTimer()
        self.timer = QTimer(self)

        self.ui.UsedTime.setText("00:00:00")
        self.ui.UserName.setText("")
Пример #9
0
    def initiateBlinkSequence(self, component: 'Component') -> None:
        """
		Starts the blink sequence by setting timers and executing an event loop.
		
		NOTE: Because this function executes an event loop, it is blocking.
		
		:param component: The component to select.
		:type component: Component
		:return: None
		:rtype: NoneType
		"""
        self._component = component
        self._component.top_level_parent().set_focus()
        self._timer = QTimer(self)
        self._timer.timeout.connect(lambda: self.tick())
        self._stopWatch = QElapsedTimer()
        self._timer.start(Blinker.INTERVAL_MILLIS)
        self._stopWatch.start()
        self.exec_()
Пример #10
0
class InUse(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        # UI for the page
        self.ui = Ui_InUse()
        self.ui.setupUi(self)

        # reader
        self.reader = TestDatabaseReader()
        self.userData = dict()
        self.userTime = QTime(0, 0)

        # Set timer
        self.elapsedTimer = QElapsedTimer()
        self.timer = QTimer(self)

        self.ui.UsedTime.setText("00:00:00")
        self.ui.UserName.setText("")

    def updateTime(self):

        self.ui.UsedTime.setText((self.userTime.addMSecs(
            self.elapsedTimer.elapsed())).toString("hh:mm:ss"))

    def setPage(self):

        self.elapsedTimer.start()
        self.timer.timeout.connect(self.updateTime)
        self.timer.start(500)
        self.userData = self.reader.getCurrentData()
        self.userTime = QTime.fromString(self.userData["time_used"],
                                         "hh:mm:ss")
        self.ui.UsedTime.setText(self.userTime.toString("hh:mm:ss"))
        self.ui.UserName.setText(self.userData["name"] + " 님")

    def clearPage(self):

        self.timer.stop()
        self.ui.UsedTime.setText("00:00:00")
        self.ui.UserName.setText("")
        """TODO: ADD CODES FOR WRITING CURRENT USERTIME TO DB HERE"""
Пример #11
0
    def __init__(self, usbif, read_msg, data_msg, num_banks, bank_size, switches, aux_switch=None):
        QObject.__init__(self)

        self._usbif = usbif
        self._read_msg = read_msg
        self._data_msg = data_msg
        self._num_banks = num_banks
        self._bank_size = bank_size

        self._data = []
        self._read_addrs = []
        self._bank = 0

        self._switches = switches
        self._aux_switch = aux_switch

        self._timer = QElapsedTimer()
        self._timer.start()
        self._check_timer = QTimer()
        self._check_timer.timeout.connect(self._check_progress)

        usbif.listen(self)
Пример #12
0
    def start_time(cls):
        """Start or resume the timer."""
        Clock.connect(on_tick=cls.update_time)
        if cls.time is None:
            cls.time = QElapsedTimer()
            cls.time.start()

            # Do not clobber timestamp when restoring from backup
            if not cls.from_backup:
                cls.timestamp = QDateTime.currentDateTime()
            else:
                # After restoring from backup, forget about it.
                # We need to be able to create new timestamps afterward.
                cls.from_backup = False
        else:
            cls.time.restart()

        cls.paused = False
Пример #13
0
class ProfilerFactor(Singleton):
    def __init__(self):
        pass

    def init(self, mainwindow):
        self.factor_init = True
        self.mainwindow = mainwindow
        self.timer = None

    def timer_start(self):
        self.timer = QElapsedTimer()
        self.timer.start()

    def timer_elapsed(self):
        elapsed = self.timer.elapsed()
        LogFactor().debug("timer elapsed:{}".format(elapsed))
        self.timer.restart()

    def timer_finish(self):
        elapsed = self.timer.elapsed()
        LogFactor().debug("timer elapsed:{}".format(elapsed))
        self.timer = None
Пример #14
0
class MemoryDump(QObject):
    finished = Signal()

    def __init__(self, usbif, read_msg, data_msg, num_banks, bank_size, switches, aux_switch=None):
        QObject.__init__(self)

        self._usbif = usbif
        self._read_msg = read_msg
        self._data_msg = data_msg
        self._num_banks = num_banks
        self._bank_size = bank_size

        self._data = []
        self._read_addrs = []
        self._bank = 0

        self._switches = switches
        self._aux_switch = aux_switch

        self._timer = QElapsedTimer()
        self._timer.start()
        self._check_timer = QTimer()
        self._check_timer.timeout.connect(self._check_progress)

        usbif.listen(self)

    def handle_msg(self, msg):
        if isinstance(msg, self._data_msg):
            if msg.addr in self._read_addrs:
                self._timer.restart()
                bisect.insort(self._data, msg)

                last_addr = self._read_addrs[-1]
                self._read_addrs.remove(msg.addr)
                if not self._read_addrs:
                    self._dump_next_bank()

                elif msg.addr == last_addr:
                    self._dump_addrs(self._read_addrs)

    def _check_progress(self):
        if self._timer.elapsed() >= 80:
            self._dump_addrs(self._read_addrs)

    def _dump_addrs(self, addrs):
        self._read_addrs = addrs
        for a in addrs:
            self._usbif.send(self._read_msg(a))

    def _dump_next_bank(self):
        while self._bank < self._num_banks:
            if self._bank < 0o44:
                sw = self._switches[self._bank]
            else:
                sw = self._aux_switch

            if sw.isChecked():
                sw.setCheckState(Qt.PartiallyChecked)
                break

            self._bank += 1

        if self._bank == self._num_banks:
            self._complete_dump()
            return

        bank_start = self._bank * self._bank_size
        bank_end = bank_start + self._bank_size
        self._dump_addrs(list(range(bank_start, bank_end)))

        self._bank += 1

    def _complete_dump(self):
        self._check_timer.stop()

        for sw in self._switches:
            sw.setTristate(False)
            sw.update()

        if self._aux_switch:
            self._aux_switch.setTristate(False)
            self._aux_switch.update()

        data = array.array('H')
        data.fromlist([agc.pack_word(m.data, m.parity) for m in self._data])
        data.byteswap()

        with open(self._filename, 'wb') as f:
            data.tofile(f)

        self.finished.emit()

    def dump_memory(self, filename):
        self._filename = filename
        self._bank = 0
        self._data = []
        self._timer.restart()
        self._check_timer.start(20)
        self._dump_next_bank()
Пример #15
0
 def __init__(self):
     super(Window, self).__init__()
     self.timer = QElapsedTimer()
     self.timer.start()
Пример #16
0
 def timer_start(self):
     self.timer = QElapsedTimer()
     self.timer.start()
Пример #17
0
class Window(QOpenGLWindow):

    def __init__(self):
        super(Window, self).__init__()
        self.timer = QElapsedTimer()
        self.timer.start()

        self.cubePositions = [
            QVector3D( 0.0,  0.0,   0.0),
            QVector3D( 2.0,  5.0, -15.0),
            QVector3D(-1.5, -2.2, - 2.5),
            QVector3D(-3.8, -2.0, -12.3),
            QVector3D( 2.4, -0.4, - 3.5),
            QVector3D(-1.7,  3.0, - 7.5),
            QVector3D( 1.3, -2.0, - 2.5),
            QVector3D( 1.5,  2.0, - 2.5),
            QVector3D( 1.5,  0.2, - 1.5),
            QVector3D(-1.3,  1.0, - 1.5)
        ]

    def initializeGL(self):
        super(Window, self).initializeGL()

        gl.glEnable(gl.GL_BLEND)
        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        gl.glEnable(gl.GL_PROGRAM_POINT_SIZE)
        gl.glEnable(gl.GL_DEPTH_TEST)

        self.cube = CubeObject(parent=self.context())

    def paintGL(self):
        retinaScale = self.devicePixelRatio()
        gl.glViewport(0, 0, int(self.width() * retinaScale),
                      int(self.height() * retinaScale))

        gl.glClearColor(0.2, 0.3, 0.3, 1.0)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)

        self.cube.program.bind()

        projection = QMatrix4x4()
        projection.setToIdentity()
        projection.perspective(45., float(self.width()) / self.height(), 0.1,
                               100.)
        self.cube.program.setUniformValue('projection', projection)

        # Initialize view matrix.
        view = QMatrix4x4()
        view.setToIdentity()
        view.translate(0., 0., -10.)
        self.cube.program.setUniformValue('view', view)

        for i, t in enumerate(self.cubePositions):
            transform = QMatrix4x4()
            transform.setToIdentity()
            transform.translate(t)
            transform.rotate(1.2 ** ((i + 1) * 5) * self.timer.elapsed() / 10,
                             QVector3D(0.5, 1, 0).normalized())

            self.cube.render(transform)

        self.cube.program.release()
Пример #18
0
class TabDisplays(QTabWidget):
    def __init__(self, parent=None):
        super(TabDisplays, self).__init__(parent)

        # Initialize logging
        logging_conf_file = os.path.join(os.path.dirname(__file__),
                                         'cfg/aecgviewer_aecg_logging.conf')
        logging.config.fileConfig(logging_conf_file)
        self.logger = logging.getLogger(__name__)

        self.studyindex_info = aecg.tools.indexer.StudyInfo()

        self.validator = QWidget()

        self.studyinfo = QWidget()

        self.statistics = QWidget()

        self.waveforms = QWidget()

        self.waveforms.setAccessibleName("Waveforms")

        self.scatterplot = QWidget()

        self.histogram = QWidget()

        self.trends = QWidget()

        self.xmlviewer = QWidget()
        self.xml_display = QTextEdit(self.xmlviewer)

        self.options = QWidget()

        self.aecg_display_area = QScrollArea()
        self.cbECGLayout = QComboBox()
        self.aecg_display = EcgDisplayWidget(self.aecg_display_area)
        self.aecg_display_area.setWidget(self.aecg_display)

        self.addTab(self.validator, "Study information")
        self.addTab(self.waveforms, "Waveforms")
        self.addTab(self.xmlviewer, "XML")
        self.addTab(self.options, "Options")

        self.setup_validator()
        self.setup_waveforms()
        self.setup_xmlviewer()
        self.setup_options()

        size = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        size.setHeightForWidth(False)
        self.setSizePolicy(size)

        # Initialized a threpool with 2 threads 1 for the GUI, 1 for long
        # tasks, so GUI remains responsive
        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(2)
        self.validator_worker = None
        self.indexing_timer = QElapsedTimer()

    def setup_validator(self):
        self.directory_indexer = None  # aecg.indexing.DirectoryIndexer()
        self.validator_layout_container = QWidget()
        self.validator_layout = QFormLayout()
        self.validator_form_layout = QFormLayout(
            self.validator_layout_container)
        self.validator_grid_layout = QGridLayout()
        self.study_info_file = QLineEdit()
        self.study_info_file.setToolTip("Study index file")
        self.study_info_description = QLineEdit()
        self.study_info_description.setToolTip("Description")
        self.app_type = QLineEdit()
        self.app_type.setToolTip("Application type (e.g., NDA, IND, BLA, IDE)")
        self.app_num = QLineEdit()
        self.app_num.setToolTip("Six-digit application number")
        self.app_num.setValidator(QIntValidator(self.app_num))
        self.study_id = QLineEdit()
        self.study_id.setToolTip("Study identifier")
        self.study_sponsor = QLineEdit()
        self.study_sponsor.setToolTip("Sponsor of the study")

        self.study_annotation_aecg_cb = QComboBox()
        self.study_annotation_aecg_cb.addItems(
            ["Rhythm", "Derived beat", "Holter-rhythm", "Holter-derived"])
        self.study_annotation_aecg_cb.setToolTip(
            "Waveforms used to perform the ECG measurements (i.e., "
            "annotations)\n"
            "\tRhythm: annotations in a rhythm strip or discrete ECG "
            "extraction (e.g., 10-s strips)\n"
            "\tDerived beat: annotations in a representative beat derived "
            "from a rhythm strip\n"
            "\tHolter-rhythm: annotations in a the analysis window of a "
            "continuous recording\n"
            "\tHolter-derived: annotations in a representative beat derived "
            "from analysis window of a continuous recording\n")
        self.study_annotation_lead_cb = QComboBox()
        self.ui_leads = ["GLOBAL"] + aecg.STD_LEADS[0:12] +\
            [aecg.KNOWN_NON_STD_LEADS[1]] + aecg.STD_LEADS[12:15] + ["Other"]
        self.study_annotation_lead_cb.addItems(self.ui_leads)
        self.study_annotation_lead_cb.setToolTip(
            "Primary analysis lead annotated per protocol. There could be "
            "annotations in other leads also, but only the primary lead should"
            " be selected here.\n"
            "Select global if all leads were used at the "
            "same time (e.g., superimposed on screen).\n"
            "Select other if the primary lead used is not in the list.")

        self.study_numsubjects = QLineEdit()
        self.study_numsubjects.setToolTip(
            "Number of subjects with ECGs in the study")
        self.study_numsubjects.setValidator(
            QIntValidator(self.study_numsubjects))

        self.study_aecgpersubject = QLineEdit()
        self.study_aecgpersubject.setToolTip(
            "Number of scheduled ECGs (or analysis windows) per subject as "
            "specified in the study protocol.\n"
            "Enter average number of ECGs "
            "per subject if the protocol does not specify a fixed number of "
            "ECGs per subject.")
        self.study_aecgpersubject.setValidator(
            QIntValidator(self.study_aecgpersubject))

        self.study_numaecg = QLineEdit()
        self.study_numaecg.setToolTip(
            "Total number of aECG XML files in the study")
        self.study_numaecg.setValidator(QIntValidator(self.study_numaecg))

        self.study_annotation_numbeats = QLineEdit()
        self.study_annotation_numbeats.setToolTip(
            "Minimum number of beats annotated in each ECG or analysis window"
            ".\nEnter 1 if annotations were done in the derived beat.")
        self.study_annotation_numbeats.setValidator(
            QIntValidator(self.study_annotation_numbeats))

        self.aecg_numsubjects = QLineEdit()
        self.aecg_numsubjects.setToolTip(
            "Number of subjects found across the provided aECG XML files")
        self.aecg_numsubjects.setReadOnly(True)

        self.aecg_aecgpersubject = QLineEdit()
        self.aecg_aecgpersubject.setToolTip(
            "Average number of ECGs per subject found across the provided "
            "aECG XML files")
        self.aecg_aecgpersubject.setReadOnly(True)

        self.aecg_numaecg = QLineEdit()
        self.aecg_numaecg.setToolTip(
            "Number of aECG XML files found in the study aECG directory")
        self.aecg_numaecg.setReadOnly(True)

        self.subjects_less_aecgs = QLineEdit()
        self.subjects_less_aecgs.setToolTip(
            "Percentage of subjects with less aECGs than specified per "
            "protocol")
        self.subjects_less_aecgs.setReadOnly(True)

        self.subjects_more_aecgs = QLineEdit()
        self.subjects_more_aecgs.setToolTip(
            "Percentage of subjects with more aECGs than specified per "
            "protocol")
        self.subjects_more_aecgs.setReadOnly(True)

        self.aecgs_no_annotations = QLineEdit()
        self.aecgs_no_annotations.setToolTip(
            "Percentage of aECGs with no annotations")
        self.aecgs_no_annotations.setReadOnly(True)

        self.aecgs_less_qt_in_primary_lead = QLineEdit()
        self.aecgs_less_qt_in_primary_lead.setToolTip(
            "Percentage of aECGs with less QT intervals in the primary lead "
            "than specified per protocol")
        self.aecgs_less_qt_in_primary_lead.setReadOnly(True)

        self.aecgs_less_qts = QLineEdit()
        self.aecgs_less_qts.setToolTip(
            "Percentage of aECGs with less QT intervals than specified per "
            "protocol")
        self.aecgs_less_qts.setReadOnly(True)

        self.aecgs_annotations_multiple_leads = QLineEdit()
        self.aecgs_annotations_multiple_leads.setToolTip(
            "Percentage of aECGs with QT annotations in multiple leads")
        self.aecgs_annotations_multiple_leads.setReadOnly(True)

        self.aecgs_annotations_no_primary_lead = QLineEdit()
        self.aecgs_annotations_no_primary_lead.setToolTip(
            "Percentage of aECGs with QT annotations not in the primary lead")
        self.aecgs_annotations_no_primary_lead.setReadOnly(True)

        self.aecgs_with_errors = QLineEdit()
        self.aecgs_with_errors.setToolTip("Number of aECG files with errors")
        self.aecgs_with_errors.setReadOnly(True)

        self.aecgs_potentially_digitized = QLineEdit()
        self.aecgs_potentially_digitized.setToolTip(
            "Number of aECG files potentially digitized (i.e., with more than "
            "5% of samples missing)")
        self.aecgs_potentially_digitized.setReadOnly(True)

        self.study_dir = QLineEdit()
        self.study_dir.setToolTip("Directory containing the aECG files")
        self.study_dir_button = QPushButton("...")
        self.study_dir_button.clicked.connect(self.select_study_dir)
        self.study_dir_button.setToolTip("Open select directory dialog")

        self.validator_form_layout.addRow("Application Type", self.app_type)
        self.validator_form_layout.addRow("Application Number", self.app_num)
        self.validator_form_layout.addRow("Study name/ID", self.study_id)
        self.validator_form_layout.addRow("Sponsor", self.study_sponsor)
        self.validator_form_layout.addRow("Study description",
                                          self.study_info_description)

        self.validator_form_layout.addRow("Annotations in",
                                          self.study_annotation_aecg_cb)
        self.validator_form_layout.addRow("Annotations primary lead",
                                          self.study_annotation_lead_cb)

        self.validator_grid_layout.addWidget(QLabel(""), 0, 0)
        self.validator_grid_layout.addWidget(
            QLabel("Per study protocol or report"), 0, 1)
        self.validator_grid_layout.addWidget(QLabel("Found in aECG files"), 0,
                                             2)

        self.validator_grid_layout.addWidget(QLabel("Number of subjects"), 1,
                                             0)
        self.validator_grid_layout.addWidget(self.study_numsubjects, 1, 1)
        self.validator_grid_layout.addWidget(self.aecg_numsubjects, 1, 2)

        self.validator_grid_layout.addWidget(
            QLabel("Number of aECG per subject"), 2, 0)
        self.validator_grid_layout.addWidget(self.study_aecgpersubject, 2, 1)
        self.validator_grid_layout.addWidget(self.aecg_aecgpersubject, 2, 2)

        self.validator_grid_layout.addWidget(QLabel("Total number of aECG"), 3,
                                             0)
        self.validator_grid_layout.addWidget(self.study_numaecg, 3, 1)
        self.validator_grid_layout.addWidget(self.aecg_numaecg, 3, 2)

        self.validator_grid_layout.addWidget(
            QLabel("Number of beats per aECG"), 4, 0)
        self.validator_grid_layout.addWidget(self.study_annotation_numbeats, 4,
                                             1)

        self.validator_grid_layout.addWidget(
            QLabel("Subjects with fewer ECGs"), 5, 1)
        self.validator_grid_layout.addWidget(self.subjects_less_aecgs, 5, 2)
        self.validator_grid_layout.addWidget(QLabel("Subjects with more ECGs"),
                                             6, 1)
        self.validator_grid_layout.addWidget(self.subjects_more_aecgs, 6, 2)
        self.validator_grid_layout.addWidget(
            QLabel("aECGs without annotations"), 7, 1)
        self.validator_grid_layout.addWidget(self.aecgs_no_annotations, 7, 2)
        self.validator_grid_layout.addWidget(
            QLabel("aECGs without expected number of QTs in primary lead"), 8,
            1)
        self.validator_grid_layout.addWidget(
            self.aecgs_less_qt_in_primary_lead, 8, 2)
        self.validator_grid_layout.addWidget(
            QLabel("aECGs without expected number of QTs"), 9, 1)
        self.validator_grid_layout.addWidget(self.aecgs_less_qts, 9, 2)

        self.validator_grid_layout.addWidget(
            QLabel("aECGs annotated in multiple leads"), 10, 1)
        self.validator_grid_layout.addWidget(
            self.aecgs_annotations_multiple_leads, 10, 2)
        self.validator_grid_layout.addWidget(
            QLabel("aECGs with annotations not in primary lead"), 11, 1)
        self.validator_grid_layout.addWidget(
            self.aecgs_annotations_no_primary_lead, 11, 2)
        self.validator_grid_layout.addWidget(QLabel("aECGs with errors"), 12,
                                             1)
        self.validator_grid_layout.addWidget(self.aecgs_with_errors, 12, 2)

        self.validator_grid_layout.addWidget(
            QLabel("Potentially digitized aECGs"), 13, 1)
        self.validator_grid_layout.addWidget(self.aecgs_potentially_digitized,
                                             13, 2)

        self.validator_form_layout.addRow(self.validator_grid_layout)

        tmp = QHBoxLayout()
        tmp.addWidget(self.study_dir)
        tmp.addWidget(self.study_dir_button)
        self.validator_form_layout.addRow("Study aECGs directory", tmp)

        self.validator_form_layout.addRow("Study index file",
                                          self.study_info_file)

        self.validator_layout.addWidget(self.validator_layout_container)
        self.validator_effective_dirs = QLabel("")
        self.validator_effective_dirs.setWordWrap(True)
        self.validator_layout.addWidget(self.validator_effective_dirs)

        self.val_button = QPushButton("Generate/update study index")
        self.val_button.clicked.connect(self.importstudy_dialog)
        self.validator_layout.addWidget(self.val_button)
        self.cancel_val_button = QPushButton("Cancel study index generation")
        self.cancel_val_button.clicked.connect(self.cancel_validator)
        self.cancel_val_button.setEnabled(False)
        self.validator_layout.addWidget(self.cancel_val_button)
        self.validator_pl = QLabel("")
        self.validator_layout.addWidget(self.validator_pl)
        self.validator_pb = QProgressBar()
        self.validator_layout.addWidget(self.validator_pb)
        self.validator.setLayout(self.validator_layout)
        self.stop_indexing = False

        self.lastindexing_starttime = None

        self.update_validator_effective_dirs()

    def effective_aecgs_dir(self, navwidget, silent=False):
        aecgs_effective_dir = self.study_dir.text()
        if navwidget.project_loaded != '':
            # Path specified in the GUI
            potential_aecgs_dirs = [self.study_dir.text()]
            # StudyDir path from current working directory
            potential_aecgs_dirs += [self.studyindex_info.StudyDir]
            # StudyDir path from directory where the index is located
            potential_aecgs_dirs += [
                os.path.join(os.path.dirname(navwidget.project_loaded),
                             self.studyindex_info.StudyDir)
            ]
            # StudyDir replaced with the directory where the index is located
            potential_aecgs_dirs += [os.path.dirname(navwidget.project_loaded)]
            dir_found = False
            # Get xml and zip filenames from first element in the index
            aecg_xml_file = navwidget.data_index["AECGXML"][0]
            zipfile = ""
            if aecg_xml_file != "":
                zipfile = navwidget.data_index["ZIPFILE"][0]
            for p in potential_aecgs_dirs:
                testfn = os.path.join(p, aecg_xml_file)
                if zipfile != "":
                    testfn = os.path.join(p, zipfile)
                if os.path.isfile(testfn):
                    dir_found = True
                    aecgs_effective_dir = p
                    break
            if not silent:
                if not dir_found:
                    QMessageBox.warning(
                        self, f"Study aECGs directory not found",
                        f"The following paths were checked:"
                        f"{[','.join(p) for p in potential_aecgs_dirs]} and "
                        f"none of them is valid")
                elif p != self.study_dir.text():
                    QMessageBox.warning(
                        self, f"Study aECGs directory not found",
                        f"The path specified in the study aECGs directory is "
                        f"not valid and {p} is being used instead. Check and "
                        f"update the path in Study aECGs directory textbox if "
                        f"the suggested path is not the adequate path")
        return aecgs_effective_dir

    def update_validator_effective_dirs(self):
        msg = f"Working directory: {os.getcwd()}"
        if self.parent() is not None:
            if isinstance(self.parent(), QSplitter):
                navwidget = self.parent().parent()
            else:  # Tabs widget has not been allocated the QSplitter yet
                navwidget = self.parent()
            project_loaded = navwidget.project_loaded
            if project_loaded != '':
                msg = f"{msg}\nLoaded project index: "\
                      f"{navwidget.project_loaded}"
                effective_aecgs_path = self.effective_aecgs_dir(navwidget)
                msg = f"{msg}\nEffective study aECGs directory: "\
                      f"{effective_aecgs_path}"
            else:
                msg = f"{msg}\nLoaded project index: None"
        else:
            msg = f"{msg}\nLoaded project index: None"
        self.validator_effective_dirs.setText(msg)

    def load_study_info(self, fileName):
        self.study_info_file.setText(fileName)
        try:
            study_info = pd.read_excel(fileName, sheet_name="Info")
            self.studyindex_info = aecg.tools.indexer.StudyInfo()
            self.studyindex_info.__dict__.update(
                study_info.set_index("Property").transpose().reset_index(
                    drop=True).to_dict('index')[0])
            sponsor = ""
            description = ""
            if self.studyindex_info.Sponsor is not None and\
                    isinstance(self.studyindex_info.Sponsor, str):
                sponsor = self.studyindex_info.Sponsor
            if self.studyindex_info.Description is not None and\
                    isinstance(self.studyindex_info.Description, str):
                description = self.studyindex_info.Description
            self.study_sponsor.setText(sponsor)
            self.study_info_description.setText(description)
            self.app_type.setText(self.studyindex_info.AppType)
            self.app_num.setText(f"{int(self.studyindex_info.AppNum):06d}")
            self.study_id.setText(self.studyindex_info.StudyID)
            self.study_numsubjects.setText(str(self.studyindex_info.NumSubj))
            self.study_aecgpersubject.setText(
                str(self.studyindex_info.NECGSubj))
            self.study_numaecg.setText(str(self.studyindex_info.TotalECGs))

            anns_in = self.studyindex_info.AnMethod.upper()
            idx = 0
            if anns_in == "RHYTHM":
                idx = 0
            elif anns_in == "DERIVED":
                idx = 1
            elif anns_in == "HOLTER_RHYTHM":
                idx = 2
            elif anns_in == "HOLTER_MEDIAN_BEAT":
                idx = 3
            else:
                idx = int(anns_in) - 1
            self.study_annotation_aecg_cb.setCurrentIndex(idx)

            the_lead = self.studyindex_info.AnLead
            idx = self.study_annotation_lead_cb.findText(str(the_lead))
            if idx == -1:
                idx = self.study_annotation_lead_cb.findText("MDC_ECG_LEAD_" +
                                                             str(the_lead))
            if idx == -1:
                idx = int(the_lead)
            self.study_annotation_lead_cb.setCurrentIndex(idx)

            self.study_annotation_numbeats.setText(
                str(self.studyindex_info.AnNbeats))
            if self.studyindex_info.StudyDir == "":
                self.studyindex_info.StudyDir = os.path.dirname(fileName)
            self.study_dir.setText(self.studyindex_info.StudyDir)

            self.update_validator_effective_dirs()
            self.setCurrentWidget(self.validator)

        except Exception as ex:
            QMessageBox.critical(
                self, "Import study error",
                "Error reading the study information file: '" + fileName + "'")

    def setup_waveforms(self):
        wflayout = QVBoxLayout()

        # ECG plot layout selection box
        self.cbECGLayout.addItems(
            ['12-lead stacked', '3x4 + lead II rhythm', 'Superimposed'])
        self.cbECGLayout.currentIndexChanged.connect(
            self.ecgplotlayout_changed)

        # Zoom buttons
        blayout = QHBoxLayout()

        pb_zoomin = QPushButton()
        pb_zoomin.setText("Zoom +")
        pb_zoomin.clicked.connect(self.zoom_in)

        pb_zoomreset = QPushButton()
        pb_zoomreset.setText("Zoom 1:1")
        pb_zoomreset.clicked.connect(self.zoom_reset)

        pb_zoomout = QPushButton()
        pb_zoomout.setText("Zoom -")
        pb_zoomout.clicked.connect(self.zoom_out)

        blayout.addWidget(self.cbECGLayout)
        blayout.addWidget(pb_zoomout)
        blayout.addWidget(pb_zoomreset)
        blayout.addWidget(pb_zoomin)

        wflayout.addLayout(blayout)

        # Add QScrollArea to main layout of waveforms tab
        self.aecg_display_area.setWidgetResizable(False)
        wflayout.addWidget(self.aecg_display_area)
        self.waveforms.setLayout(wflayout)
        size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        size.setHeightForWidth(False)
        self.aecg_display_area.setSizePolicy(size)
        self.waveforms.setSizePolicy(size)

    def setup_xmlviewer(self):
        wf_layout = QHBoxLayout()
        wf_layout.addWidget(self.xml_display)
        self.xmlviewer.setLayout(wf_layout)

    def setup_options(self):
        self.options_layout = QFormLayout()
        self.aecg_schema_filename = QLineEdit(aecg.get_aecg_schema_location())
        self.options_layout.addRow("aECG XML schema path",
                                   self.aecg_schema_filename)

        self.save_index_every_n_aecgs = QSpinBox()
        self.save_index_every_n_aecgs.setMinimum(0)
        self.save_index_every_n_aecgs.setMaximum(50000)
        self.save_index_every_n_aecgs.setValue(0)
        self.save_index_every_n_aecgs.setSingleStep(100)
        self.save_index_every_n_aecgs.setSuffix(" aECGs")
        self.save_index_every_n_aecgs.setToolTip(
            "Set o 0 to save the study index file only after its generation "
            "is completed.\nOtherwise, the file is saved everytime the "
            " specified number of ECGs have been appended to the index.")
        self.options_layout.addRow("Save index every ",
                                   self.save_index_every_n_aecgs)

        self.save_all_intervals_cb = QCheckBox("")
        self.save_all_intervals_cb.setChecked(False)
        self.options_layout.addRow("Save individual beat intervals",
                                   self.save_all_intervals_cb)

        self.parallel_processing_cb = QCheckBox("")
        self.parallel_processing_cb.setChecked(True)
        self.options_layout.addRow("Parallel processing of files",
                                   self.parallel_processing_cb)

        self.options.setLayout(self.options_layout)

    def zoom_in(self):
        self.aecg_display.apply_zoom(self.aecg_display.zoom_factor + 0.1)

    def zoom_out(self):
        self.aecg_display.apply_zoom(self.aecg_display.zoom_factor - 0.1)

    def zoom_reset(self):
        self.aecg_display.apply_zoom(1.0)

    def ecgplotlayout_changed(self, i):
        self.aecg_display.update_aecg_plot(
            ecg_layout=aecg.utils.ECG_plot_layout(i + 1))

    def update_search_progress(self, i, n):
        self.validator_pl.setText(
            f"Searching aECGs in directory ({n} XML files found)")

    def update_progress(self, i, n):
        j = i
        m = n
        if i <= 1:
            j = 1
            if self.validator_pb.value() > 0:
                j = self.validator_pb.value() + 1
            m = self.validator_pb.maximum()
        running_time = self.indexing_timer.elapsed() * 1e-3  # in seconds
        time_per_item = running_time / j
        # reamining = seconds per item so far * total pending items to process
        remaining_time = time_per_item * (m - j)
        eta = datetime.datetime.now() +\
            datetime.timedelta(seconds=round(remaining_time, 0))
        self.validator_pl.setText(
            f"Validating aECG {j}/{m} | "
            f"Execution time: "
            f"{str(datetime.timedelta(0,seconds=round(running_time)))} | "
            f"{round(1/time_per_item,2)} aECGs per second | "
            f"ETA: {eta.isoformat(timespec='seconds')}")
        self.validator_pb.setValue(j)
        if self.save_index_every_n_aecgs.value() > 0 and\
                len(self.directory_indexer.studyindex) % \
                self.save_index_every_n_aecgs.value() == 0:
            self.save_validator_results(
                pd.concat(self.directory_indexer.studyindex,
                          ignore_index=True))

    def save_validator_results(self, res):
        if res.shape[0] > 0:
            self.studyindex_info = aecg.tools.indexer.StudyInfo()
            self.studyindex_info.StudyDir = self.study_dir.text()
            self.studyindex_info.IndexFile = self.study_info_file.text()
            self.studyindex_info.Sponsor = self.study_sponsor.text()
            self.studyindex_info.Description =\
                self.study_info_description.text()
            self.studyindex_info.Date = self.lastindexing_starttime.isoformat()
            self.studyindex_info.End_date = datetime.datetime.now().isoformat()
            self.studyindex_info.Version = aecg.__version__
            self.studyindex_info.AppType = self.app_type.text()
            self.studyindex_info.AppNum = f"{int(self.app_num.text()):06d}"
            self.studyindex_info.StudyID = self.study_id.text()
            self.studyindex_info.NumSubj = int(self.study_numsubjects.text())
            self.studyindex_info.NECGSubj = int(
                self.study_aecgpersubject.text())
            self.studyindex_info.TotalECGs = int(self.study_numaecg.text())
            anmethod = aecg.tools.indexer.AnnotationMethod(
                self.study_annotation_aecg_cb.currentIndex())
            self.studyindex_info.AnMethod = anmethod.name
            self.studyindex_info.AnLead =\
                self.study_annotation_lead_cb.currentText()
            self.studyindex_info.AnNbeats = int(
                self.study_annotation_numbeats.text())

            # Calculate stats
            study_stats = aecg.tools.indexer.StudyStats(
                self.studyindex_info, res)

            # Save to file
            aecg.tools.indexer.save_study_index(self.studyindex_info, res,
                                                study_stats)

    validator_data_ready = Signal()

    def save_validator_results_and_load_index(self, res):
        self.save_validator_results(res)
        self.validator_data_ready.emit()

    def indexer_validator_results(self, res):
        self.studyindex_df = pd.concat([self.studyindex_df, res],
                                       ignore_index=True)

    def subindex_thread_complete(self):
        return

    def index_directory_thread_complete(self):
        tmp = self.validator_pl.text().replace("ETA:", "Completed: ").replace(
            "Validating", "Validated")
        self.validator_pl.setText(tmp)
        self.val_button.setEnabled(True)
        self.cancel_val_button.setEnabled(False)
        self.validator_layout_container.setEnabled(True)

    def index_directory(self, progress_callback):
        self.lastindexing_starttime = datetime.datetime.now()
        self.indexing_timer.start()

        studyindex_df = []
        n_cores = os.cpu_count()
        aecg_schema = None
        if self.aecg_schema_filename.text() != "":
            aecg_schema = self.aecg_schema_filename.text()
        if self.parallel_processing_cb.isChecked():
            studyindex_df = self.directory_indexer.index_directory(
                self.save_all_intervals_cb.isChecked(), aecg_schema, n_cores,
                progress_callback)
        else:
            studyindex_df = self.directory_indexer.index_directory(
                self.save_all_intervals_cb.isChecked(), aecg_schema, 1,
                progress_callback)

        return studyindex_df

    def importstudy_dialog(self):
        dirName = os.path.normpath(self.study_dir.text())
        if dirName != "":
            if os.path.exists(dirName):
                self.directory_indexer = aecg.indexing.DirectoryIndexer()
                self.directory_indexer.set_aecg_dir(
                    dirName, self.update_search_progress)
                self.validator_pb.setMaximum(self.directory_indexer.num_files)
                self.validator_pb.reset()
                self.stop_indexing = False
                self.validator_layout_container.setEnabled(False)
                self.val_button.setEnabled(False)
                self.cancel_val_button.setEnabled(True)
                self.validator_worker = Worker(self.index_directory)
                self.validator_worker.signals.result.connect(
                    self.save_validator_results_and_load_index)
                self.validator_worker.signals.finished.connect(
                    self.index_directory_thread_complete)
                self.validator_worker.signals.progress.connect(
                    self.update_progress)

                # Execute
                self.threadpool.start(self.validator_worker)
            else:
                QMessageBox.critical(
                    self, "Directory not found",
                    f"Specified study directory not found:\n{dirName}")
        else:
            QMessageBox.critical(self, "Import study error",
                                 "Study directory cannot be empty")

    def cancel_validator(self):
        self.cancel_val_button.setEnabled(False)
        self.stop_indexing = True
        self.directory_indexer.cancel_indexing = True
        self.threadpool.waitForDone(3000)
        self.val_button.setEnabled(True)

    def select_study_dir(self):
        cd = self.study_dir.text()
        if cd == "":
            cd = "."
        dir = QFileDialog.getExistingDirectory(
            self, "Open Directory", cd,
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
        if dir != "":
            self.study_dir.setText(dir)
Пример #19
0
class Blinker(QThread):
    """
	The Blinker class is used to draw a box around a given element at a specified frequency for a small
	amount of time. Because the box sometimes disappears on its own, this can cause a blinking affect.
	"""

    componentNotFound = Signal(str)

    INTERVAL_MILLIS = 250
    DURATION_MILLIS = 10_000

    colors = ["red", "green", "blue"]
    curColorIdx = 0

    def __init__(self, pid: int, backend: str,
                 superToken: 'SuperToken') -> None:
        """
		Creates a blinker that will draw a box around the component represented by SuperToken periodically
		if the component can be found.
		
		:param pid: The id of the target application's process.
		:type pid: int
		:param backend: either "win32" or "uia" depending on target application.
		:type backend: str
		:param superToken: The supertoken that represents the component that we want to draw a box around.
		:return: None
		:retype: NoneType
		"""
        QThread.__init__(self)
        self._pid = pid
        self._backend = backend
        self._superToken = superToken
        self._color = Blinker.colors[Blinker.curColorIdx % len(Blinker.colors)]
        Blinker.curColorIdx += 1

    def run(self) -> None:
        """
		DO NOT CALL THIS METHOD!
		This method is called automatically when the start() method is called.
		
		This method searches for a Component in the target GUI by traversing
		:return: None
		:rtype: NoneType
		"""
        self._process = psutil.Process(self._pid)
        app = Application(backend=self._backend)
        app.setProcess(self._process)

        options = {
            MatchOption.ExactToken, MatchOption.CloseToken,
            MatchOption.PWABestMatch
        }
        finder = ComponentFinder(app, options)

        try:
            component = finder.find(self._superToken)
        except ComponentNotFoundException:
            self.componentNotFound.emit(
                "The selected component could not be\nfound in the target GUI."
            )
        else:
            self.initiateBlinkSequence(component)

    def initiateBlinkSequence(self, component: 'Component') -> None:
        """
		Starts the blink sequence by setting timers and executing an event loop.
		
		NOTE: Because this function executes an event loop, it is blocking.
		
		:param component: The component to select.
		:type component: Component
		:return: None
		:rtype: NoneType
		"""
        self._component = component
        self._component.top_level_parent().set_focus()
        self._timer = QTimer(self)
        self._timer.timeout.connect(lambda: self.tick())
        self._stopWatch = QElapsedTimer()
        self._timer.start(Blinker.INTERVAL_MILLIS)
        self._stopWatch.start()
        self.exec_()

    def tick(self) -> None:
        """
		Draws an outline around the component of interest.
		
		:return: None
		:rtype: NoneType
		"""
        try:
            self._component.draw_outline(colour=self._color, thickness=5)
            self._component.top_level_parent().set_focus()
        except:
            pass

        if self._stopWatch.hasExpired(Blinker.DURATION_MILLIS):
            self.stop()

    def stop(self) -> None:
        """
		Stops the blinker regardless of whether it was running or not.
		
		:return: None
		:rtype: NoneType
		"""
        try:
            self._timer.stop()
        except:
            pass
        finally:
            self.quit()
Пример #20
0
class DSRViewer(QObject):
    save_graph_signal = Signal()
    close_window_signal = Signal()
    reset_viewer = Signal(QWidget)

    def __init__(self, window, G, options, main=None):
        super().__init__()
        self.timer = QTimer()
        self.alive_timer = QElapsedTimer()
        self.g = G
        self.window = window
        self.view_menu = QMenu()
        self.file_menu = QMenu()
        self.forces_menu = QMenu()
        self.main_widget = window
        self.docks = {}
        self.widgets = {}
        self.widgets_by_type = {}

        available_geometry = QApplication.desktop().availableGeometry()
        window.move((available_geometry.width() - window.width()) / 2,
                    (available_geometry.height() - window.height()) / 2)
        self.__initialize_file_menu()
        viewMenu = window.menuBar().addMenu(window.tr("&View"))
        forcesMenu = window.menuBar().addMenu(window.tr("&Forces"))
        actionsMenu = window.menuBar().addMenu(window.tr("&Actions"))
        restart_action = actionsMenu.addAction("Restart")

        self.__initialize_views(options, main)
        self.alive_timer.start()
        self.timer.start(500)
        # self.init()  #intialize processor number
        # connect(timer, SIGNAL(timeout()), self, SLOT(compute()))

    def __del__(self):
        settings = QSettings("RoboComp", "DSR")
        settings.beginGroup("MainWindow")
        settings.setValue("size", self.window.size())
        settings.setValue("pos", self.window.pos())
        settings.endGroup()

    def get_widget_by_type(self, widget_type) -> QWidget:
        if widget_type in self.widgets_by_type:
            return self.widgets_by_type[widget_type].widget
        return None

    def get_widget_by_name(self, name) -> QWidget:
        if name in self.widgets:
            return self.widgets[name].widget
        return None

    def add_custom_widget_to_dock(self, name, custom_view):
        widget_c = WidgetContainer()
        widget_c.name = name
        widget_c.type = View.none
        widget_c.widget = custom_view
        self.widgets[name] = widget_c
        self.__create_dock_and_menu(name, custom_view)
        # Tabification of current docks
        previous = None
        for dock_name, dock in self.docks.items():
            if previous and previous != dock:
                self.window.tabifyDockWidget(previous, self.docks[name])
                break
            previous = dock
        self.docks[name].raise_()

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close_window_signal.emit()

    # SLOTS
    def save_graph_slot(self, state):
        self.save_graph_signal.emit()

    def restart_app(self, state):
        pass

    def switch_view(self, state, container):
        widget = container.widget
        dock = container.dock
        if state:
            widget.blockSignals(True)
            dock.hide()
        else:
            widget.blockSignals(False)
            self.reset_viewer.emit(widget)
            dock.show()
            dock.raise_()

    def compute(self):
        pass

    def __create_dock_and_menu(self, name, view):
        # TODO: Check if name exists in docks
        if name in self.docks:
            dock_widget = self.docks[name]
            self.window.removeDockWidget(dock_widget)
        else:
            dock_widget = QDockWidget(name)
            new_action = QAction(name, self)
            new_action.setStatusTip("Create a new file")
            new_action.setCheckable(True)
            new_action.setChecked(True)
            new_action.triggered.connect(
                lambda state: self.switch_view(state, self.widgets[name]))
            self.view_menu.addAction(new_action)
            self.docks[name] = dock_widget
            self.widgets[name].dock = dock_widget
        dock_widget.setWidget(view)
        dock_widget.setAllowedAreas(Qt.AllDockWidgetAreas)
        self.window.addDockWidget(Qt.RightDockWidgetArea, dock_widget)
        dock_widget.raise_()

    def __initialize_views(self, options, central):
        # Create docks view and main widget
        valid_options = [(View.graph, "Graph"), (View.tree, "Tree"),
                         (View.osg, "3D"), (View.scene, "2D")]

        # Creation of docks and mainwidget
        for widget_type, widget_name in valid_options:
            if widget_type == central and central != View.none:
                viewer = self.__create_widget(widget_type)
                self.window.setCentralWidget(viewer)
                widget_c = WidgetContainer()
                widget_c.widget = viewer
                widget_c.name = widget_name
                widget_c.type = widget_type
                self.widgets[widget_name] = widget_c
                self.widgets_by_type[widget_type] = widget_c
                self.main_widget = viewer
            elif options & widget_type:
                viewer = self.__create_widget(widget_type)
                widget_c = WidgetContainer()
                widget_c.widget = viewer
                widget_c.name = widget_name
                widget_c.type = widget_type
                self.widgets[widget_name] = widget_c
                self.widgets_by_type[widget_type] = widget_c
                self.__create_dock_and_menu(widget_name, viewer)
        if View.graph in self.widgets_by_type:
            new_action = QAction("Animation", self)
            new_action.setStatusTip("Toggle animation")
            new_action.setCheckable(True)
            new_action.setChecked(False)
            self.forces_menu.addAction(new_action)
            new_action.triggered.connect(lambda: self.widgets_by_type[
                View.graph].widget.toggle_animation(True))

        # Tabification of current docks
        previous = None
        for dock_name, dock_widget in self.docks.items():
            if previous:
                self.window.tabifyDockWidget(previous, dock_widget)
            previous = dock_widget

        # Connection of tree to graph signals
        if "Tree" in self.docks:
            if self.main_widget:
                graph_widget = self.main_widget
                if graph_widget:
                    tree_widget = self.docks["Tree"].widget()
                    tree_widget.node_check_state_changed_signal.connect(
                        lambda node_id: graph_widget.hide_show_node_SLOT(
                            node_id, 2))
        if len(self.docks) > 0 or central != None:
            self.window.show()
        else:
            self.window.showMinimized()

    def __initialize_file_menu(self):
        file_menu = self.window.menuBar().addMenu(self.window.tr("&File"))
        file_submenu = file_menu.addMenu("Save")
        save_action = QAction("Save", self)
        file_submenu.addAction(save_action)
        rgbd = QAction("RGBD", self)
        rgbd.setCheckable(True)
        rgbd.setChecked(False)
        file_submenu.addAction(rgbd)
        laser = QAction("Laser", self)
        laser.setCheckable(True)
        laser.setChecked(False)
        file_submenu.addAction(laser)
        # save_action
        save_action.triggered.connect(
            lambda: self.__save_json_file(rgbd, laser))

    def __save_json_file(self, rgbd, laser):
        file_name = QFileDialog.getSaveFileName(
            None, "Save file",
            "/home/robocomp/robocomp/components/dsr-graph/etc",
            "JSON Files (*.json)", None,
            QFileDialog.Option.DontUseNativeDialog)
        skip_content = []
        if not rgbd.isChecked():
            skip_content.push_back("rgbd")
        if not laser.isChecked():
            skip_content.push_back("laser")
        self.g.write_to_json_file(file_name.toStdString(), skip_content)
        print("File saved")

    def __create_widget(self, widget_type):
        widget_view = None
        if widget_type == View.graph:
            widget_view = GraphViewer(self.g)
        elif widget_type == View.osg:
            widget_view = OSG3dViewer(self.g, 1, 1)
        elif widget_type == View.tree:
            widget_view = TreeViewer(self.g)
        elif widget_type == View.scene:
            widget_view = QScene2dViewer(self.g)
        elif widget_type == View.none:
            widget_view = None
        # self.reset_viewer.connect(self.reload)
        return widget_view
Пример #21
0
class Ui_Query(QMainWindow):
    about_close = Signal()

    def __init__(self, totaltime=0):
        super().__init__()
        # 在主窗口左侧添加题干和选项
        lsplitter = QSplitter(Qt.Vertical)
        self.question_panel = Question_panel()
        lsplitter.addWidget(self.question_panel)
        # 在主窗口右侧添加绘图和文本编辑,并把比例设置为3比1
        rsplitter = QSplitter(Qt.Vertical)
        self.painter = QGraphicsView(rsplitter)
        self.note = QTextEdit(rsplitter)
        rsplitter.setStretchFactor(0, 3)
        rsplitter.setStretchFactor(1, 1)
        # 添加插件的顺序会导致左右不同
        mainSplitter = QSplitter(Qt.Horizontal)
        mainSplitter.addWidget(lsplitter)
        mainSplitter.addWidget(rsplitter)
        self.setCentralWidget(mainSplitter)
        # 点击暂停按钮切换图标和停继时间
        self.question_panel.ui.pushButtonPause.clicked.connect(
            self.toggle_play_and_pause)
        self.totaltime = totaltime  # 当前试卷答题总时间
        self.elapsed_time = QElapsedTimer()  # 答题总时间的计时器
        self.paused = False  # 默认刚打开时,还未暂停时间
        self.setTime()  # 更新时间显示
        self.timer = QTimer()
        self.timer.timeout.connect(self.setTime)  # 每秒更新时间显示的定时器

    def getDateTime(self):
        """获取当前日期及时间"""
        return QDateTime.currentDateTime().toTime_t()

    def setTime(self):
        """更新时间显示"""
        time = (self.elapsed_time.elapsed() +
                self.totaltime) // 1000  # totaltime还包括上次答题所用的时间
        hours = time // 3600
        minutes = time % 3600 // 60
        seconds = time % 3600 % 60
        self.question_panel.ui.labelTimeUsed.setText(
            f"{hours}:{minutes:02}:{seconds:02}")  # :02表示补全数字到2位,填充0

    def toggle_play_and_pause(self):
        if self.paused:
            # 继续计时
            # 把继续图标换回暂停图标
            icon_pause = QIcon()
            icon_pause.addPixmap(
                QPixmap(":/icons/icons/ic_pause_black_48dp.png"),
                QIcon.Normal,
                QIcon.Off,
            )
            self.question_panel.ui.pushButtonPause.setIcon(icon_pause)
            # 重新开始计时,包括1秒定时器和总时间计时器
            self.timer.start(1000)
            self.elapsed_time.restart()
            # 把自身状态标记为非暂停状态
            self.paused = False
        else:
            # 暂停计时
            icon_play = QIcon()
            icon_play.addPixmap(
                QPixmap(":/icons/icons/ic_play_arrow_black_48dp.png"),
                QIcon.Normal,
                QIcon.Off,
            )
            self.question_panel.ui.pushButtonPause.setIcon(icon_play)
            self.totaltime += self.elapsed_time.elapsed()
            self.timer.stop()
            self.paused = True

    def closeEvent(self, event):
        self.totaltime += self.elapsed_time.elapsed()  # 关闭前更新答题总时间
        self.about_close.emit()