예제 #1
0
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()
예제 #2
0
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)))
예제 #3
0
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)
예제 #4
0
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)