class EventPropertiesDialog(QDialog): def __init__(self): super().__init__(GlobalAccess().get_main_window()) def exec_(self): self.init_ui() return super().exec_() def init_ui(self): self.setFixedWidth(500) self.setWindowTitle(_('Event properties')) self.setWindowIcon(QIcon(config.ICON)) self.setSizeGripEnabled(False) self.setModal(True) self.layout = QFormLayout(self) self.label_main_title = QLabel(_('Main title')) self.item_main_title = QLineEdit() self.layout.addRow(self.label_main_title, self.item_main_title) self.label_sub_title = QLabel(_('Sub title')) self.item_sub_title = QTextEdit() self.item_sub_title.setMaximumHeight(100) self.layout.addRow(self.label_sub_title, self.item_sub_title) self.label_start_date = QLabel(_('Start date')) self.item_start_date = QDateTimeEdit() self.item_start_date.setDisplayFormat('yyyy.MM.dd HH:mm:ss') self.layout.addRow(self.label_start_date, self.item_start_date) self.label_end_date = QLabel(_('End date')) # self.item_end_date = QCalendarWidget() self.item_end_date = QDateTimeEdit() self.item_end_date.setDisplayFormat('yyyy.MM.dd HH:mm:ss') self.layout.addRow(self.label_end_date, self.item_end_date) self.label_location = QLabel(_('Location')) self.item_location = QLineEdit() self.layout.addRow(self.label_location, self.item_location) self.label_type = QLabel(_('Event type')) self.item_type = AdvComboBox() self.item_type.addItems(RaceType.get_titles()) self.layout.addRow(self.label_type, self.item_type) self.label_relay_legs = QLabel(_('Relay legs')) self.item_relay_legs = QSpinBox() self.item_relay_legs.setMinimum(1) self.item_relay_legs.setMaximum(20) self.item_relay_legs.setValue(3) self.layout.addRow(self.label_relay_legs, self.item_relay_legs) self.item_type.currentTextChanged.connect(self.change_type) self.label_refery = QLabel(_('Chief referee')) self.item_refery = QLineEdit() self.layout.addRow(self.label_refery, self.item_refery) self.label_secretary = QLabel(_('Secretary')) self.item_secretary = QLineEdit() self.layout.addRow(self.label_secretary, self.item_secretary) self.label_url = QLabel(_('URL')) self.item_url = QLineEdit() self.layout.addRow(self.label_url, self.item_url) def cancel_changes(): self.close() def apply_changes(): try: self.apply_changes_impl() except Exception as e: logging.error(str(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_from_model() self.show() def change_type(self): flag = self.item_type.currentText() == RaceType.RELAY.get_title() self.label_relay_legs.setVisible(flag) self.item_relay_legs.setVisible(flag) def set_values_from_model(self): obj = race() self.item_main_title.setText(str(obj.data.title)) self.item_sub_title.setText(str(obj.data.description)) self.item_location.setText(str(obj.data.location)) self.item_url.setText(str(obj.data.url)) self.item_refery.setText(str(obj.data.chief_referee)) self.item_secretary.setText(str(obj.data.secretary)) self.item_start_date.setDateTime(obj.data.get_start_datetime()) self.item_end_date.setDateTime(obj.data.get_end_datetime()) self.item_type.setCurrentText(obj.data.race_type.get_title()) self.item_relay_legs.setValue(obj.data.relay_leg_count) self.change_type() def apply_changes_impl(self): obj = race() start_date = self.item_start_date.dateTime().toPython() end_date = self.item_end_date.dateTime().toPython() obj.data.title = self.item_main_title.text() obj.data.description = self.item_sub_title.toPlainText() obj.data.description = obj.data.description.replace('\n', '<br>\n') obj.data.location = self.item_location.text() obj.data.url = self.item_url.text() obj.data.chief_referee = self.item_refery.text() obj.data.secretary = self.item_secretary.text() obj.data.start_datetime = start_date obj.data.end_datetime = end_date t = RaceType.get_by_name(self.item_type.currentText()) if t is not None: obj.data.race_type = t obj.data.relay_leg_count = self.item_relay_legs.value() obj.set_setting('system_zero_time', (start_date.hour, start_date.minute, 0)) ResultCalculation(race()).process_results() GlobalAccess().get_main_window().set_title()
class StreamFieldsWidget(QDialog): """ A stream widget containing schema-specific properties. """ def __init__(self, parent, show_only_f142_stream: bool = False): super().__init__() self.setParent(parent) self.setLayout(QGridLayout()) self.setWindowModality(Qt.WindowModal) self.setModal(True) self._show_only_f142_stream = show_only_f142_stream self.minimum_spinbox_value = 0 self.maximum_spinbox_value = 100_000_000 self.advanced_options_enabled = False self.hs00_unimplemented_label = QLabel( "hs00 (Event histograms) has not yet been fully implemented.") self.schema_label = QLabel("Schema: ") self.schema_combo = DropDownList() self.schema_validator = SchemaSelectionValidator() self.schema_combo.setValidator(self.schema_validator) self.schema_validator.is_valid.connect( partial(validate_general_widget, self.schema_combo)) self.topic_label = QLabel("Topic: ") self.topic_line_edit = QLineEdit() self.topic_validator = NoEmptyStringValidator() self.topic_line_edit.setValidator(self.topic_validator) self.topic_validator.is_valid.connect( partial( validate_line_edit, self.topic_line_edit, tooltip_on_reject="Topic name can not be empty.", )) validate_line_edit(self.topic_line_edit, False) self.source_label = QLabel("Source: ") self.source_line_edit = QLineEdit() self.source_validator = NoEmptyStringValidator() self.source_line_edit.setValidator(self.source_validator) self.source_validator.is_valid.connect( partial( validate_line_edit, self.source_line_edit, tooltip_on_reject="Source name can not be empty.", )) validate_line_edit(self.source_line_edit, False) self.array_size_label = QLabel("Array size: ") self.array_size_spinbox = QSpinBox() self.array_size_spinbox.setMaximum(np.iinfo(np.int32).max) self.array_size_table = QTableWidget(1, 3) self.array_size_table.setHorizontalHeaderLabels(["x", "y", "z"]) self.array_size_table.setVerticalHeaderLabels([""]) table_height = self.array_size_table.sizeHintForRow( 0) + self.array_size_table.sizeHintForRow(1) self.array_size_table.setMaximumHeight(table_height) self.array_size_table.setFrameStyle(QFrame.NoFrame) self.array_size_table.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.array_size_table.resizeColumnsToContents() self.array_size_table.resizeRowsToContents() self.array_size_table.setItemDelegate( ValueDelegate(int, self.array_size_table)) self.type_label = QLabel("Type: ") self.type_combo = QComboBox() self.type_combo.addItems(F142_TYPES) self.type_combo.setCurrentText("double") self.value_units_edit = QLineEdit() self.value_units_label = QLabel("Value Units:") self.show_advanced_options_button = QPushButton( text="Show/hide advanced options") self.show_advanced_options_button.setCheckable(True) self.show_advanced_options_button.clicked.connect( self.advanced_options_button_clicked) self._set_up_f142_group_box() self._set_up_ev42_group_box() self.scalar_radio = QRadioButton(text=SCALAR) self.scalar_radio.clicked.connect(partial(self._show_array_size, False)) self.scalar_radio.setChecked(True) self.scalar_radio.clicked.emit() self.array_radio = QRadioButton(text=ARRAY) self.array_radio.clicked.connect(partial(self._show_array_size, True)) self.schema_combo.currentTextChanged.connect(self._schema_type_changed) if self._show_only_f142_stream: self.schema_combo.addItems([StreamModules.F142.value]) else: self.schema_combo.addItems([e.value for e in StreamModules]) self.ok_button = QPushButton("OK") self.ok_button.clicked.connect(self.parent().close) self.layout().addWidget(self.schema_label, 0, 0) self.layout().addWidget(self.schema_combo, 0, 1) self.layout().addWidget(self.topic_label, 1, 0) self.layout().addWidget(self.topic_line_edit, 1, 1) self.layout().addWidget(self.source_label, 2, 0) self.layout().addWidget(self.source_line_edit, 2, 1) self.layout().addWidget(self.value_units_label, 3, 0) self.layout().addWidget(self.value_units_edit, 3, 1) self.value_units_label.setVisible(False) self.value_units_edit.setVisible(False) self.layout().addWidget(self.type_label, 4, 0) self.layout().addWidget(self.type_combo, 4, 1) self.layout().addWidget(self.scalar_radio, 5, 0) self.layout().addWidget(self.array_radio, 5, 1) self.layout().addWidget(self.array_size_label, 6, 0) self.layout().addWidget(self.array_size_spinbox, 6, 1) self.layout().addWidget(self.array_size_table, 6, 1) self.layout().addWidget(self.hs00_unimplemented_label, 7, 0, 1, 2) # Spans both rows self.layout().addWidget(self.show_advanced_options_button, 8, 0, 1, 2) self.layout().addWidget(self.f142_advanced_group_box, 9, 0, 1, 2) self.layout().addWidget(self.ev42_advanced_group_box, 10, 0, 1, 2) self.layout().addWidget(self.ok_button, 11, 0, 1, 2) self._schema_type_changed(self.schema_combo.currentText()) self.parent().parent().field_name_edit.setVisible(False) def advanced_options_button_clicked(self): self._show_advanced_options( show=self.show_advanced_options_button.isChecked()) def _set_up_ev42_group_box(self): """ Sets up the UI for ev42 advanced options. """ self.ev42_advanced_group_box = QGroupBox( parent=self.show_advanced_options_button) self.ev42_advanced_group_box.setLayout(QFormLayout()) self.ev42_adc_pulse_debug_label = QLabel(ADC_PULSE_DEBUG) self.ev42_adc_pulse_debug_checkbox = QCheckBox() self.ev42_advanced_group_box.layout().addRow( self.ev42_adc_pulse_debug_label, self.ev42_adc_pulse_debug_checkbox) self.ev42_chunk_size_spinner = ( self.create_label_and_spinbox_for_advanced_option( CHUNK_SIZE, self.ev42_advanced_group_box)) self.ev42_cue_interval_spinner = ( self.create_label_and_spinbox_for_advanced_option( CUE_INTERVAL, self.ev42_advanced_group_box)) def create_label_and_spinbox_for_advanced_option(self, nexus_string: str, group_box: QGroupBox): """ Creates a SpinBox with a label and adds them to GroupBox then returns the SpinBox. :param nexus_string: The nexus string label for the SpinBox. :param group_box: The GroupBox that the label and SpinBox should be added to. :return: The newly created SpinBox. """ label = QLabel(nexus_string) spinner = QSpinBox() spinner.setRange(self.minimum_spinbox_value, self.maximum_spinbox_value) group_box.layout().addRow(label, spinner) return spinner def _set_up_f142_group_box(self): """ Sets up the UI for the f142 advanced options. """ self.f142_advanced_group_box = QGroupBox( parent=self.show_advanced_options_button) self.f142_advanced_group_box.setLayout(QFormLayout()) self.f142_chunk_size_spinner = ( self.create_label_and_spinbox_for_advanced_option( CHUNK_SIZE, self.f142_advanced_group_box)) self.f142_cue_interval_spinner = ( self.create_label_and_spinbox_for_advanced_option( CUE_INTERVAL, self.f142_advanced_group_box)) def _show_advanced_options(self, show): schema = self.schema_combo.currentText() if schema == WriterModules.F142.value: self.f142_advanced_group_box.setVisible(show) elif schema == WriterModules.EV42.value: self.ev42_advanced_group_box.setVisible(show) self.advanced_options_enabled = show def _show_array_size(self, show: bool): self.array_size_spinbox.setVisible(show) self.array_size_label.setVisible(show) def _schema_type_changed(self, schema: str): self.parent().setWindowTitle(f"Editing {schema} stream field") self.hs00_unimplemented_label.setVisible(False) self.f142_advanced_group_box.setVisible(False) self.ev42_advanced_group_box.setVisible(False) self.show_advanced_options_button.setVisible(False) self.show_advanced_options_button.setChecked(False) self.value_units_label.setVisible(False) self.value_units_edit.setVisible(False) self.array_size_table.setVisible(False) if schema == WriterModules.F142.value: self.value_units_label.setVisible(True) self.value_units_edit.setVisible(True) self._set_edits_visible(True, True) self.show_advanced_options_button.setVisible(True) self.f142_advanced_group_box.setVisible(False) elif schema == WriterModules.EV42.value: self._set_edits_visible(True, False) self.show_advanced_options_button.setVisible(True) self.ev42_advanced_group_box.setVisible(False) elif schema == WriterModules.ADAR.value: self._set_edits_visible(True, False) self._show_array_size_table(True) elif schema == WriterModules.HS00.value: self._set_edits_visible(True, False) self.hs00_unimplemented_label.setVisible(True) elif schema == WriterModules.NS10.value: self._set_edits_visible(True, False, "nicos/<device>/<parameter>") elif (schema == WriterModules.TDCTIME.value or schema == WriterModules.SENV.value): self._set_edits_visible(True, False) def _show_array_size_table(self, show: bool): self.array_size_label.setVisible(show) self.array_size_table.setVisible(show) def _set_edits_visible(self, source: bool, type: bool, source_hint=None): self.source_label.setVisible(source) self.source_line_edit.setVisible(source) self.type_label.setVisible(type) self.type_combo.setVisible(type) self.array_radio.setVisible(type) self.scalar_radio.setVisible(type) if source_hint: self.source_line_edit.setPlaceholderText(source_hint) else: self.source_line_edit.setPlaceholderText("") def get_stream_module(self, parent) -> StreamModule: """ Create the stream module :return: The created stream module """ source = self.source_line_edit.text() topic = self.topic_line_edit.text() stream: StreamModule = None type = self.type_combo.currentText() current_schema = self.schema_combo.currentText() if current_schema == WriterModules.F142.value: value_units = self.value_units_edit.text() array_size = self.array_size_spinbox.value() stream = F142Stream( parent_node=parent, source=source, topic=topic, type=type, value_units=value_units, array_size=array_size, ) if array_size: stream.array_size = array_size if self.advanced_options_enabled: self.record_advanced_f142_values(stream) elif current_schema == WriterModules.ADAR.value: array_size = [] for i in range(self.array_size_table.columnCount()): table_value = self.array_size_table.item(0, i) if table_value: array_size.append(int(table_value.text())) stream = ADARStream(parent_node=parent, source=source, topic=topic) stream.array_size = array_size elif current_schema == WriterModules.EV42.value: stream = EV42Stream(parent_node=parent, source=source, topic=topic) if self.advanced_options_enabled: self.record_advanced_ev42_values(stream) elif current_schema == WriterModules.NS10.value: stream = NS10Stream(parent_node=parent, source=source, topic=topic) elif current_schema == WriterModules.SENV.value: stream = SENVStream(parent_node=parent, source=source, topic=topic) elif current_schema == WriterModules.HS00.value: stream = HS00Stream( # type: ignore parent=parent, source=source, topic=topic, data_type=NotImplemented, edge_type=NotImplemented, error_type=NotImplemented, shape=[], ) elif current_schema == WriterModules.TDCTIME.value: stream = TDCTStream(parent_node=parent, source=source, topic=topic) return stream def record_advanced_f142_values(self, stream: F142Stream): """ Save the advanced f142 properties to the stream data object. :param stream: The stream data object to be modified. """ stream.chunk_size = self.f142_chunk_size_spinner.value() stream.cue_interval = self.f142_cue_interval_spinner.value() def record_advanced_ev42_values(self, stream: EV42Stream): """ Save the advanced ev42 properties to the stream data object. :param stream: The stream data object to be modified. """ stream.adc_pulse_debug = self.ev42_adc_pulse_debug_checkbox.isChecked() stream.chunk_size = self.ev42_chunk_size_spinner.value() stream.cue_interval = self.ev42_cue_interval_spinner.value() def fill_in_existing_ev42_fields(self, field: EV42Stream): """ Fill in specific existing ev42 fields into the new UI field. :param field: The stream group """ if check_if_advanced_options_should_be_enabled( [field.adc_pulse_debug, field.chunk_size, field.cue_interval]): self._show_advanced_options(True) self._fill_existing_advanced_ev42_fields(field) def _fill_existing_advanced_ev42_fields(self, field: EV42Stream): """ Fill the fields in the interface with the existing ev42 stream data. :param field: The ev42 stream data object. """ self.ev42_adc_pulse_debug_checkbox.setChecked(field.adc_pulse_debug) self.ev42_chunk_size_spinner.setValue(field.chunk_size) self.ev42_cue_interval_spinner.setValue(field.cue_interval) def fill_in_existing_f142_fields(self, field: F142Stream): """ Fill in specific existing f142 fields into the new UI field. :param field: The stream group """ self.type_combo.setCurrentText(field.type) if field.array_size is not None: self.array_radio.setChecked(True) self.scalar_radio.setChecked(False) self.array_size_spinbox.setValue(field.array_size) else: self.array_radio.setChecked(False) self.scalar_radio.setChecked(True) if field.value_units is not None: self.value_units_edit.setText(field.value_units) if check_if_advanced_options_should_be_enabled( [field.chunk_size, field.cue_interval]): self._show_advanced_options(True) self._fill_existing_advanced_f142_fields(field) def _fill_existing_advanced_f142_fields(self, field: F142Stream): """ Fill the advanced fields in the interface with the existing f142 stream data. :param field: The f412 stream data object. """ self.f142_chunk_size_spinner.setValue(field.chunk_size) self.f142_cue_interval_spinner.setValue(field.cue_interval) def update_existing_stream_info(self, field): """ Fill in stream fields and properties into the new UI field. :param field: The stream group """ if isinstance(field, Group): field = field.children[0] if hasattr(field, "parent_node") and isinstance( field.parent_node, Group): self.schema_validator.set_group(field.parent_node) else: self.schema_validator.set_group(None) schema = field.writer_module self.schema_combo.setCurrentText(schema) self.schema_validator.validate(schema, 0) self.topic_line_edit.setText(field.topic) self.topic_validator.validate(field.topic, 0) self.source_line_edit.setText(field.source) self.source_validator.validate(field.source, 0) if schema == WriterModules.F142.value: self.fill_in_existing_f142_fields(field) elif schema == WriterModules.EV42.value: self.fill_in_existing_ev42_fields(field) elif schema == WriterModules.ADAR.value: for i, val in enumerate(field.array_size): self.array_size_table.setItem(0, i, QTableWidgetItem(str(val)))
class MixedDistributionChart(QDialog): def __init__(self, parent=None, show_mode=True, toolbar=False, use_animation=False): flags = Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint super().__init__(parent=parent, f=flags) self.setWindowTitle(self.tr("Mixed Distribution Chart")) self.figure = plt.figure(figsize=(4, 3)) self.axes = self.figure.subplots() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.main_layout = QGridLayout(self) self.main_layout.addWidget(self.toolbar, 0, 0, 1, 2) self.main_layout.addWidget(self.canvas, 1, 0, 1, 2) if not toolbar: self.toolbar.hide() self.supported_scales = [("log-linear", self.tr("Log-linear")), ("log", self.tr("Log")), ("phi", self.tr("φ")), ("linear", self.tr("Linear"))] self.scale_label = QLabel(self.tr("Scale")) self.scale_combo_box = QComboBox() self.scale_combo_box.addItems([name for key, name in self.supported_scales]) self.scale_combo_box.currentIndexChanged.connect(self.update_chart) self.main_layout.addWidget(self.scale_label, 2, 0) self.main_layout.addWidget(self.scale_combo_box, 2, 1) self.interval_label = QLabel(self.tr("Interval [ms]")) self.interval_input = QSpinBox() self.interval_input.setRange(0, 10000) self.interval_input.setValue(30) self.interval_input.valueChanged.connect(self.update_animation) self.main_layout.addWidget(self.interval_label, 3, 0) self.main_layout.addWidget(self.interval_input, 3, 1) self.repeat_check_box = QCheckBox(self.tr("Repeat")) self.repeat_check_box.setChecked(False) self.repeat_check_box.stateChanged.connect(self.update_animation) self.save_button = QPushButton(self.tr("Save")) self.save_button.clicked.connect(self.save_animation) self.main_layout.addWidget(self.repeat_check_box, 4, 0) self.main_layout.addWidget(self.save_button, 4, 1) self.show_mode = show_mode self.animation = None self.last_model = None self.last_result = None if not use_animation: self.interval_label.setVisible(False) self.interval_input.setVisible(False) self.repeat_check_box.setVisible(False) self.save_button.setVisible(False) self.normal_msg = QMessageBox(parent=self) self.file_dialog = QFileDialog(parent=self) @property def scale(self) -> str: index = self.scale_combo_box.currentIndex() key, name = self.supported_scales[index] return key @property def transfer(self) -> typing.Callable: if self.scale == "log-linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) elif self.scale == "log": return lambda classes_φ: np.log(convert_φ_to_μm(classes_φ)) elif self.scale == "phi": return lambda classes_φ: classes_φ elif self.scale == "linear": return lambda classes_φ: convert_φ_to_μm(classes_φ) @property def xlabel(self) -> str: if self.scale == "log-linear": return self.tr("Grain-size [μm]") elif self.scale == "log": return self.tr("Ln(grain-size in μm)") elif self.scale == "phi": return self.tr("Grain-size [φ]") elif self.scale == "linear": return self.tr("Grain-size [μm]") @property def ylabel(self) -> str: return self.tr("Frequency") @property def xlog(self) -> bool: if self.scale == "log-linear": return True else: return False @property def interval(self) -> float: return self.interval_input.value() @property def repeat(self) -> bool: return self.repeat_check_box.isChecked() def show_demo(self): demo_model = get_demo_view_model() self.show_model(demo_model) def update_chart(self): if self.last_model is not None: self.show_model(self.last_model) elif self.last_result is not None: self.show_result(self.last_result) def update_animation(self): if self.last_result is not None: self.show_result(self.last_result) def show_model(self, model: SSUViewModel, quick=False): if self.animation is not None: self.animation._stop() self.animation = None if not quick: self.last_result = None self.last_model = model self.interval_label.setEnabled(False) self.interval_input.setEnabled(False) self.repeat_check_box.setEnabled(False) self.save_button.setEnabled(False) self.axes.clear() x = self.transfer(model.classes_φ) if self.xlog: self.axes.set_xscale("log") self.axes.set_title(model.title) self.axes.set_xlabel(self.xlabel) self.axes.set_ylabel(self.ylabel) self.target = self.axes.plot(x, model.target, c="#ffffff00", marker=".", ms=8, mfc="black", mew=0.0, label=self.tr("Target"))[0] # scatter can not be modified from the tool bar # self.target = self.axes.scatter(x, model.target, c="black", s=1) self.axes.set_xlim(x[0], x[-1]) self.axes.set_ylim(0.0, round(np.max(model.target)*1.2, 2)) self.mixed = self.axes.plot(x, model.mixed, c="black", label=self.tr("Mixed"))[0] self.components = [self.axes.plot(x, distribution*fraction, c=plt.get_cmap()(i), label=model.component_prefix+str(i+1))[0] for i, (distribution, fraction) in enumerate(zip(model.distributions, model.fractions))] if self.show_mode: modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] colors = [plt.get_cmap()(i) for i in range(model.n_components)] self.vlines = self.axes.vlines(modes, 0.0, round(np.max(model.target)*1.2, 2), colors=colors) if model.n_components < 6: self.axes.legend(loc="upper left") self.figure.tight_layout() self.canvas.draw() else: self.mixed.set_ydata(model.mixed) for comp, distribution, fraction in zip(self.components, model.distributions, model.fractions): comp.set_ydata(distribution*fraction) if self.show_mode: modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] self.vlines.set_offsets(modes) self.canvas.draw() def show_result(self, result: SSUResult): if self.animation is not None: self.animation._stop() self.animation = None self.last_model = None self.last_result = result self.interval_label.setEnabled(True) self.interval_input.setEnabled(True) self.repeat_check_box.setEnabled(True) self.save_button.setEnabled(True) models = iter(result.view_models) first = next(models) x = self.transfer(first.classes_φ) self.axes.cla() if self.xlog: self.axes.set_xscale("log") self.axes.set_title(first.title) self.axes.set_xlabel(self.xlabel) self.axes.set_ylabel(self.ylabel) self.target = self.axes.plot(x, first.target, c="#ffffff00", marker=".", ms=8, mfc="black", mew=0.0)[0] self.axes.set_xlim(x[0], x[-1]) self.axes.set_ylim(0.0, round(np.max(first.target)*1.2, 2)) self.figure.tight_layout() # self.canvas.draw() colors = [plt.get_cmap()(i) for i in range(first.n_components)] def init(): model = first self.mixed = self.axes.plot(x, model.mixed, c="black")[0] self.components = [self.axes.plot(x, distribution*fraction, c=plt.get_cmap()(i))[0] for i, (distribution, fraction) in enumerate(zip(model.distributions, model.fractions))] if self.show_mode: modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] self.vlines = self.axes.vlines(modes, 0.0, round(np.max(model.target)*1.2, 2), colors=colors) return self.mixed, self.vlines, *self.components def animate(current): model = current self.mixed.set_ydata(current.mixed) for line, distribution, fraction in zip(self.components, model.distributions, model.fractions): line.set_ydata(distribution*fraction) if self.show_mode: self.vlines.remove() modes = [self.transfer(model.classes_φ[np.unravel_index(np.argmax(distribution), distribution.shape)]) for distribution in model.distributions] self.vlines = self.axes.vlines(modes, 0.0, round(np.max(model.target)*1.2, 2), colors=colors) return self.mixed, self.vlines, *self.components self.animation = FuncAnimation(self.figure, animate, frames=models, init_func=init, interval=self.interval, blit=True, repeat=self.repeat, repeat_delay=3.0, save_count=result.n_iterations) def save_animation(self): if self.last_result is not None: filename, format_str = self.file_dialog.getSaveFileName(self, self.tr("Save the animation of this SSU result"), None, self.tr("MPEG-4 Video File (*.mp4);;Graphics Interchange Format (*.gif)")) if filename is None or filename == "": return progress = QProgressDialog(self) progress.setRange(0, 100) progress.setLabelText(self.tr("Saving Animation [{0} Frames]").format(self.last_result.n_iterations)) canceled = False def save_callback(i, n): if progress.wasCanceled(): nonlocal canceled canceled = True raise StopIteration() progress.setValue((i+1)/n*100) QCoreApplication.processEvents() self.show_result(self.last_result) # plt.rcParams["savefig.dpi"] = 120.0 if "*.gif" in format_str: if not ImageMagickWriter.isAvailable(): self.normal_msg.setWindowTitle(self.tr("Error")) self.normal_msg.setText(self.tr("ImageMagick is not installed, please download and install it from its offical website (https://imagemagick.org/index.php).")) self.normal_msg.exec_() else: self.animation.save(filename, writer="imagemagick", fps=30, progress_callback=save_callback) elif "*.mp4" in format_str: if not FFMpegWriter.isAvailable(): self.normal_msg.setWindowTitle(self.tr("Error")) self.normal_msg.setText(self.tr("FFMpeg is not installed, please download and install it from its offical website (https://ffmpeg.org/).")) self.normal_msg.exec_() else: self.animation.save(filename, writer="ffmpeg", fps=30, progress_callback=save_callback) # plt.rcParams["savefig.dpi"] = 300.0 if not canceled: progress.setValue(100)
class StreamFieldsWidget(QDialog): """ A stream widget containing schema-specific properties. """ def __init__(self, parent): super().__init__() self.setParent(parent) self.setLayout(QGridLayout()) self.setWindowModality(Qt.WindowModal) self.setModal(True) self.minimum_spinbox_value = 0 self.maximum_spinbox_value = 100_000_000 self.advanced_options_enabled = False self.hs00_unimplemented_label = QLabel( "hs00 (Event histograms) has not yet been fully implemented.") self.schema_label = QLabel("Schema: ") self.schema_combo = QComboBox() self.topic_label = QLabel("Topic: ") self.topic_line_edit = QLineEdit() self.topic_line_edit.setPlaceholderText( "[broker][:port, default=9092]/topic") self.source_label = QLabel("Source: ") self.source_line_edit = QLineEdit() self.array_size_label = QLabel("Array size") self.array_size_spinbox = QSpinBox() self.array_size_spinbox.setMaximum(np.iinfo(np.int32).max) self.type_label = QLabel("Type: ") self.type_combo = QComboBox() self.type_combo.addItems(F142_TYPES) self.type_combo.setCurrentText("double") self.value_units_edit = QLineEdit() self.value_units_label = QLabel("Value Units:") self.show_advanced_options_button = QPushButton( text="Show/hide advanced options") self.show_advanced_options_button.setCheckable(True) self.show_advanced_options_button.clicked.connect( self.advanced_options_button_clicked) self._set_up_f142_group_box() self._set_up_ev42_group_box() self.scalar_radio = QRadioButton(text="Scalar") self.scalar_radio.clicked.connect(partial(self._show_array_size, False)) self.scalar_radio.setChecked(True) self.scalar_radio.clicked.emit() self.array_radio = QRadioButton(text="Array") self.array_radio.clicked.connect(partial(self._show_array_size, True)) self.schema_combo.currentTextChanged.connect(self._schema_type_changed) self.schema_combo.addItems([e.value for e in WriterModules]) self.ok_button = QPushButton("OK") self.ok_button.clicked.connect(self.parent().close) self.layout().addWidget(self.schema_label, 0, 0) self.layout().addWidget(self.schema_combo, 0, 1) self.layout().addWidget(self.topic_label, 1, 0) self.layout().addWidget(self.topic_line_edit, 1, 1) self.layout().addWidget(self.source_label, 2, 0) self.layout().addWidget(self.source_line_edit, 2, 1) self.layout().addWidget(self.value_units_label, 3, 0) self.layout().addWidget(self.value_units_edit, 3, 1) self.value_units_label.setVisible(False) self.value_units_edit.setVisible(False) self.layout().addWidget(self.type_label, 4, 0) self.layout().addWidget(self.type_combo, 4, 1) self.layout().addWidget(self.scalar_radio, 5, 0) self.layout().addWidget(self.array_radio, 5, 1) self.layout().addWidget(self.array_size_label, 6, 0) self.layout().addWidget(self.array_size_spinbox, 6, 1) self.layout().addWidget(self.hs00_unimplemented_label, 7, 0, 1, 2) # Spans both rows self.layout().addWidget(self.show_advanced_options_button, 8, 0, 1, 2) self.layout().addWidget(self.f142_advanced_group_box, 9, 0, 1, 2) self.layout().addWidget(self.ev42_advanced_group_box, 10, 0, 1, 2) self.layout().addWidget(self.ok_button, 11, 0, 1, 2) self._schema_type_changed(self.schema_combo.currentText()) def advanced_options_button_clicked(self): self._show_advanced_options( show=self.show_advanced_options_button.isChecked()) def _set_up_ev42_group_box(self): """ Sets up the UI for ev42 advanced options. """ self.ev42_nexus_elements = [ NEXUS_INDICES_INDEX_EVERY_MB, NEXUS_INDICES_INDEX_EVERY_KB, NEXUS_CHUNK_CHUNK_MB, NEXUS_CHUNK_CHUNK_KB, ] self.ev42_nexus_to_spinner_ui_element = {} self.ev42_advanced_group_box = QGroupBox( parent=self.show_advanced_options_button) self.ev42_advanced_group_box.setLayout(QFormLayout()) self.ev42_adc_pulse_debug_label = QLabel(ADC_PULSE_DEBUG) self.ev42_adc_pulse_debug_checkbox = QCheckBox() self.ev42_advanced_group_box.layout().addRow( self.ev42_adc_pulse_debug_label, self.ev42_adc_pulse_debug_checkbox) self.add_labels_and_spinboxes_for_advanced_options( self.ev42_nexus_elements, self.ev42_advanced_group_box, self.ev42_nexus_to_spinner_ui_element, ) def add_labels_and_spinboxes_for_advanced_options(self, elements, group_box, nexus_to_spinner): for nexus_string in elements: label = QLabel(nexus_string) spinner = QSpinBox() spinner.setRange(self.minimum_spinbox_value, self.maximum_spinbox_value) group_box.layout().addRow(label, spinner) nexus_to_spinner[nexus_string] = spinner def _set_up_f142_group_box(self): """ Sets up the UI for the f142 advanced options. """ self.f142_advanced_group_box = QGroupBox( parent=self.show_advanced_options_button) self.f142_advanced_group_box.setLayout(QFormLayout()) self.f142_nexus_to_spinner_ui_element = {} self.f142_nexus_elements = [ NEXUS_INDICES_INDEX_EVERY_MB, NEXUS_INDICES_INDEX_EVERY_KB, STORE_LATEST_INTO, ] self.add_labels_and_spinboxes_for_advanced_options( self.f142_nexus_elements, self.f142_advanced_group_box, self.f142_nexus_to_spinner_ui_element, ) def _show_advanced_options(self, show): schema = self.schema_combo.currentText() if schema == WriterModules.F142.value: self.f142_advanced_group_box.setVisible(show) elif schema == WriterModules.EV42.value: self.ev42_advanced_group_box.setVisible(show) self.advanced_options_enabled = show def _show_array_size(self, show: bool): self.array_size_spinbox.setVisible(show) self.array_size_label.setVisible(show) def _schema_type_changed(self, schema: str): self.parent().setWindowTitle(f"Editing {schema} stream field") self.hs00_unimplemented_label.setVisible(False) self.f142_advanced_group_box.setVisible(False) self.ev42_advanced_group_box.setVisible(False) self.show_advanced_options_button.setVisible(False) self.show_advanced_options_button.setChecked(False) self.value_units_label.setVisible(False) self.value_units_edit.setVisible(False) if schema == WriterModules.F142.value: self.value_units_label.setVisible(True) self.value_units_edit.setVisible(True) self._set_edits_visible(True, True) self.show_advanced_options_button.setVisible(True) self.f142_advanced_group_box.setVisible(False) elif schema == WriterModules.EV42.value: self._set_edits_visible(True, False) self.show_advanced_options_button.setVisible(True) self.ev42_advanced_group_box.setVisible(False) elif schema == WriterModules.HS00.value: self._set_edits_visible(True, False) self.hs00_unimplemented_label.setVisible(True) elif schema == WriterModules.NS10.value: self._set_edits_visible(True, False, "nicos/<device>/<parameter>") elif (schema == WriterModules.TDCTIME.value or schema == WriterModules.SENV.value): self._set_edits_visible(True, False) def _set_edits_visible(self, source: bool, type: bool, source_hint=None): self.source_label.setVisible(source) self.source_line_edit.setVisible(source) self.type_label.setVisible(type) self.type_combo.setVisible(type) self.array_radio.setVisible(type) self.scalar_radio.setVisible(type) if source_hint: self.source_line_edit.setPlaceholderText(source_hint) else: self.source_line_edit.setPlaceholderText("") def get_stream_group(self) -> h5py.Group: """ Create the stream group with a temporary in-memory HDF5 file. :return: The created HDF group. """ temp_file = create_temporary_in_memory_file() group = temp_file.create_group("children") group.create_dataset(name="type", dtype=STRING_DTYPE, data="stream") stream_group = group.create_group( self.parent().parent().field_name_edit.text()) stream_group.attrs[CommonAttrs.NX_CLASS] = CommonAttrs.NC_STREAM stream_group.create_dataset(name="topic", dtype=STRING_DTYPE, data=self.topic_line_edit.text()) stream_group.create_dataset( name="writer_module", dtype=STRING_DTYPE, data=self.schema_combo.currentText(), ) schema = self.schema_combo.currentText() stream_group.create_dataset("source", dtype=STRING_DTYPE, data=self.source_line_edit.text()) if schema == WriterModules.F142.value: self._create_f142_fields(stream_group) elif schema == WriterModules.EV42.value: self._create_ev42_fields(stream_group) return stream_group def _create_ev42_fields(self, stream_group: h5py.Group): """ Create ev42 fields in the given group if advanced options are specified. :param stream_group: The group to apply fields to. """ if self.advanced_options_enabled: if self.ev42_adc_pulse_debug_checkbox.isChecked(): stream_group.create_dataset( ADC_PULSE_DEBUG, dtype=bool, data=self.ev42_adc_pulse_debug_checkbox.isChecked(), ) self._create_dataset_from_spinner( stream_group, self.ev42_nexus_to_spinner_ui_element) def _create_f142_fields(self, stream_group: h5py.Group): """ Create f142 fields in the given group if advanced options are specified. :param stream_group: The group to apply fields to. """ stream_group.create_dataset("type", dtype=STRING_DTYPE, data=self.type_combo.currentText()) if self.array_radio.isChecked(): stream_group.create_dataset("array_size", data=self.array_size_spinbox.value()) if self.value_units_edit.text(): stream_group.create_dataset("value_units", data=self.value_units_edit.text()) if self.advanced_options_enabled: self._create_dataset_from_spinner( stream_group, self.f142_nexus_to_spinner_ui_element) @staticmethod def _create_dataset_from_spinner(stream_group: h5py.Group, nexus_to_spinner_dict: Dict[str, QSpinBox]): for (nexus_string, ui_element) in nexus_to_spinner_dict.items(): if ui_element.value() > 0: stream_group.create_dataset(nexus_string, dtype=int, data=ui_element.value()) def fill_in_existing_ev42_fields(self, field: h5py.Group): """ Fill in specific existing ev42 fields into the new UI field. :param field: The stream group :param new_ui_field: The new UI field to be filled in """ all_ev42_elements = list(self.ev42_nexus_elements) all_ev42_elements.append(ADC_PULSE_DEBUG) if check_if_advanced_options_should_be_enabled(all_ev42_elements, field): self._show_advanced_options(True) if ADC_PULSE_DEBUG in field.keys(): self.ev42_adc_pulse_debug_checkbox.setChecked( bool(field[ADC_PULSE_DEBUG][()])) fill_in_advanced_options( self.ev42_nexus_to_spinner_ui_element.items(), field) def fill_in_existing_f142_fields(self, field: h5py.Group): """ Fill in specific existing f142 fields into the new UI field. :param field: The stream group :param new_ui_field: The new UI field to be filled in """ self.type_combo.setCurrentText(field["type"][()]) if "array_size" in field.keys(): self.array_radio.setChecked(True) self.scalar_radio.setChecked(False) self.array_size_spinbox.setValue(field["array_size"][()]) else: self.array_radio.setChecked(False) self.scalar_radio.setChecked(True) if check_if_advanced_options_should_be_enabled( self.f142_nexus_elements, field): self._show_advanced_options(True) fill_in_advanced_options( self.f142_nexus_to_spinner_ui_element.items(), field) def update_existing_stream_info(self, field: h5py.Group): """ Fill in stream fields and properties into the new UI field. :param field: The stream group :param new_ui_field: The new UI field to be filled in """ schema = field["writer_module"][()] self.schema_combo.setCurrentText(str(schema)) self.topic_line_edit.setText(str(field["topic"][()])) self.source_line_edit.setText(str(field["source"][()])) if schema == WriterModules.F142.value: self.fill_in_existing_f142_fields(field) elif schema == WriterModules.EV42.value: self.fill_in_existing_ev42_fields(field)