class CountdownWidget(QWidget): """Define custom widget as countdown control panel.""" def __init__(self, ctrl, parent=None): """Init completer.""" super().__init__(parent) self.controller = ctrl self.createLayout() self.loadSettings() self.connect() def createLayout(self): """Create widget to control the countdown browser source.""" layout = QGridLayout() self.rb_static = QRadioButton(_("Static Countdown to date:"), self) layout.addWidget(self.rb_static, 0, 0) self.rb_dynamic = QRadioButton(_("Dynamic Countdown duration:"), self) self.rb_dynamic.setChecked(True) self.rb_dynamic.toggled.connect(self.toggleRadio) layout.addWidget(self.rb_dynamic, 1, 0) self.te_datetime = QDateTimeEdit() self.te_datetime.setCalendarPopup(True) self.te_datetime.setContextMenuPolicy(Qt.CustomContextMenu) self.te_datetime.customContextMenuRequested.connect( self.openDateTimeMenu) layout.addWidget(self.te_datetime, 0, 1) self.te_duration = QTimeEdit() self.te_duration.setDisplayFormat("HH 'h' mm 'm' ss 's'") self.te_duration.setContextMenuPolicy(Qt.CustomContextMenu) self.te_duration.customContextMenuRequested.connect( self.openDurationMenu) layout.addWidget(self.te_duration, 1, 1) self.event_label = QLabel(' ' + _('Event description:')) layout.addWidget(self.event_label, 0, 2) self.le_desc = QLineEdit() self.le_desc.setAlignment(Qt.AlignCenter) layout.addWidget(self.le_desc, 0, 3, 1, 2) self.cb_restart = QCheckBox( _('Restart countdown when source becomes active')) layout.addWidget(self.cb_restart, 1, 2, 1, 2) self.pb_start = QPushButton(" " + _('Start Countdown') + " ") layout.addWidget(self.pb_start, 1, 4) layout.setColumnStretch(2, 1) layout.setColumnStretch(3, 2) self.setLayout(layout) def openDateTimeMenu(self, position): """Open menu to set date to today.""" menu = QMenu() act1 = QAction(_("Set Today")) act1.triggered.connect(self.setToday) menu.addAction(act1) menu.exec_(QCursor.pos()) def openDurationMenu(self, position): """Open menu to set the duration.""" menu = QMenu() for duration in [15, 10, 5, 3, 1]: act = QAction(_("Set {} min").format(duration), menu) act.triggered.connect( lambda x, duration=duration: self.setDuration(duration)) menu.addAction(act) menu.exec_(QCursor.pos()) def setToday(self): """Set date to today.""" today = QDateTime.currentDateTime() today.setTime(self.te_datetime.time()) self.te_datetime.setDateTime(today) def setFromTimestamp(self, timestamp): """Set time and date based on timestamp.""" self.te_datetime.setDateTime(QDateTime.fromTime_t(int(timestamp))) def setDuration(self, duration): """Set the duration.""" self.te_duration.setTime(QTime(0, duration, 0)) def toggleRadio(self): """Toggle radio buttion.""" static = self.rb_static.isChecked() self.te_datetime.setEnabled(static) self.te_duration.setEnabled(not static) self.cb_restart.setEnabled(not static) self.pb_start.setEnabled(not static) def loadSettings(self): """Load data from settings.""" static = scctool.settings.config.parser.getboolean( "Countdown", "static") if static: self.rb_static.setChecked(True) else: self.rb_dynamic.setChecked(True) description = scctool.settings.config.parser.get( 'Countdown', 'description') self.le_desc.setText(description.strip()) restart = scctool.settings.config.parser.getboolean( "Countdown", "restart") self.cb_restart.setChecked(restart) duration = QTime() string = scctool.settings.config.parser.get('Countdown', 'duration').strip() duration = QTime.fromString(string, 'HH:mm:ss') self.te_duration.setTime(duration) string = scctool.settings.config.parser.get('Countdown', 'datetime').strip() datetime = QDateTime.fromString(string, 'yyyy-MM-dd HH:mm') self.te_datetime.setDateTime(datetime) def connect(self): """Connect all form elements.""" self.le_desc.textChanged.connect(self.changed_description) self.cb_restart.toggled.connect(self.changed_restart) self.te_datetime.dateTimeChanged.connect(self.changed_datetime) self.te_duration.timeChanged.connect(self.changed_duration) self.rb_static.toggled.connect(self.changed_static) self.pb_start.pressed.connect(self.start_pressed) def changed_description(self): """Change the description.""" desc = self.le_desc.text().strip() scctool.settings.config.parser.set('Countdown', 'description', desc) self.controller.websocketThread.sendData2Path('countdown', "DESC", desc) def changed_restart(self): """Handle change of restart option.""" restart = self.cb_restart.isChecked() scctool.settings.config.parser.set('Countdown', 'restart', str(restart)) self.controller.websocketThread.sendData2Path('countdown', "RESTART", restart) def changed_datetime(self, time): """Handle change of datetime.""" datetime = time.toString('yyyy-MM-dd HH:mm') scctool.settings.config.parser.set('Countdown', 'datetime', datetime) self.sendData() def changed_duration(self, time): """Handle change of duration.""" duration = time.toString('HH:mm:ss') scctool.settings.config.parser.set('Countdown', 'duration', duration) self.sendData() def changed_static(self): """Handle change of static/dynamic.""" static = self.rb_static.isChecked() scctool.settings.config.parser.set('Countdown', 'static', str(static)) self.sendData() def start_pressed(self): """Handle press of the start button.""" self.controller.websocketThread.sendData2Path('countdown', 'START') def sendData(self): """Send the data to the websocket.""" self.controller.websocketThread.sendData2Path( 'countdown', "DATA", self.controller.websocketThread.getCountdownData())
class StartHandicapDialog(QDialog): def __init__(self): super().__init__(GlobalAccess().get_main_window()) self.time_format = 'hh:mm:ss' self.setWindowTitle(_('Handicap start time')) self.setWindowIcon(QIcon(config.ICON)) self.setSizeGripEnabled(False) self.setModal(True) self.layout = QFormLayout(self) self.handicap_mode = QRadioButton(_('Handicap mode')) self.reverse_mode = QRadioButton(_('Reverse mode')) self.layout.addRow(self.handicap_mode) self.layout.addRow(self.reverse_mode) self.zero_time_label = QLabel(_('Start time')) self.zero_time = QTimeEdit() self.zero_time.setDisplayFormat(self.time_format) self.layout.addRow(self.zero_time_label, self.zero_time) self.max_gap_label = QLabel(_('Max gap from leader')) self.max_gap = QTimeEdit() self.max_gap.setDisplayFormat(self.time_format) self.layout.addRow(self.max_gap_label, self.max_gap) self.second_start_time_label = QLabel(_('Start time for 2 group')) self.second_time = QTimeEdit() self.second_time.setDisplayFormat(self.time_format) self.layout.addRow(self.second_start_time_label, self.second_time) self.interval_time_label = QLabel(_('Start interval')) self.interval_time = QTimeEdit() self.interval_time.setDisplayFormat(self.time_format) self.layout.addRow(self.interval_time_label, self.interval_time) self.dsq_offset_label = QLabel(_('Offset after DSQ')) self.dsq_offset = QTimeEdit() self.dsq_offset.setDisplayFormat(self.time_format) self.layout.addRow(self.dsq_offset_label, self.dsq_offset) def mode_changed(): status = self.handicap_mode.isChecked() self.max_gap.setEnabled(status) self.second_time.setEnabled(status) self.dsq_offset.setDisabled(status) self.handicap_mode.toggled.connect(mode_changed) self.reverse_mode.toggled.connect(mode_changed) def cancel_changes(): self.close() def apply_changes(): try: self.apply_changes_impl() except Exception as e: logging.error(str(e)) logging.exception(e) self.close() button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = button_box.button(QDialogButtonBox.Ok) self.button_ok.setText(_('OK')) self.button_ok.clicked.connect(apply_changes) self.button_cancel = button_box.button(QDialogButtonBox.Cancel) self.button_cancel.setText(_('Cancel')) self.button_cancel.clicked.connect(cancel_changes) self.layout.addRow(button_box) self.set_values() self.show() def set_values(self): obj = race() if obj.get_setting('handicap_mode', True): self.handicap_mode.toggle() else: self.reverse_mode.toggle() self.zero_time.setTime( OTime(msec=obj.get_setting('handicap_start', OTime(0, 11).to_msec())).to_time()) self.max_gap.setTime( OTime(msec=obj.get_setting('handicap_max_gap', OTime(0, 0, 30).to_msec())).to_time()) self.second_time.setTime( OTime(msec=obj.get_setting('handicap_second_start', OTime(0, 11, 30).to_msec())).to_time()) self.interval_time.setTime( OTime(msec=obj.get_setting('handicap_interval', OTime(0, 0, 1).to_msec())).to_time()) self.dsq_offset.setTime( OTime(msec=obj.get_setting('handicap_dsq_offset', OTime(0, 0, 10).to_msec())).to_time()) def apply_changes_impl(self): obj = race() obj.set_setting('handicap_mode', self.handicap_mode.isChecked()) obj.set_setting('handicap_start', time_to_otime(self.zero_time.time()).to_msec()) obj.set_setting('handicap_max_gap', time_to_otime(self.max_gap.time()).to_msec()) obj.set_setting('handicap_second_start', time_to_otime(self.second_time.time()).to_msec()) obj.set_setting('handicap_interval', time_to_otime(self.interval_time.time()).to_msec()) obj.set_setting('handicap_dsq_offset', time_to_otime(self.dsq_offset.time()).to_msec()) if obj.get_setting('handicap_mode', True): handicap_start_time() else: reverse_start_time() GlobalAccess().get_main_window().refresh()
class CountdownWidget(QWidget): """Define custom widget as countdown control panel.""" def __init__(self, ctrl, parent=None): """Init completer.""" super().__init__(parent) self.controller = ctrl self.createLayout() self.loadSettings() self.connect() def createLayout(self): """Create widget to control the countdown browser source.""" layout = QGridLayout() self.rb_static = QRadioButton(_("Static Countdown to date:"), self) layout.addWidget(self.rb_static, 0, 0) self.rb_dynamic = QRadioButton(_("Dynamic Countdown duration:"), self) self.rb_dynamic.setChecked(True) self.rb_dynamic.toggled.connect(self.toggleRadio) layout.addWidget(self.rb_dynamic, 1, 0) self.te_datetime = QDateTimeEdit() layout.addWidget(self.te_datetime, 0, 1) self.te_duration = QTimeEdit() self.te_duration.setDisplayFormat("HH 'h' mm 'm' ss 's'") layout.addWidget(self.te_duration, 1, 1) self.event_label = QLabel(' ' + _('Event description:')) layout.addWidget(self.event_label, 0, 2) self.le_desc = QLineEdit() self.le_desc.setAlignment(Qt.AlignCenter) layout.addWidget(self.le_desc, 0, 3, 1, 2) self.cb_restart = QCheckBox( _('Restart countdown when source becomes active')) layout.addWidget(self.cb_restart, 1, 2, 1, 2) self.pb_start = QPushButton(" " + _('Start Countdown') + " ") layout.addWidget(self.pb_start, 1, 4) layout.setColumnStretch(2, 1) layout.setColumnStretch(3, 2) self.setLayout(layout) def toggleRadio(self): static = self.rb_static.isChecked() self.te_datetime.setEnabled(static) self.te_duration.setEnabled(not static) self.cb_restart.setEnabled(not static) self.pb_start.setEnabled(not static) def loadSettings(self): static = scctool.settings.config.parser.getboolean( "Countdown", "static") if static: self.rb_static.setChecked(True) else: self.rb_dynamic.setChecked(True) description = scctool.settings.config.parser.get( 'Countdown', 'description') self.le_desc.setText(description.strip()) restart = scctool.settings.config.parser.getboolean( "Countdown", "restart") self.cb_restart.setChecked(restart) duration = QTime() string = scctool.settings.config.parser.get('Countdown', 'duration').strip() duration = QTime.fromString(string, 'HH:mm:ss') self.te_duration.setTime(duration) string = scctool.settings.config.parser.get('Countdown', 'datetime').strip() datetime = QDateTime.fromString(string, 'yyyy-MM-dd HH:mm') self.te_datetime.setDateTime(datetime) def connect(self): self.le_desc.textChanged.connect(self.changed_description) self.cb_restart.toggled.connect(self.changed_restart) self.te_datetime.dateTimeChanged.connect(self.changed_datetime) self.te_duration.timeChanged.connect(self.changed_duration) self.rb_static.toggled.connect(self.changed_static) self.pb_start.pressed.connect(self.start_pressed) def changed_description(self): desc = self.le_desc.text().strip() scctool.settings.config.parser.set('Countdown', 'description', desc) self.controller.websocketThread.sendData2Path('countdown', "DESC", desc) def changed_restart(self): restart = self.cb_restart.isChecked() scctool.settings.config.parser.set('Countdown', 'restart', str(restart)) self.controller.websocketThread.sendData2Path('countdown', "RESTART", restart) def changed_datetime(self, time): datetime = time.toString('yyyy-MM-dd HH:mm') scctool.settings.config.parser.set('Countdown', 'datetime', datetime) self.sendData() def changed_duration(self, time): duration = time.toString('HH:mm:ss') scctool.settings.config.parser.set('Countdown', 'duration', duration) self.sendData() def changed_static(self): static = self.rb_static.isChecked() scctool.settings.config.parser.set('Countdown', 'static', str(static)) self.sendData() def start_pressed(self): self.controller.websocketThread.sendData2Path('countdown', 'START') def sendData(self): data = {} data['static'] = scctool.settings.config.parser.getboolean( 'Countdown', 'static') data['desc'] = scctool.settings.config.parser.get( 'Countdown', 'description') data['restart'] = scctool.settings.config.parser.getboolean( 'Countdown', 'restart') data['datetime'] = scctool.settings.config.parser.get( 'Countdown', 'datetime') data['duration'] = scctool.settings.config.parser.get( 'Countdown', 'duration') data['replacement'] = scctool.settings.config.parser.get( 'Countdown', 'replacement') self.controller.websocketThread.sendData2Path('countdown', "DATA", data)
class TimeFrame(BaseFrame): time_hacked = pyqtSignal() def __init__(self, is_mission, parent=None): super().__init__(parent) # Setup frame self.label.setText("Time:") zulu_label = QLabel("ZULU") zulu_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) self.time_edit = QTimeEdit() self.time_edit.setDisplayFormat("HH:mm:ss") self.hack_btn = QPushButton("Hack") self.hack_btn.setAutoDefault(False) if is_mission: self.hack_btn.setStyleSheet("background-color: red; color: white;") else: self.hack_btn.setStyleSheet( "background-color: gray; color: white;") self.sys_time_btn = QPushButton("Use System Time") self.sys_time_btn.setAutoDefault(False) if not is_mission: self.time_edit.setEnabled(False) self.hack_btn.setEnabled(False) self.sys_time_btn.setEnabled(False) self.layout.addWidget(self.label) self.layout.addWidget(self.time_edit) self.layout.addWidget(zulu_label) self.layout.addWidget(self.hack_btn) self.layout.addWidget(self.sys_time_btn) self.setLayout(self.layout) # Setup behavior self.time = QTime() self.hack_timer = QTimer() self.hack_timer.setTimerType(Qt.VeryCoarseTimer) self.hack_timer.setInterval(1000) self.hack_timer.timeout.connect(self.inc_time) self.hack_btn.clicked.connect(self.hack_time) self.sys_time_btn.clicked.connect(self.set_sys_time) @pyqtSlot() def hack_time(self, sys_time=None): if not self.hack_timer.isActive(): self.time = self.time_edit.time() if not sys_time else sys_time self.hack_timer.start() self.time_edit.setTime(self.time) self.hack_btn.setStyleSheet( "background-color: green; color: white;") self.hack_btn.setText("Hacked!") self.time_edit.setEnabled(False) self.sys_time_btn.setEnabled(False) else: self.hack_btn.setStyleSheet("background-color: red; color: white;") self.hack_btn.setText("Hack") self.hack_timer.stop() self.time_edit.setEnabled(True) self.sys_time_btn.setEnabled(True) self.time_hacked.emit() @pyqtSlot() def set_sys_time(self): self.hack_time(QTime().currentTime()) @pyqtSlot() def inc_time(self): self.time = self.time.addSecs(1) self.time_edit.setTime(self.time)
class DataWidget(QWidget): def __init__(self, parent, shared_data): super(DataWidget, self).__init__(parent) self.__shared_data = shared_data self.__shared_data.update_sync.emit() # Add the file selection controls self.__dir_picker_button = QPushButton() self.__dir_picker_button.setEnabled(True) self.__dir_picker_button.setText("Load data") self.__dir_picker_button.setIcon(self.style().standardIcon(QStyle.SP_DirIcon)) self.__dir_picker_button.setToolTip('Select the directory using the file explorer') self.__dir_picker_button.clicked.connect(self.__open_dir_picker) # Add the sync controls self.__sync_time_label = QLabel() self.__sync_time_label.setText('Enter the timecode (HH:mm:ss:zzz) : ') self.__sync_time_edit = QTimeEdit() self.__sync_time_edit.setDisplayFormat('HH:mm:ss:zzz') self.__sync_time_edit.setEnabled(False) self.__sync_time_button = QPushButton() self.__sync_time_button.setText('Sync data') self.__sync_time_button.setEnabled(False) self.__sync_time_button.clicked.connect(self.__sync_data) # Create the layout for the file controls dir_layout = QHBoxLayout() dir_layout.setContentsMargins(0, 0, 0, 0) dir_layout.addWidget(self.__dir_picker_button) dir_layout.addStretch(1) dir_layout.addWidget(self.__sync_time_label) dir_layout.addWidget(self.__sync_time_edit) dir_layout.addWidget(self.__sync_time_button) # Create the axis and their viewbox self.__x_axis_item = AxisItem('left') self.__y_axis_item = AxisItem('left') self.__z_axis_item = AxisItem('left') self.__x_axis_viewbox = ViewBox() self.__y_axis_viewbox = ViewBox() self.__z_axis_viewbox = ViewBox() # Create the widget which will display the data self.__graphic_view = GraphicsView(background="#ecf0f1") self.__graphic_layout = GraphicsLayout() self.__graphic_view.setCentralWidget(self.__graphic_layout) # Add the axis to the widget self.__graphic_layout.addItem(self.__x_axis_item, row=2, col=3, rowspan=1, colspan=1) self.__graphic_layout.addItem(self.__y_axis_item, row=2, col=2, rowspan=1, colspan=1) self.__graphic_layout.addItem(self.__z_axis_item, row=2, col=1, rowspan=1, colspan=1) self.__plot_item = PlotItem() self.__plot_item_viewbox = self.__plot_item.vb self.__graphic_layout.addItem(self.__plot_item, row=2, col=4, rowspan=1, colspan=1) self.__graphic_layout.scene().addItem(self.__x_axis_viewbox) self.__graphic_layout.scene().addItem(self.__y_axis_viewbox) self.__graphic_layout.scene().addItem(self.__z_axis_viewbox) self.__x_axis_item.linkToView(self.__x_axis_viewbox) self.__y_axis_item.linkToView(self.__y_axis_viewbox) self.__z_axis_item.linkToView(self.__z_axis_viewbox) self.__x_axis_viewbox.setXLink(self.__plot_item_viewbox) self.__y_axis_viewbox.setXLink(self.__plot_item_viewbox) self.__z_axis_viewbox.setXLink(self.__plot_item_viewbox) self.__plot_item_viewbox.sigResized.connect(self.__update_views) self.__x_axis_viewbox.enableAutoRange(axis=ViewBox.XAxis, enable=True) self.__y_axis_viewbox.enableAutoRange(axis=ViewBox.XAxis, enable=True) self.__z_axis_viewbox.enableAutoRange(axis=ViewBox.XAxis, enable=True) # Create the final layout self.__v_box = QVBoxLayout() self.__v_box.addLayout(dir_layout) self.__v_box.addWidget(self.__graphic_view) self.setLayout(self.__v_box) self.__restore_state() def __open_dir_picker(self): self.__shared_data.data_file_path = QFileDialog.getOpenFileUrl(self, 'Open the Hexoskin data directory', QDir.homePath())[0] if self.__shared_data.data_file_path is not None: try: self.__load_data() self.__add_selector_acc_gyr() self.__show_data('ACC_X', 'ACC_Y', 'ACC_Z') except FileNotFoundError: pass except UnicodeDecodeError: pass def __load_data(self): if self.__shared_data.data_file_path is not None: self.__shared_data.import_parameter() def __show_data(self, field1, field2, field3): if self.__shared_data.parameter is not None: # Generate the timecodes if needed if len(self.__shared_data.parameter['TIMECODE']) == 0: if self.__shared_data.sampling_rate is None: result = False while not result and result == 0: result = self.__show_sampling_rate_picker() self.__shared_data.add_timecode() self.__x_axis_viewbox.clear() self.__y_axis_viewbox.clear() self.__z_axis_viewbox.clear() # Show the 3 selected fields self.__x_axis_viewbox.addItem(PlotCurveItem(list(map(int, self.__shared_data.parameter.get(field1))), pen='#34495e')) self.__y_axis_viewbox.addItem(PlotCurveItem(list(map(int, self.__shared_data.parameter.get(field2))), pen='#9b59b6')) self.__z_axis_viewbox.addItem(PlotCurveItem(list(map(int, self.__shared_data.parameter.get(field3))), pen='#3498db')) self.__x_axis_item.setLabel(field1, color="#34495e") self.__y_axis_item.setLabel(field2, color="#9b59b6") self.__z_axis_item.setLabel(field3, color="#3498db") # Add the middle line and the bottom timecodes timecodes = self.__shared_data.parameter['TIMECODE'] middle = [0] * len(timecodes) self.__plot_item_viewbox.addItem(PlotCurveItem(middle, pen='#000000')) self.__plot_item.getAxis('bottom').setTicks( self.__generate_time_ticks(timecodes, self.__shared_data.sampling_rate)) # Enable the controls self.__sync_time_edit.setEnabled(True) self.__sync_time_button.setEnabled(True) self.__dir_picker_button.setEnabled(False) self.__update_views() def __update_views(self): self.__x_axis_viewbox.setGeometry(self.__plot_item_viewbox.sceneBoundingRect()) self.__y_axis_viewbox.setGeometry(self.__plot_item_viewbox.sceneBoundingRect()) self.__z_axis_viewbox.setGeometry(self.__plot_item_viewbox.sceneBoundingRect()) def __generate_time_ticks(self, timecodes, rate): ticks = list() steps = [rate * 30, rate * 15, rate * 5, rate] for step in steps: temp = list() i = step while i in range(len(timecodes)): temp.append((i, timecodes[i].strftime('%H:%M:%S:') + str(int(timecodes[i].microsecond / 1000)))) i += step ticks.append(temp) return ticks def __sync_data(self): self.__shared_data.data_sync = self.__sync_time_edit.text() self.__shared_data.update_sync.emit() def __show_sampling_rate_picker(self) -> bool: self.__shared_data.sampling_rate, result = QInputDialog.getInt(self, 'Set sampling rate value', 'Sampling rate') return result def __add_selector_acc_gyr(self): if 'GYR_X' in self.__shared_data.parameter.keys() or 'GYR_Y' in self.__shared_data.parameter.keys() \ or 'GYR_Z' in self.__shared_data.parameter.keys(): show_acc = QPushButton() show_acc.setText('Show Accelerometer Axis') show_acc.clicked.connect(self.__show_acc) show_gyr = QPushButton() show_gyr.setText('Show Gyroscope Axis') show_gyr.clicked.connect(self.__show_gyr) layout = QHBoxLayout() layout.addWidget(show_acc) layout.addWidget(show_gyr) layout.addStretch(1) self.__v_box.addLayout(layout) def __show_acc(self): self.__show_data('ACC_X', 'ACC_Y', 'ACC_Z') def __show_gyr(self): self.__show_data('GYR_X', 'GYR_Y', 'GYR_Z') def __restore_state(self): if self.__shared_data.parameter is not None: self.__add_selector_acc_gyr() self.__show_data('ACC_X', 'ACC_Y', 'ACC_Z') print('trigger reimport') if self.__shared_data.data_sync is not None: text_time = self.__shared_data.data_sync.split(':') time = QTime() time.setHMS(int(text_time[0]), int(text_time[1]), int(text_time[2]), int(text_time[3])) self.__sync_time_edit.setTime(time)