class Config(SignalNode.Config): """Config widget displayed for LSLInput.""" def __init__(self, parent=None): super().__init__(parent=parent) self.smoothing_factor = QDoubleSpinBox() self.smoothing_factor.setMinimum(0) self.smoothing_factor.setMaximum(1) self.smoothing_factor.setSingleStep(0.1) self.smoothing_factor.setPrefix("x") self.smoothing_factor.valueChanged.connect(self.updateModel) self.method = QComboBox() self.method.addItem("Rectification") self.method.addItem("Fourier Transform") self.method.addItem("Hilbert Transform") self.method.addItem("cFIR") self.method.currentTextChanged.connect(self.updateModel) self.smoother_type = QComboBox() for name in EnvelopeDetector.smoother_name_to_type: self.smoother_type.addItem(name) self.smoother_type.currentTextChanged.connect(self.updateModel) layout = QFormLayout() self.setLayout(layout) layout.addRow("Smoothing factor", self.smoothing_factor) layout.addRow("Method", self.method) layout.addRow("Smoother type", self.smoother_type) def updateModel(self): n = self.node() if n is None: return smoothing_factor = self.smoothing_factor.value() method = self.method.currentText() smoother_type = n.smoother_name_to_type[self.smoother_type.currentText()] n.setSmoothingFactor(smoothing_factor) n.setMethod(method) n.setSmootherType(smoother_type) def updateView(self): n = self.node() if n is None: return self.smoothing_factor.blockSignals(True) self.method.blockSignals(True) self.smoother_type.blockSignals(True) self.smoothing_factor.setValue(n.smoothingFactor()) self.method.setCurrentText(n.method()) self.smoother_type.setCurrentText(n.smoother_type_to_name[n.smootherType()]) self.smoothing_factor.blockSignals(False) self.method.blockSignals(False) self.smoother_type.blockSignals(False)
def affichNote(self): cpt = 0 self.ui.tWTableNoteModif.clear() self.ui.tWTableNoteModif.setColumnCount(3) academie = self.ui.cBAcademie.currentIndex() etabliss = self.ui.cBEtablissement.currentIndex() cla = self.ui.cBClasse.currentIndex() dicoClasse = self.mesDatas["academies"][academie]["etablissements"][etabliss]["classes"][cla] for eleve in dicoClasse["eleves"]: for matiere in eleve["matieres"]: mat = self.ui.cBMatiereModifNote.currentText() if matiere["nom"] == mat: dicoDevoir = matiere["notes"] for devoir in dicoDevoir: nomDevoir=self.ui.cBListeDevoir.currentText() if devoir["nom"]==nomDevoir : nomE = eleve["nom"] prenomE = eleve["prenom"] valNote=devoir["valeur"] coefNote=devoir["coef"] self.ui.dSBCoeffModifNote.setValue(coefNote) self.ui.tWTableNoteModif.setRowCount(cpt + 1) itemE = QTableWidgetItem(nomE) itemPE = QTableWidgetItem(prenomE) self.ui.tWTableNoteModif.setItem(cpt, 0, itemE) self.ui.tWTableNoteModif.setItem(cpt, 1, itemPE) spinB = QDoubleSpinBox() self.ui.tWTableNoteModif.setCellWidget(cpt, 2, spinB) spinB.setValue(valNote) cpt += 1 self.ui.tWTableNoteModif.setHorizontalHeaderLabels(['Nom', 'Prénom', 'Note'])
def testFloatSignal(self): foo1 = QDoubleSpinBox() foo2 = QDoubleSpinBox() foo1.valueChanged[float].connect(foo2.setValue) foo2.valueChanged[float].connect(foo1.setValue) foo1.setValue(0.42) self.assertEqual(foo1.value(), foo2.value())
class GaussianFuzzierSetting(QFrame): def __init__(self): super().__init__() layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.mean = QDoubleSpinBox() self.mean.setRange(-100, 100) self.mean.setStatusTip("The mean (mu) value for Gaussian function.") self.sd = QDoubleSpinBox() self.sd.setDecimals(3) self.sd.setValue(5) self.sd.setMinimum(0.1) self.sd.setStatusTip("The standard deviation (sigma) value for " "Gaussian function.") self.ascending = QCheckBox() self.ascending.setIcon(QIcon(':/icons/ascending_icon.png')) self.ascending.setStatusTip("Make the fuzzier strictly ascending.") self.descending = QCheckBox() self.descending.setIcon(QIcon(':/icons/descending_icon.png')) self.descending.setStatusTip("Make the fuzzier strictly descending.") layout.addWidget(QLabel("Mean")) layout.addWidget(self.mean, 1) layout.addWidget(QLabel("Standard Deviation")) layout.addWidget(self.sd, 1) layout.addWidget(self.ascending) layout.addWidget(self.descending) def get_values(self): return (self.mean.value(), self.sd.value(), self.ascending.isChecked(), self.descending.isChecked())
def generate_numeric_input(self, maximum, minimum, increment, current): sb = QDoubleSpinBox(self) sb.setMaximum(maximum) sb.setMinimum(minimum) sb.setSingleStep(increment) sb.setValue(current) return sb
def _make_double_spinbox(default, min_value, max_value, single_step): spinbox = QDoubleSpinBox() spinbox.setSingleStep(single_step) spinbox.setMaximum(max_value) spinbox.setMinimum(min_value) spinbox.setValue(default) return spinbox
def change_n_components(self, n_components: int): for widget in self.control_widgets: self.control_layout.removeWidget(widget) widget.hide() self.control_widgets.clear() self.input_widgets.clear() widgets = [] slider_range = (0, 1000) input_widgets = [] mean_range = (-5, 15) std_range = (0.0, 10) weight_range = (0, 10) names = [self.tr("Mean"), self.tr("STD"), self.tr("Weight")] ranges = [mean_range, std_range, weight_range] slider_values = [500, 100, 100] input_values = [0.0, 1.0, 1.0] for i in range(n_components): group = QGroupBox(f"C{i+1}") group.setMinimumWidth(200) group_layout = QGridLayout(group) inputs = [] for j, (name, range_, slider_value, input_value) in enumerate( zip(names, ranges, slider_values, input_values)): label = QLabel(name) slider = QSlider() slider.setRange(*slider_range) slider.setValue(slider_value) slider.setOrientation(Qt.Horizontal) input_ = QDoubleSpinBox() input_.setRange(*range_) input_.setDecimals(3) input_.setSingleStep(0.01) input_.setValue(input_value) slider.valueChanged.connect(self.on_value_changed) input_.valueChanged.connect(self.on_value_changed) slider.valueChanged.connect( lambda x, input_=input_, range_=range_: input_.setValue( x / 1000 * (range_[-1] - range_[0]) + range_[0])) input_.valueChanged.connect( lambda x, slider=slider, range_=range_: slider.setValue( (x - range_[0]) / (range_[-1] - range_[0]) * 1000)) group_layout.addWidget(label, j, 0) group_layout.addWidget(slider, j, 1) group_layout.addWidget(input_, j, 2) inputs.append(input_) self.control_layout.addWidget(group, i + 5, 0, 1, 4) widgets.append(group) input_widgets.append(inputs) self.control_widgets = widgets self.input_widgets = input_widgets
def init_font_size_layout(text_item): font_size_input = QDoubleSpinBox() font_size_input.setDecimals(2) font_size_input.setRange(0.5, 900.) font_size_input.setValue(text_item.font().pointSizeF()) font_size_input.valueChanged.connect(lambda: set_font_size( font_size_input.value(), text_item)) font_size_layout = QHBoxLayout() font_size_layout.addWidget(font_size_input) font_size_layout.addWidget(QLabel("pt")) return font_size_layout
def set_object(self, controller): for idx, h in enumerate(self._layouts): h.removeWidget(self._sliders[idx]) self._sliders[idx].deleteLater() h.removeWidget(self._spin_boxes[idx]) self._spin_boxes[idx].deleteLater() h.removeWidget(self._labels[idx]) self._labels[idx].deleteLater() self.parameterSliderLayout.removeItem(h) h.deleteLater() self._layouts = [] self._sliders = [] self._spin_boxes = [] self._labels = [] self._controller = controller if self._controller is not None: self.activatePlayerControls() n_frames = controller.getNumberOfFrames() self.setFrameRange(0, n_frames - 1) self.neighborLineEdit.setText(str(controller.n_neighbors)) parameter = controller.current_parameter min_pos, max_pos = controller.get_parameter_range() for idx, label in enumerate(controller.get_parameter_labels()): print("add parameter", label, min_pos[idx], max_pos[idx]) h_layout = QHBoxLayout() parameter_label = QLabel() parameter_label.setText(label) h_layout.addWidget(parameter_label) parameter_slider = QSlider() parameter_slider.setOrientation(Qt.Horizontal) parameter_slider.setRange(min_pos[idx] * 100, max_pos[idx] * 100) if parameter is not None: parameter_slider.setValue(parameter[idx] * 100) h_layout.addWidget(parameter_slider) parameter_spin_box = QDoubleSpinBox() parameter_spin_box.setRange(min_pos[idx], max_pos[idx]) if parameter is not None: parameter_spin_box.setValue(parameter[idx]) h_layout.addWidget(parameter_spin_box) self.parameterSliderLayout.addLayout(h_layout) parameter_slider.valueChanged.connect(self.parameter_changed) parameter_spin_box.valueChanged.connect( self.update_parameter_from_spin_box) self._sliders.append(parameter_slider) self._spin_boxes.append(parameter_spin_box) self._labels.append(parameter_label) self._layouts.append(h_layout) self.update_animation_controller_list()
class Slider(): def update_offset(self, pos): offset = round(pos, 2) return offset def __init__(self, tool, text, tooltip, precision, minimal, maximal, interval): self.tool = tool self.scale = precision self.name_label = QLabel(text) self.name_label.setAlignment(Qt.AlignLeft) if not tooltip == None: self.name_label.setToolTip(tooltip) self.offset_edit = QDoubleSpinBox() self.offset_edit.setRange(minimal, maximal) self.offset_edit.setSingleStep(precision) self.offset_edit.setDecimals(2) self.offset_edit.setAlignment(Qt.AlignRight) self.offset_edit.valueChanged.connect( lambda pos: self.slider.setSliderPosition( self.update_offset(pos) / self.scale)) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(minimal / self.scale, maximal) self.slider.setTickInterval(interval / self.scale) self.slider.setTickPosition(QSlider.TicksAbove) self.slider.valueChanged.connect(lambda pos: self.offset_edit.setValue( self.update_offset(pos * self.scale))) self.update_offset(0)
class FloatWidget(QWidget): def __init__(self, name, value): super(FloatWidget, self).__init__() self.setContentsMargins(0, 0, 0, 0) layout = QHBoxLayout() self.setLayout(layout) self.name_lbl = QLabel(name) self.name_lbl.setFixedWidth(150) layout.addWidget(self.name_lbl) self.float_spn = QDoubleSpinBox() self.float_spn.setValue(value) layout.addWidget(self.float_spn)
def get_settings_window(self, parent=None): gcodesender_settings = QGroupBox("GCodeSender Settings:", parent) buildplate_label = QLabel("Buildplate Motor:", gcodesender_settings) nodes = [str(item) for item in self.__g_code_sender.get_motor_nodes_id_list()] self.buildplate_combo_box = MyQComboBox(gcodesender_settings) self.buildplate_combo_box.addItems(nodes) self.buildplate_combo_box.setCurrentIndex(self.__g_code_sender.get_buildplate_node()) self.buildplate_combo_box.combo_box_clicked.connect(self.__update_nodes_list) self.buildplate_combo_box.currentIndexChanged.connect(self.__set_buildplate_node) wiper_label = QLabel("Wiper Motor:", gcodesender_settings) self.wiper_combo_box = MyQComboBox(gcodesender_settings) self.wiper_combo_box.addItems(nodes) self.wiper_combo_box.setCurrentIndex(self.__g_code_sender.get_wiper_node()) self.wiper_combo_box.combo_box_clicked.connect(self.__update_nodes_list) self.wiper_combo_box.currentIndexChanged.connect(self.__set_wiper_node) buildplate_recoat_offset_label = QLabel("Buildplate recoating offset (mm):", gcodesender_settings) buildplate_recoat_offset_spin = QDoubleSpinBox(gcodesender_settings) buildplate_recoat_offset_spin.setRange(0, 99999) buildplate_recoat_offset_spin.setDecimals(3) buildplate_recoat_offset_spin.setSingleStep(0.001) buildplate_recoat_offset_spin.setValue(self.__g_code_sender.get_building_plate_recoating_offset()) buildplate_recoat_offset_spin.valueChanged.connect(self.__g_code_sender.set_building_plate_recoat_offset) buildplate_recoat_feedrate_label = QLabel("Buildplate recoating feedrate (mm/min):", gcodesender_settings) buildplate_recoat_feedrate_spin = QDoubleSpinBox(gcodesender_settings) buildplate_recoat_feedrate_spin.setRange(0, 99999) buildplate_recoat_feedrate_spin.setDecimals(3) buildplate_recoat_feedrate_spin.setSingleStep(0.001) buildplate_recoat_feedrate_spin.setValue(self.__g_code_sender.get_building_plate_recoating_feedrate()) buildplate_recoat_feedrate_spin.valueChanged.connect(self.__g_code_sender.set_building_plate_recoat_feedrate) wiper_recoat_offset_label = QLabel("Wiper recoating offset: (mm)", gcodesender_settings) wiper_recoat_offset_spin = QDoubleSpinBox(gcodesender_settings) wiper_recoat_offset_spin.setRange(0, 99999) wiper_recoat_offset_spin.setDecimals(3) wiper_recoat_offset_spin.setSingleStep(0.001) wiper_recoat_offset_spin.setValue(self.__g_code_sender.get_wiper_recoating_offset()) wiper_recoat_offset_spin.valueChanged.connect(self.__g_code_sender.set_wiper_recoat_offset) wiper_recoat_feedrate_label = QLabel("Wiper recoating feedrate (mm/min):", gcodesender_settings) wiper_recoat_feedrate_spin = QDoubleSpinBox(gcodesender_settings) wiper_recoat_feedrate_spin.setRange(0, 99999) wiper_recoat_feedrate_spin.setDecimals(3) wiper_recoat_feedrate_spin.setSingleStep(0.001) wiper_recoat_feedrate_spin.setValue(self.__g_code_sender.get_wiper_recoating_feedrate()) wiper_recoat_feedrate_spin.valueChanged.connect(self.__g_code_sender.set_wiper_recoat_feedrate) motor_layout = QGridLayout(gcodesender_settings) motor_layout.addWidget(buildplate_label, 0, 0) motor_layout.addWidget(self.buildplate_combo_box, 0, 1) motor_layout.addWidget(buildplate_recoat_offset_label, 1, 0) motor_layout.addWidget(buildplate_recoat_offset_spin, 1, 1) motor_layout.addWidget(buildplate_recoat_feedrate_label, 2, 0) motor_layout.addWidget(buildplate_recoat_feedrate_spin, 2, 1) motor_layout.addWidget(wiper_label, 3, 0) motor_layout.addWidget(self.wiper_combo_box, 3, 1) motor_layout.addWidget(wiper_recoat_offset_label, 4, 0) motor_layout.addWidget(wiper_recoat_offset_spin, 4, 1) motor_layout.addWidget(wiper_recoat_feedrate_label, 5, 0) motor_layout.addWidget(wiper_recoat_feedrate_spin, 5, 1) return gcodesender_settings
class Config(SignalNode.Config): """Config widget displayed for LSLInput.""" def __init__(self, parent=None): super().__init__(parent=parent) # Add a new layout self.average = QDoubleSpinBox() self.average.setMaximum(sys.float_info.max) # TODO: proper max self.average.valueChanged.connect(self.updateModel) self.standard_deviation = QDoubleSpinBox() self.standard_deviation.setMaximum( sys.float_info.max) # TODO: proper max self.standard_deviation.valueChanged.connect(self.updateModel) layout = QFormLayout() self.setLayout(layout) layout.addRow("Average", self.average) layout.addRow("Std. Deviation", self.standard_deviation) def updateModel(self): n = self.node() if n is None: return average = self.average.value() standard_deviation = self.standard_deviation.value() n.setAverage(average) n.setStandardDeviation(standard_deviation) def updateView(self): n = self.node() if n is None: return self.average.blockSignals(True) self.standard_deviation.blockSignals(True) self.average.setValue(n.average()) self.standard_deviation.setValue(n.standardDeviation()) self.average.blockSignals(False) self.standard_deviation.blockSignals(False)
class FloatField(Field): def __init__(self, property): super(FloatField, self).__init__(property) self._widget = QDoubleSpinBox() self._widget.valueChanged.connect(self.emitChanged) self._widget.setMinimum(property.min()) self._widget.setMaximum(property.max()) self._widget.setSingleStep(property.step()) def value(self): return self._widget.value() def setValue(self, value): self._widget.setValue(value) def setReadOnly(self, value): self._widget.setReadOnly(value) valueProperty = Property( float, value, Field.setValueBlocked) # C++: should be in parent class
class Ui_MainWindow: """This class holds all the UI structral info: widgets and their organization. main_window is the parent app window, which handles hooking the GUI widgets up to the controller/model.""" def setupUi(self, main_window): main_window.setObjectName('main_window') main_window.setWindowTitle('qt_mvc Demo') main_window.resize(800, 600) self.central_widget = QWidget(main_window) self.central_widget.setObjectName('centralwidget') self.central_layout = QVBoxLayout(self.central_widget) self.central_layout.setObjectName('centrallayout') self.top_bar_layout = QHBoxLayout() self.top_bar_layout.setObjectName('top_barlayout') self.base_label = QLabel('Base: ') self.base_entry = QDoubleSpinBox() self.base_entry.setObjectName('base_entry') self.exp_label = QLabel('Exponent: ') self.exp_entry = QDoubleSpinBox() self.exp_entry.setObjectName('exp_entry') self.base_entry.setValue(1) self.base_entry.setMinimum(0.1) self.exp_entry = QDoubleSpinBox() self.exp_entry.setValue(2) for widget in [ self.base_label, self.base_entry, self.exp_label, self.exp_entry ]: self.top_bar_layout.addWidget(widget) self.central_layout.addLayout(self.top_bar_layout) self.plot = PlotWidget() self.central_layout.addWidget(self.plot) main_window.setCentralWidget(self.central_widget)
def _create_int_float(self, val, is_float: bool = False, key: str = None) -> QWidget: """ Creates the widget for integer or float. Parameters ---------- val : list list of type, default value, description is_float : bool, optional is the value suposed to be a float, by default False key : str, optional The variable name, by default None Returns ------- QWidget The given widget correctly set up """ default_val = val[1] if val[1] is not None else 0 if is_float: widget = QDoubleSpinBox() else: widget = QSpinBox() widget.setMinimum(-1000) widget.setMaximum(1000) # Needed to return to default if it is ever needed widget.setMinimum(-1000) widget.setMaximum(1000) widget.setValue(self._set_default(widget, default_val)) tmp_val = self.config_val.get(key, None) if isinstance(tmp_val, str): if 'None' not in tmp_val: widget.setValue(float(tmp_val)) elif tmp_val is not None: widget.setValue(float(tmp_val)) widget.setToolTip(val[2]) return widget
def createPointWidget(self, x, y, z): layout = QHBoxLayout() x_label = QLabel("x:") x_label.setFixedWidth(10) x_val = QDoubleSpinBox() x_val.setSingleStep(0.01) x_val.setMinimum(-100) x_val.setMaximum(100) x_val.setDecimals(5) x_val.setValue(x) x_val.valueChanged.connect(self.onCoordsChanged) y_label = QLabel("y:") y_label.setFixedWidth(10) y_val = QDoubleSpinBox() y_val.setMinimum(-100) y_val.setMaximum(100) y_val.setDecimals(5) y_val.setSingleStep(0.01) y_val.setValue(y) y_val.valueChanged.connect(self.onCoordsChanged) z_label = QLabel("z:") z_label.setFixedWidth(10) z_val = QDoubleSpinBox() z_val.setMinimum(-100) z_val.setMaximum(100) z_val.setDecimals(5) z_val.setSingleStep(0.01) z_val.setValue(z) z_val.valueChanged.connect(self.onCoordsChanged) layout.addWidget(x_label) layout.addWidget(x_val) layout.addWidget(y_label) layout.addWidget(y_val) layout.addWidget(z_label) layout.addWidget(z_val) self.widgets.append([x_val, y_val, z_val]) return layout
def addArrowsBox(title, indexOffset, scale): layout = QGridLayout() deltaSB = QDoubleSpinBox() deltaSB.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.MinimumExpanding) if title == "Translation": deltaSB.setRange(-7, 7) deltaSB.setDecimals(3) deltaSB.setValue(1.0) else: deltaSB.setRange(-60, 60) deltaSB.setDecimals(2) deltaSB.setValue(10.0) setattr(self, "delta_" + title, deltaSB) deltaF = QFormLayout() deltaF.addRow("Delta", deltaSB) layout.addLayout(deltaF, 0, 0, 1, 3) layout.addWidget( positionButton( style.standardIcon(QStyle.SP_ArrowUp), "X+", 0 + indexOffset, deltaSB, scale, ), 1, 1, ) layout.addWidget( positionButton( style.standardIcon(QStyle.SP_ArrowDown), "X-", 0 + indexOffset, deltaSB, -scale, ), 3, 1, ) layout.addWidget( positionButton( style.standardIcon(QStyle.SP_ArrowLeft), "Y-", 1 + indexOffset, deltaSB, -scale, ), 2, 0, ) layout.addWidget( positionButton( style.standardIcon(QStyle.SP_ArrowRight), "Y+", 1 + indexOffset, deltaSB, scale, ), 2, 2, ) layout.addWidget( positionButton( style.standardIcon(QStyle.SP_ArrowUp), "Z+", 2 + indexOffset, deltaSB, scale, ), 1, 4, ) layout.addWidget( positionButton( style.standardIcon(QStyle.SP_ArrowDown), "Z-", 2 + indexOffset, deltaSB, -scale, ), 3, 4, ) ret = QGroupBox(title) ret.setLayout(layout) return ret
class SubtitleInfoDialog(QDialog): def __init__(self, subtitle_name="Test", subtitle_delay=0.0, subtitle_language=Default_Subtitle_Language, subtitle_track_name="Test", subtitle_set_default=False, subtitle_set_forced=False, subtitle_default_value_delay=0.0, subtitle_default_value_language=Default_Subtitle_Language, subtitle_default_value_track_name="Test", subtitle_default_value_set_default=False, subtitle_default_value_set_forced=False, subtitle_set_default_disabled=False, subtitle_set_forced_disabled=False, disable_edit=False, parent=None): super().__init__(parent) self.window_title = "Subtitle Info" self.state = "no" self.messageIcon = QLabel() self.disable_edit = disable_edit self.current_subtitle_language = str(subtitle_language) self.current_subtitle_delay = str(subtitle_delay) self.current_subtitle_track_name = str(subtitle_track_name) self.current_subtitle_set_default = subtitle_set_default self.current_subtitle_set_forced = subtitle_set_forced self.default_subtitle_language = str(subtitle_default_value_language) self.default_subtitle_delay = str(subtitle_default_value_delay) self.default_subtitle_track_name = str( subtitle_default_value_track_name) self.default_subtitle_set_default = subtitle_default_value_set_default self.default_subtitle_set_forced = subtitle_default_value_set_forced self.subtitle_set_default_disabled = subtitle_set_default_disabled self.subtitle_set_forced_disabled = subtitle_set_forced_disabled self.subtitle_name_label = QLabel("Subtitle Name:") self.subtitle_name_value = QLabel(str(subtitle_name)) self.subtitle_delay_label = QLabel("Subtitle Delay:") self.subtitle_delay_spin = QDoubleSpinBox() self.setup_subtitle_delay_spin() self.subtitle_language_label = QLabel("Subtitle Language:") self.subtitle_language_comboBox = QComboBox() self.setup_subtitle_language_comboBox() self.subtitle_track_name_label = QLabel("Subtitle Track Name:") self.subtitle_track_name_lineEdit = QLineEdit() self.setup_subtitle_track_name_lineEdit() self.subtitle_set_forced_label = QLabel("Subtitle Forced State:") self.subtitle_set_forced_checkBox = QCheckBox() self.setup_subtitle_set_forced_checkBox() self.subtitle_set_default_label = QLabel("Subtitle Default State:") self.subtitle_set_default_checkBox = QCheckBox() self.setup_subtitle_set_default_checkBox() self.yes_button = QPushButton("OK") self.no_button = QPushButton("Cancel") self.reset_button = QPushButton("Reset To Default") self.buttons_layout = QHBoxLayout() self.subtitle_delay_layout = QHBoxLayout() self.subtitle_language_layout = QHBoxLayout() self.subtitle_track_name_layout = QHBoxLayout() self.subtitle_set_default_layout = QHBoxLayout() self.subtitle_set_forced_layout = QHBoxLayout() self.buttons_layout.addWidget(QLabel(""), stretch=3) self.buttons_layout.addWidget(self.reset_button, stretch=2) self.buttons_layout.addWidget(self.yes_button, stretch=2) self.buttons_layout.addWidget(self.no_button, stretch=2) self.buttons_layout.addWidget(QLabel(""), stretch=3) self.subtitle_setting_layout = QGridLayout() self.subtitle_changeble_setting_layout = QFormLayout() self.subtitle_changeble_setting_layout.addRow(self.subtitle_name_label, self.subtitle_name_value) self.subtitle_changeble_setting_layout.addRow( self.subtitle_track_name_label, self.subtitle_track_name_lineEdit) self.subtitle_changeble_setting_layout.addRow( self.subtitle_language_label, self.subtitle_language_comboBox) self.subtitle_changeble_setting_layout.addRow( self.subtitle_delay_label, self.subtitle_delay_spin) self.subtitle_changeble_setting_layout.addRow( self.subtitle_set_default_label, self.subtitle_set_default_checkBox) self.subtitle_changeble_setting_layout.addRow( self.subtitle_set_forced_label, self.subtitle_set_forced_checkBox) self.subtitle_setting_layout.addLayout( self.subtitle_changeble_setting_layout, 0, 0, 5, 2) self.subtitle_setting_layout.addWidget(self.messageIcon, 0, 3, 5, -1) self.main_layout = QGridLayout() self.main_layout.addLayout(self.subtitle_setting_layout, 0, 0, 2, 3) self.main_layout.addLayout(self.buttons_layout, 2, 0, 1, -1) self.main_layout.setContentsMargins(20, 20, 20, 20) self.setLayout(self.main_layout) self.setup_ui() self.signal_connect() def setup_ui(self): self.disable_question_mark_window() self.messageIcon.setPixmap( QtGui.QPixmap(GlobalFiles.SubtitleIconPath).scaledToHeight(100)) self.set_dialog_values() self.set_default_buttons() if self.subtitle_set_default_disabled: self.subtitle_set_default_disable() if self.subtitle_set_forced_disabled: self.subtitle_set_forced_disable() if self.disable_edit: self.subtitle_track_name_lineEdit.setEnabled(False) self.subtitle_language_comboBox.setEnabled(False) self.subtitle_delay_spin.setEnabled(False) self.subtitle_set_default_checkBox.setEnabled(False) self.subtitle_set_forced_checkBox.setEnabled(False) self.reset_button.setEnabled(False) self.setup_tool_tip_hint_subtitle_set_default() self.setup_tool_tip_hint_subtitle_set_forced() def signal_connect(self): self.subtitle_track_name_lineEdit.textEdited.connect( self.update_current_subtitle_track_name) self.subtitle_delay_spin.editingFinished.connect( self.update_current_subtitle_delay) self.subtitle_language_comboBox.currentTextChanged.connect( self.update_current_subtitle_language) self.subtitle_set_default_checkBox.stateChanged.connect( self.update_current_subtitle_set_default) self.subtitle_set_forced_checkBox.stateChanged.connect( self.update_current_subtitle_set_forced) self.yes_button.clicked.connect(self.click_yes) self.no_button.clicked.connect(self.click_no) self.reset_button.clicked.connect(self.reset_subtitle_setting) def click_yes(self): self.state = "yes" self.close() def click_no(self): self.close() def set_dialog_values(self): self.setWindowTitle(self.window_title) self.setWindowIcon(GlobalFiles.InfoSettingIcon) def disable_question_mark_window(self): self.setWindowFlag(Qt.WindowContextHelpButtonHint, on=False) def increase_message_font_size(self, value): message_font = self.message.font() message_font.setPointSize(self.message.fontInfo().pointSize() + value) self.message.setFont(message_font) def set_default_buttons(self): self.yes_button.setDefault(True) self.yes_button.setFocus() def showEvent(self, a0: QtGui.QShowEvent) -> None: super().showEvent(a0) self.setFixedSize(self.size()) def setup_subtitle_track_name_lineEdit(self): self.subtitle_track_name_lineEdit.setClearButtonEnabled(True) self.subtitle_track_name_lineEdit.setText( self.current_subtitle_track_name) def setup_subtitle_language_comboBox(self): self.subtitle_language_comboBox.addItems(AllSubtitlesLanguages) self.subtitle_language_comboBox.setCurrentIndex( AllSubtitlesLanguages.index(self.current_subtitle_language)) self.subtitle_language_comboBox.setMaxVisibleItems(8) self.subtitle_language_comboBox.setStyleSheet( "QComboBox { combobox-popup: 0; }") def setup_subtitle_delay_spin(self): # self.subtitle_delay_spin.setMaximumWidth(screen_size.width() // 16) self.subtitle_delay_spin.setDecimals(3) self.subtitle_delay_spin.setMinimum(-9999.0) self.subtitle_delay_spin.setMaximum(9999.0) self.subtitle_delay_spin.setSingleStep(0.5) self.subtitle_delay_spin.setValue(float(self.current_subtitle_delay)) def setup_subtitle_set_default_checkBox(self): self.subtitle_set_default_checkBox.setText("Set Default") self.subtitle_set_default_checkBox.setChecked( bool(self.current_subtitle_set_default)) def setup_subtitle_set_forced_checkBox(self): self.subtitle_set_forced_checkBox.setText("Set Forced") self.subtitle_set_forced_checkBox.setChecked( bool(self.current_subtitle_set_forced)) def update_current_subtitle_track_name(self): self.current_subtitle_track_name = str( self.subtitle_track_name_lineEdit.text()) def update_current_subtitle_delay(self): self.current_subtitle_delay = round(self.subtitle_delay_spin.value(), 5) def update_current_subtitle_language(self): self.current_subtitle_language = str( self.subtitle_language_comboBox.currentText()) def update_current_subtitle_set_default(self): self.current_subtitle_set_default = ( self.subtitle_set_default_checkBox.checkState() == Qt.Checked) def update_current_subtitle_set_forced(self): self.current_subtitle_set_forced = ( self.subtitle_set_forced_checkBox.checkState() == Qt.Checked) def reset_subtitle_setting(self): self.current_subtitle_language = self.default_subtitle_language self.current_subtitle_delay = self.default_subtitle_delay self.current_subtitle_track_name = self.default_subtitle_track_name self.current_subtitle_set_default = self.default_subtitle_set_default self.current_subtitle_set_forced = self.default_subtitle_set_forced self.subtitle_language_comboBox.setCurrentIndex( AllSubtitlesLanguages.index(self.current_subtitle_language)) self.subtitle_delay_spin.setValue(float(self.current_subtitle_delay)) self.subtitle_track_name_lineEdit.setText( self.current_subtitle_track_name) self.subtitle_set_default_checkBox.setChecked( bool(self.current_subtitle_set_default)) self.subtitle_set_forced_checkBox.setChecked( bool(self.current_subtitle_set_forced)) def subtitle_set_default_disable(self): self.subtitle_set_default_checkBox.setDisabled(True) def subtitle_set_forced_disable(self): self.subtitle_set_forced_checkBox.setDisabled(True) def setup_tool_tip_hint_subtitle_set_default(self): if self.subtitle_set_default_checkBox.isEnabled(): self.subtitle_set_default_checkBox.setToolTip( "<nobr>set this subtitle to be the default subtitle track " "when play") self.subtitle_set_default_checkBox.setToolTipDuration(12000) else: self.subtitle_set_default_checkBox.setToolTip( "<nobr>set this subtitle to be the default subtitle track when play<br><b>Disabled</b> because " "option " "<b>make this subtitle default</b> is enabled on mux setting tab " ) self.subtitle_set_default_checkBox.setToolTipDuration(12000) def setup_tool_tip_hint_subtitle_set_forced(self): if self.subtitle_set_forced_checkBox.isEnabled(): self.subtitle_set_forced_checkBox.setToolTip( "<nobr>set this subtitle to be the forced subtitle track when " "play") self.subtitle_set_forced_checkBox.setToolTipDuration(12000) else: self.subtitle_set_forced_checkBox.setToolTip( "<nobr>set this subtitle to be the forced subtitle track when play<br><b>Disabled</b> because " "option " "<b>make this subtitle default and forced</b> is enabled on mux setting tab " ) self.subtitle_set_forced_checkBox.setToolTipDuration(12000) def execute(self): self.exec_()
class MainWidget(QWidget): def __init__(self, parent=None): super(MainWidget, self).__init__(parent) self.chart = QtCharts.QChart() self.series = QtCharts.QBarSeries() self.main_layout = QGridLayout() self.button_layout = QGridLayout() self.font_layout = QFormLayout() self.font_size = QDoubleSpinBox() self.legend_posx = QDoubleSpinBox() self.legend_posy = QDoubleSpinBox() self.legend_width = QDoubleSpinBox() self.legend_height = QDoubleSpinBox() self.detach_legend_button = QPushButton("Toggle attached") self.detach_legend_button.clicked.connect(self.toggle_attached) self.button_layout.addWidget(self.detach_legend_button, 0, 0) self.add_set_button = QPushButton("add barset") self.add_set_button.clicked.connect(self.add_barset) self.button_layout.addWidget(self.add_set_button, 2, 0) self.remove_barset_button = QPushButton("remove barset") self.remove_barset_button.clicked.connect(self.remove_barset) self.button_layout.addWidget(self.remove_barset_button, 3, 0) self.align_button = QPushButton("Align (Bottom)") self.align_button.clicked.connect(self.set_legend_alignment) self.button_layout.addWidget(self.align_button, 4, 0) self.bold_button = QPushButton("Toggle bold") self.bold_button.clicked.connect(self.toggle_bold) self.button_layout.addWidget(self.bold_button, 8, 0) self.italic_button = QPushButton("Toggle italic") self.italic_button.clicked.connect(self.toggle_italic) self.button_layout.addWidget(self.italic_button, 9, 0) self.legend_posx.valueChanged.connect(self.update_legend_layout) self.legend_posy.valueChanged.connect(self.update_legend_layout) self.legend_width.valueChanged.connect(self.update_legend_layout) self.legend_height.valueChanged.connect(self.update_legend_layout) legend_layout = QFormLayout() legend_layout.addRow("HPos", self.legend_posx) legend_layout.addRow("VPos", self.legend_posy) legend_layout.addRow("Width", self.legend_width) legend_layout.addRow("Height", self.legend_height) self.legend_settings = QGroupBox("Detached legend") self.legend_settings.setLayout(legend_layout) self.button_layout.addWidget(self.legend_settings) self.legend_settings.setVisible(False) # Create chart view with the chart self.chart_view = QtCharts.QChartView(self.chart, self) # Create spinbox to modify font size self.font_size.setValue(self.chart.legend().font().pointSizeF()) self.font_size.valueChanged.connect(self.font_size_changed) self.font_layout.addRow("Legend font size", self.font_size) # Create layout for grid and detached legend self.main_layout.addLayout(self.button_layout, 0, 0) self.main_layout.addLayout(self.font_layout, 1, 0) self.main_layout.addWidget(self.chart_view, 0, 1, 3, 1) self.setLayout(self.main_layout) self.create_series() def create_series(self): self.add_barset() self.add_barset() self.add_barset() self.add_barset() self.chart.addSeries(self.series) self.chart.setTitle("Legend detach example") self.chart.createDefaultAxes() self.chart.legend().setVisible(True) self.chart.legend().setAlignment(Qt.AlignBottom) self.chart_view.setRenderHint(QPainter.Antialiasing) def show_legend_spinbox(self): self.legend_settings.setVisible(True) chart_viewrect = self.chart_view.rect() self.legend_posx.setMinimum(0) self.legend_posx.setMaximum(chart_viewrect.width()) self.legend_posx.setValue(150) self.legend_posy.setMinimum(0) self.legend_posy.setMaximum(chart_viewrect.height()) self.legend_posy.setValue(150) self.legend_width.setMinimum(0) self.legend_width.setMaximum(chart_viewrect.width()) self.legend_width.setValue(150) self.legend_height.setMinimum(0) self.legend_height.setMaximum(chart_viewrect.height()) self.legend_height.setValue(75) def hideLegendSpinbox(self): self.legend_settings.setVisible(False) def toggle_attached(self): legend = self.chart.legend() if legend.isAttachedToChart(): legend.detachFromChart() legend.setBackgroundVisible(True) legend.setBrush(QBrush(QColor(128, 128, 128, 128))) legend.setPen(QPen(QColor(192, 192, 192, 192))) self.show_legend_spinbox() self.update_legend_layout() else: legend.attachToChart() legend.setBackgroundVisible(False) self.hideLegendSpinbox() self.update() def add_barset(self): series_count = self.series.count() bar_set = QtCharts.QBarSet("set {}".format(series_count)) delta = series_count * 0.1 bar_set.append([1 + delta, 2 + delta, 3 + delta, 4 + delta]) self.series.append(bar_set) def remove_barset(self): sets = self.series.barSets() len_sets = len(sets) if len_sets > 0: self.series.remove(sets[len_sets - 1]) def set_legend_alignment(self): button = self.sender() legend = self.chart.legend() alignment = legend.alignment() if alignment == Qt.AlignTop: legend.setAlignment(Qt.AlignLeft) if button: button.setText("Align (Left)") elif alignment == Qt.AlignLeft: legend.setAlignment(Qt.AlignBottom) if button: button.setText("Align (Bottom)") elif alignment == Qt.AlignBottom: legend.setAlignment(Qt.AlignRight) if button: button.setText("Align (Right)") else: if button: button.setText("Align (Top)") legend.setAlignment(Qt.AlignTop) def toggle_bold(self): legend = self.chart.legend() font = legend.font() font.setBold(not font.bold()) legend.setFont(font) def toggle_italic(self): legend = self.chart.legend() font = legend.font() font.setItalic(not font.italic()) legend.setFont(font) def font_size_changed(self): legend = self.chart.legend() font = legend.font() font_size = self.font_size.value() if font_size < 1: font_size = 1 font.setPointSizeF(font_size) legend.setFont(font) def update_legend_layout(self): legend = self.chart.legend() rect = QRectF(self.legend_posx.value(), self.legend_posy.value(), self.legend_width.value(), self.legend_height.value()) legend.setGeometry(rect) legend.update()
class Config(SignalNode.Config): """Config widget displayed for BandpassFilter.""" def __init__(self, parent=None): super().__init__(parent=parent) # Upper bound ---------------------------------------------------------------------------------------------- self.lower_bound_enable = QCheckBox() self.lower_bound_enable.setChecked(True) self.lower_bound_enable.stateChanged.connect(self.updateModel) self.lower_bound = QDoubleSpinBox() self.lower_bound.valueChanged.connect(self.updateModel) self.lower_bound.setMinimum(0) self.lower_bound.setMaximum(250) self.lower_bound.setSuffix(" Hz") layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) lower_bound_widget = QWidget() lower_bound_widget.setContentsMargins(0, 0, 0, 0) lower_bound_widget.setLayout(layout) layout.addWidget(self.lower_bound_enable) layout.addWidget(self.lower_bound) # Lower bound ---------------------------------------------------------------------------------------------- self.upper_bound_enable = QCheckBox() self.upper_bound_enable.setChecked(True) self.upper_bound_enable.stateChanged.connect(self.updateModel) self.upper_bound = QDoubleSpinBox() self.upper_bound.valueChanged.connect(self.updateModel) self.upper_bound.setMinimum(0) self.upper_bound.setMaximum(250) self.upper_bound.setSuffix(" Hz") layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) upper_bound_widget = QWidget() upper_bound_widget.setContentsMargins(0, 0, 0, 0) upper_bound_widget.setLayout(layout) layout.addWidget(self.upper_bound_enable) layout.addWidget(self.upper_bound) # Filter type and length ----------------------------------------------------------------------------------- self.filter_type = QComboBox() for name in BandpassFilter.filter_name_to_type: self.filter_type.addItem(name) self.filter_type.currentTextChanged.connect(self.updateModel) self.filter_length = QSpinBox() self.filter_length.setMinimum(2) self.filter_length.setMaximum(1000000) self.filter_length.setValue(1000) self.filter_length.valueChanged.connect(self.updateModel) self.filter_order = QSpinBox() self.filter_order.setRange(1, 4) self.filter_order.valueChanged.connect(self.updateModel) # ---------------------------------------------------------------------------------------------------------- layout = QFormLayout() layout.addRow("Lower bound:", lower_bound_widget) layout.addRow("Upper bound:", upper_bound_widget) layout.addRow("Filter type:", self.filter_type) layout.addRow("Filter order:", self.filter_order) layout.addRow("Filter length:", self.filter_length) self.setLayout(layout) def updateModel(self): n = self.node() if n is None: return if self.lower_bound_enable.isChecked(): lower_bound = self.lower_bound.value() else: lower_bound = None if self.upper_bound_enable.isChecked(): upper_bound = self.upper_bound.value() else: upper_bound = None filter_type = n.filter_name_to_type[self.filter_type.currentText()] filter_length = self.filter_length.value() filter_order = self.filter_order.value() n.setLowerBound(lower_bound) n.setUpperBound(upper_bound) n.setFilterType(filter_type) n.setFilterLength(filter_length) n.setFilterOrder(filter_order) def updateView(self): n = self.node() if n is None: return # Prevent view fields from emitting signals while they are updated self.lower_bound.blockSignals(True) self.upper_bound.blockSignals(True) self.filter_type.blockSignals(True) self.filter_length.blockSignals(True) self.filter_order.blockSignals(True) if n.upperBound() is None: self.upper_bound_enable.setChecked(False) else: self.upper_bound_enable.setChecked(True) self.upper_bound.setValue(n.upperBound()) if n.lowerBound() is None: self.lower_bound_enable.setChecked(False) else: self.lower_bound_enable.setChecked(True) self.lower_bound.setValue(n.lowerBound()) self.filter_type.setCurrentText( n.filter_type_to_name[n.filterType()]) self.filter_length.setValue(n.filterLength()) self.filter_order.setValue(n.filterOrder()) # Release the block and call adjust self.lower_bound.blockSignals(False) self.upper_bound.blockSignals(False) self.filter_type.blockSignals(False) self.filter_length.blockSignals(False) self.filter_order.blockSignals(False) self._adjust() def _adjust(self): """Adjust displayed values and limits in response to changes.""" # Enable spinbox widgets based on their checkbox self.lower_bound.setEnabled(self.lower_bound_enable.isChecked()) self.upper_bound.setEnabled(self.upper_bound_enable.isChecked()) # Adjust min and max so that lower_bound is never higher than upper_bound if self.lower_bound_enable.isChecked(): self.upper_bound.setMinimum(self.lower_bound.value()) else: self.upper_bound.setMinimum(0) if self.upper_bound_enable.isChecked(): self.lower_bound.setMaximum(self.upper_bound.value()) else: self.lower_bound.setMaximum(250) if self.filter_type.currentText() == "Butterworth": self.filter_order.setEnabled(True) else: self.filter_order.setEnabled(False)
class SCOUTS(QMainWindow): """Main Window Widget for SCOUTS.""" style = { 'title': 'QLabel {font-size: 18pt; font-weight: 600}', 'header': 'QLabel {font-size: 12pt; font-weight: 520}', 'label': 'QLabel {font-size: 10pt}', 'button': 'QPushButton {font-size: 10pt}', 'md button': 'QPushButton {font-size: 12pt}', 'run button': 'QPushButton {font-size: 18pt; font-weight: 600}', 'line edit': 'QLineEdit {font-size: 10pt}', 'checkbox': 'QCheckBox {font-size: 10pt}', 'radio button': 'QRadioButton {font-size: 10pt}' } def __init__(self) -> None: """SCOUTS Constructor. Defines all aspects of the GUI.""" # ### # ### Main Window setup # ### # Inherits from QMainWindow super().__init__() self.rootdir = get_project_root() self.threadpool = QThreadPool() # Sets values for QMainWindow self.setWindowTitle("SCOUTS") self.setWindowIcon( QIcon( os.path.abspath(os.path.join(self.rootdir, 'src', 'scouts.ico')))) # Creates StackedWidget as QMainWindow's central widget self.stacked_pages = QStackedWidget(self) self.setCentralWidget(self.stacked_pages) # Creates Widgets for individual "pages" and adds them to the StackedWidget self.main_page = QWidget() self.samples_page = QWidget() self.gating_page = QWidget() self.pages = (self.main_page, self.samples_page, self.gating_page) for page in self.pages: self.stacked_pages.addWidget(page) # ## Sets widget at program startup self.stacked_pages.setCurrentWidget(self.main_page) # ### # ### MAIN PAGE # ### # Main page layout self.main_layout = QVBoxLayout(self.main_page) # Title section # Title self.title = QLabel(self.main_page) self.title.setText('SCOUTS - Single Cell Outlier Selector') self.title.setStyleSheet(self.style['title']) self.title.adjustSize() self.main_layout.addWidget(self.title) # ## Input section # Input header self.input_header = QLabel(self.main_page) self.input_header.setText('Input settings') self.input_header.setStyleSheet(self.style['header']) self.main_layout.addChildWidget(self.input_header) self.input_header.adjustSize() self.main_layout.addWidget(self.input_header) # Input frame self.input_frame = QFrame(self.main_page) self.input_frame.setFrameShape(QFrame.StyledPanel) self.input_frame.setLayout(QFormLayout()) self.main_layout.addWidget(self.input_frame) # Input button self.input_button = QPushButton(self.main_page) self.input_button.setStyleSheet(self.style['button']) self.set_icon(self.input_button, 'x-office-spreadsheet') self.input_button.setObjectName('input') self.input_button.setText(' Select input file (.xlsx or .csv)') self.input_button.clicked.connect(self.get_path) # Input path box self.input_path = QLineEdit(self.main_page) self.input_path.setObjectName('input_path') self.input_path.setStyleSheet(self.style['line edit']) # Go to sample naming page self.samples_button = QPushButton(self.main_page) self.samples_button.setStyleSheet(self.style['button']) self.set_icon(self.samples_button, 'preferences-other') self.samples_button.setText(' Name samples...') self.samples_button.clicked.connect(self.goto_samples_page) # Go to gating page self.gates_button = QPushButton(self.main_page) self.gates_button.setStyleSheet(self.style['button']) self.set_icon(self.gates_button, 'preferences-other') self.gates_button.setText(' Gating && outlier options...') self.gates_button.clicked.connect(self.goto_gates_page) # Add widgets above to input frame Layout self.input_frame.layout().addRow(self.input_button, self.input_path) self.input_frame.layout().addRow(self.samples_button) self.input_frame.layout().addRow(self.gates_button) # ## Analysis section # Analysis header self.analysis_header = QLabel(self.main_page) self.analysis_header.setText('Analysis settings') self.analysis_header.setStyleSheet(self.style['header']) self.analysis_header.adjustSize() self.main_layout.addWidget(self.analysis_header) # Analysis frame self.analysis_frame = QFrame(self.main_page) self.analysis_frame.setFrameShape(QFrame.StyledPanel) self.analysis_frame.setLayout(QVBoxLayout()) self.main_layout.addWidget(self.analysis_frame) # Cutoff text self.cutoff_text = QLabel(self.main_page) self.cutoff_text.setText('Type of outlier to select:') self.cutoff_text.setToolTip( 'Choose whether to select outliers using the cutoff value from a reference\n' 'sample (OutR) or by using the cutoff value calculated for each sample\n' 'individually (OutS)') self.cutoff_text.setStyleSheet(self.style['label']) # Cutoff button group self.cutoff_group = QButtonGroup(self) # Cutoff by sample self.cutoff_sample = QRadioButton(self.main_page) self.cutoff_sample.setText('OutS') self.cutoff_sample.setObjectName('sample') self.cutoff_sample.setStyleSheet(self.style['radio button']) self.cutoff_sample.setChecked(True) self.cutoff_group.addButton(self.cutoff_sample) # Cutoff by reference self.cutoff_reference = QRadioButton(self.main_page) self.cutoff_reference.setText('OutR') self.cutoff_reference.setObjectName('ref') self.cutoff_reference.setStyleSheet(self.style['radio button']) self.cutoff_group.addButton(self.cutoff_reference) # Both cutoffs self.cutoff_both = QRadioButton(self.main_page) self.cutoff_both.setText('both') self.cutoff_both.setObjectName('sample ref') self.cutoff_both.setStyleSheet(self.style['radio button']) self.cutoff_group.addButton(self.cutoff_both) # Markers text self.markers_text = QLabel(self.main_page) self.markers_text.setStyleSheet(self.style['label']) self.markers_text.setText('Show results for:') self.markers_text.setToolTip( 'Individual markers: for each marker, select outliers\n' 'Any marker: select cells that are outliers for AT LEAST one marker' ) # Markers button group self.markers_group = QButtonGroup(self) # Single marker self.single_marker = QRadioButton(self.main_page) self.single_marker.setText('individual markers') self.single_marker.setObjectName('single') self.single_marker.setStyleSheet(self.style['radio button']) self.single_marker.setChecked(True) self.markers_group.addButton(self.single_marker) # Any marker self.any_marker = QRadioButton(self.main_page) self.any_marker.setText('any marker') self.any_marker.setObjectName('any') self.any_marker.setStyleSheet(self.style['radio button']) self.markers_group.addButton(self.any_marker) # Both methods self.both_methods = QRadioButton(self.main_page) self.both_methods.setText('both') self.both_methods.setObjectName('single any') self.both_methods.setStyleSheet(self.style['radio button']) self.markers_group.addButton(self.both_methods) # Tukey text self.tukey_text = QLabel(self.main_page) self.tukey_text.setStyleSheet(self.style['label']) # Tukey button group self.tukey_text.setText('Tukey factor:') self.tukey_group = QButtonGroup(self) # Low Tukey value self.tukey_low = QRadioButton(self.main_page) self.tukey_low.setText('1.5') self.tukey_low.setStyleSheet(self.style['radio button']) self.tukey_low.setChecked(True) self.tukey_group.addButton(self.tukey_low) # High Tukey value self.tukey_high = QRadioButton(self.main_page) self.tukey_high.setText('3.0') self.tukey_high.setStyleSheet(self.style['radio button']) self.tukey_group.addButton(self.tukey_high) # Add widgets above to analysis frame layout self.analysis_frame.layout().addWidget(self.cutoff_text) self.cutoff_buttons = QHBoxLayout() for button in self.cutoff_group.buttons(): self.cutoff_buttons.addWidget(button) self.analysis_frame.layout().addLayout(self.cutoff_buttons) self.analysis_frame.layout().addWidget(self.markers_text) self.markers_buttons = QHBoxLayout() for button in self.markers_group.buttons(): self.markers_buttons.addWidget(button) self.analysis_frame.layout().addLayout(self.markers_buttons) self.analysis_frame.layout().addWidget(self.tukey_text) self.tukey_buttons = QHBoxLayout() for button in self.tukey_group.buttons(): self.tukey_buttons.addWidget(button) self.tukey_buttons.addWidget(QLabel()) # aligns row with 2 buttons self.analysis_frame.layout().addLayout(self.tukey_buttons) # ## Output section # Output header self.output_header = QLabel(self.main_page) self.output_header.setText('Output settings') self.output_header.setStyleSheet(self.style['header']) self.output_header.adjustSize() self.main_layout.addWidget(self.output_header) # Output frame self.output_frame = QFrame(self.main_page) self.output_frame.setFrameShape(QFrame.StyledPanel) self.output_frame.setLayout(QFormLayout()) self.main_layout.addWidget(self.output_frame) # Output button self.output_button = QPushButton(self.main_page) self.output_button.setStyleSheet(self.style['button']) self.set_icon(self.output_button, 'folder') self.output_button.setObjectName('output') self.output_button.setText(' Select output folder') self.output_button.clicked.connect(self.get_path) # Output path box self.output_path = QLineEdit(self.main_page) self.output_path.setStyleSheet(self.style['line edit']) # Generate CSV checkbox self.output_csv = QCheckBox(self.main_page) self.output_csv.setText('Export multiple text files (.csv)') self.output_csv.setStyleSheet(self.style['checkbox']) self.output_csv.setChecked(True) # Generate XLSX checkbox self.output_excel = QCheckBox(self.main_page) self.output_excel.setText('Export multiple Excel spreadsheets (.xlsx)') self.output_excel.setStyleSheet(self.style['checkbox']) self.output_excel.clicked.connect(self.enable_single_excel) # Generate single, large XLSX checkbox self.single_excel = QCheckBox(self.main_page) self.single_excel.setText( 'Also save one multi-sheet Excel spreadsheet') self.single_excel.setToolTip( 'After generating all Excel spreadsheets, SCOUTS combines them into ' 'a single\nExcel spreadsheet where each sheet corresponds to an output' 'file from SCOUTS') self.single_excel.setStyleSheet(self.style['checkbox']) self.single_excel.setEnabled(False) self.single_excel.clicked.connect(self.memory_warning) # Add widgets above to output frame layout self.output_frame.layout().addRow(self.output_button, self.output_path) self.output_frame.layout().addRow(self.output_csv) self.output_frame.layout().addRow(self.output_excel) self.output_frame.layout().addRow(self.single_excel) # ## Run & help-quit section # Run button (stand-alone) self.run_button = QPushButton(self.main_page) self.set_icon(self.run_button, 'system-run') self.run_button.setText(' Run!') self.run_button.setStyleSheet(self.style['run button']) self.main_layout.addWidget(self.run_button) self.run_button.clicked.connect(self.run) # Help-quit frame (invisible) self.helpquit_frame = QFrame(self.main_page) self.helpquit_frame.setLayout(QHBoxLayout()) self.helpquit_frame.layout().setMargin(0) self.main_layout.addWidget(self.helpquit_frame) # Help button self.help_button = QPushButton(self.main_page) self.set_icon(self.help_button, 'help-about') self.help_button.setText(' Help') self.help_button.setStyleSheet(self.style['md button']) self.help_button.clicked.connect(self.get_help) # Quit button self.quit_button = QPushButton(self.main_page) self.set_icon(self.quit_button, 'process-stop') self.quit_button.setText(' Quit') self.quit_button.setStyleSheet(self.style['md button']) self.quit_button.clicked.connect(self.close) # Add widgets above to help-quit layout self.helpquit_frame.layout().addWidget(self.help_button) self.helpquit_frame.layout().addWidget(self.quit_button) # ### # ### SAMPLES PAGE # ### # Samples page layout self.samples_layout = QVBoxLayout(self.samples_page) # ## Title section # Title self.samples_title = QLabel(self.samples_page) self.samples_title.setText('Name your samples') self.samples_title.setStyleSheet(self.style['title']) self.samples_title.adjustSize() self.samples_layout.addWidget(self.samples_title) # Subtitle self.samples_subtitle = QLabel(self.samples_page) string = ( 'Please name the samples to be analysed by SCOUTS.\n\nSCOUTS searches the first ' 'column of your data\nand locates the exact string as part of the sample name.' ) self.samples_subtitle.setText(string) self.samples_subtitle.setStyleSheet(self.style['label']) self.samples_subtitle.adjustSize() self.samples_layout.addWidget(self.samples_subtitle) # ## Sample addition section # Sample addition frame self.samples_frame = QFrame(self.samples_page) self.samples_frame.setFrameShape(QFrame.StyledPanel) self.samples_frame.setLayout(QGridLayout()) self.samples_layout.addWidget(self.samples_frame) # Sample name box self.sample_name = QLineEdit(self.samples_page) self.sample_name.setStyleSheet(self.style['line edit']) self.sample_name.setPlaceholderText('Sample name ...') # Reference check self.is_reference = QCheckBox(self.samples_page) self.is_reference.setText('Reference?') self.is_reference.setStyleSheet(self.style['checkbox']) # Add sample to table self.add_sample_button = QPushButton(self.samples_page) QShortcut(QKeySequence("Return"), self.add_sample_button, self.write_to_sample_table) self.set_icon(self.add_sample_button, 'list-add') self.add_sample_button.setText(' Add sample (Enter)') self.add_sample_button.setStyleSheet(self.style['button']) self.add_sample_button.clicked.connect(self.write_to_sample_table) # Remove sample from table self.remove_sample_button = QPushButton(self.samples_page) QShortcut(QKeySequence("Delete"), self.remove_sample_button, self.remove_from_sample_table) self.set_icon(self.remove_sample_button, 'list-remove') self.remove_sample_button.setText(' Remove sample (Del)') self.remove_sample_button.setStyleSheet(self.style['button']) self.remove_sample_button.clicked.connect( self.remove_from_sample_table) # Add widgets above to sample addition layout self.samples_frame.layout().addWidget(self.sample_name, 0, 0) self.samples_frame.layout().addWidget(self.is_reference, 1, 0) self.samples_frame.layout().addWidget(self.add_sample_button, 0, 1) self.samples_frame.layout().addWidget(self.remove_sample_button, 1, 1) # ## Sample table self.sample_table = QTableWidget(self.samples_page) self.sample_table.setColumnCount(2) self.sample_table.setHorizontalHeaderItem(0, QTableWidgetItem('Sample')) self.sample_table.setHorizontalHeaderItem( 1, QTableWidgetItem('Reference?')) self.sample_table.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) self.sample_table.horizontalHeader().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.samples_layout.addWidget(self.sample_table) # ## Save & clear buttons # Save & clear frame (invisible) self.saveclear_frame = QFrame(self.samples_page) self.saveclear_frame.setLayout(QHBoxLayout()) self.saveclear_frame.layout().setMargin(0) self.samples_layout.addWidget(self.saveclear_frame) # Clear samples button self.clear_samples = QPushButton(self.samples_page) self.set_icon(self.clear_samples, 'edit-delete') self.clear_samples.setText(' Clear table') self.clear_samples.setStyleSheet(self.style['md button']) self.clear_samples.clicked.connect(self.prompt_clear_data) # Save samples button self.save_samples = QPushButton(self.samples_page) self.set_icon(self.save_samples, 'document-save') self.save_samples.setText(' Save samples') self.save_samples.setStyleSheet(self.style['md button']) self.save_samples.clicked.connect(self.goto_main_page) # Add widgets above to save & clear layout self.saveclear_frame.layout().addWidget(self.clear_samples) self.saveclear_frame.layout().addWidget(self.save_samples) # ### # ### GATING PAGE # ### # Gating page layout self.gating_layout = QVBoxLayout(self.gating_page) # ## Title section # Title self.gates_title = QLabel(self.gating_page) self.gates_title.setText('Gating & outlier options') self.gates_title.setStyleSheet(self.style['title']) self.gates_title.adjustSize() self.gating_layout.addWidget(self.gates_title) # ## Gating options section # Gating header self.gate_header = QLabel(self.gating_page) self.gate_header.setText('Gating') self.gate_header.setStyleSheet(self.style['header']) self.gate_header.adjustSize() self.gating_layout.addWidget(self.gate_header) # Gating frame self.gate_frame = QFrame(self.gating_page) self.gate_frame.setFrameShape(QFrame.StyledPanel) self.gate_frame.setLayout(QFormLayout()) self.gating_layout.addWidget(self.gate_frame) # Gating button group self.gating_group = QButtonGroup(self) # Do not gate samples self.no_gates = QRadioButton(self.gating_page) self.no_gates.setObjectName('no_gate') self.no_gates.setText("Don't gate samples") self.no_gates.setStyleSheet(self.style['radio button']) self.no_gates.setChecked(True) self.gating_group.addButton(self.no_gates) self.no_gates.clicked.connect(self.activate_gate) # CyToF gating self.cytof_gates = QRadioButton(self.gating_page) self.cytof_gates.setObjectName('cytof') self.cytof_gates.setText('Mass Cytometry gating') self.cytof_gates.setStyleSheet(self.style['radio button']) self.cytof_gates.setToolTip( 'Exclude cells for which the average expression of all\n' 'markers is below the selected value') self.gating_group.addButton(self.cytof_gates) self.cytof_gates.clicked.connect(self.activate_gate) # CyToF gating spinbox self.cytof_gates_value = QDoubleSpinBox(self.gating_page) self.cytof_gates_value.setMinimum(0) self.cytof_gates_value.setMaximum(1) self.cytof_gates_value.setValue(0.1) self.cytof_gates_value.setSingleStep(0.05) self.cytof_gates_value.setEnabled(False) # scRNA-Seq gating self.rnaseq_gates = QRadioButton(self.gating_page) self.rnaseq_gates.setText('scRNA-Seq gating') self.rnaseq_gates.setStyleSheet(self.style['radio button']) self.rnaseq_gates.setToolTip( 'When calculating cutoff, ignore reads below the selected value') self.rnaseq_gates.setObjectName('rnaseq') self.gating_group.addButton(self.rnaseq_gates) self.rnaseq_gates.clicked.connect(self.activate_gate) # scRNA-Seq gating spinbox self.rnaseq_gates_value = QDoubleSpinBox(self.gating_page) self.rnaseq_gates_value.setMinimum(0) self.rnaseq_gates_value.setMaximum(10) self.rnaseq_gates_value.setValue(0) self.rnaseq_gates_value.setSingleStep(1) self.rnaseq_gates_value.setEnabled(False) # export gated population checkbox self.export_gated = QCheckBox(self.gating_page) self.export_gated.setText('Export gated cells as an output file') self.export_gated.setStyleSheet(self.style['checkbox']) self.export_gated.setEnabled(False) # Add widgets above to Gate frame layout self.gate_frame.layout().addRow(self.no_gates, QLabel()) self.gate_frame.layout().addRow(self.cytof_gates, self.cytof_gates_value) self.gate_frame.layout().addRow(self.rnaseq_gates, self.rnaseq_gates_value) self.gate_frame.layout().addRow(self.export_gated, QLabel()) # ## Outlier options section # Outlier header self.outlier_header = QLabel(self.gating_page) self.outlier_header.setText('Outliers') self.outlier_header.setStyleSheet(self.style['header']) self.outlier_header.adjustSize() self.gating_layout.addWidget(self.outlier_header) # Outlier frame self.outlier_frame = QFrame(self.gating_page) self.outlier_frame.setFrameShape(QFrame.StyledPanel) self.outlier_frame.setLayout(QVBoxLayout()) self.gating_layout.addWidget(self.outlier_frame) # Top outliers information self.top_outliers = QLabel(self.gating_page) self.top_outliers.setStyleSheet(self.style['label']) self.top_outliers.setText( 'By default, SCOUTS selects the top outliers from the population') self.top_outliers.setStyleSheet(self.style['label']) # Bottom outliers data self.bottom_outliers = QCheckBox(self.gating_page) self.bottom_outliers.setText('Include results for low outliers') self.bottom_outliers.setStyleSheet(self.style['checkbox']) # Non-outliers data self.not_outliers = QCheckBox(self.gating_page) self.not_outliers.setText('Include results for non-outliers') self.not_outliers.setStyleSheet(self.style['checkbox']) # Add widgets above to Gate frame layout self.outlier_frame.layout().addWidget(self.top_outliers) self.outlier_frame.layout().addWidget(self.bottom_outliers) self.outlier_frame.layout().addWidget(self.not_outliers) # ## Save/back button self.save_gates = QPushButton(self.gating_page) self.set_icon(self.save_gates, 'go-next') self.save_gates.setText(' Back to main menu') self.save_gates.setStyleSheet(self.style['md button']) self.gating_layout.addWidget(self.save_gates) self.save_gates.clicked.connect(self.goto_main_page) # ## Add empty label to take vertical space self.empty_label = QLabel(self.gating_page) self.empty_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.gating_layout.addWidget(self.empty_label) # ### # ### ICON SETTING # ### def set_icon(self, widget: QWidget, icon: str) -> None: """Associates an icon to a widget.""" i = QIcon() i.addPixmap( QPixmap( os.path.abspath( os.path.join(self.rootdir, 'src', 'default_icons', f'{icon}.svg')))) widget.setIcon(QIcon.fromTheme(icon, i)) # ### # ### STACKED WIDGET PAGE SWITCHING # ### def goto_main_page(self) -> None: """Switches stacked widget pages to the main page.""" self.stacked_pages.setCurrentWidget(self.main_page) def goto_samples_page(self) -> None: """Switches stacked widget pages to the samples table page.""" self.stacked_pages.setCurrentWidget(self.samples_page) def goto_gates_page(self) -> None: """Switches stacked widget pages to the gating & other options page.""" self.stacked_pages.setCurrentWidget(self.gating_page) # ### # ### MAIN PAGE GUI LOGIC # ### def get_path(self) -> None: """Opens a dialog box and sets the chosen file/folder path, depending on the caller widget.""" options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog sender_name = self.sender().objectName() if sender_name == 'input': query, _ = QFileDialog.getOpenFileName(self, "Select file", "", "All Files (*)", options=options) elif sender_name == 'output': query = QFileDialog.getExistingDirectory(self, "Select Directory", options=options) else: return if query: getattr(self, f'{sender_name}_path').setText(query) def enable_single_excel(self) -> None: """Enables checkbox for generating a single Excel output.""" if self.output_excel.isChecked(): self.single_excel.setEnabled(True) else: self.single_excel.setEnabled(False) self.single_excel.setChecked(False) # ### # ### SAMPLE NAME/SAMPLE TABLE GUI LOGIC # ### def write_to_sample_table(self) -> None: """Writes data to sample table.""" table = self.sample_table ref = 'no' sample = self.sample_name.text() if sample: for cell in range(table.rowCount()): item = table.item(cell, 0) if item.text() == sample: self.same_sample() return if self.is_reference.isChecked(): for cell in range(table.rowCount()): item = table.item(cell, 1) if item.text() == 'yes': self.more_than_one_reference() return ref = 'yes' sample = QTableWidgetItem(sample) is_reference = QTableWidgetItem(ref) is_reference.setFlags(Qt.ItemIsEnabled) row_position = table.rowCount() table.insertRow(row_position) table.setItem(row_position, 0, sample) table.setItem(row_position, 1, is_reference) self.is_reference.setChecked(False) self.sample_name.setText('') def remove_from_sample_table(self) -> None: """Removes data from sample table.""" table = self.sample_table rows = set(index.row() for index in table.selectedIndexes()) for index in sorted(rows, reverse=True): self.sample_table.removeRow(index) def prompt_clear_data(self) -> None: """Prompts option to clear all data in the sample table.""" if self.confirm_clear_data(): table = self.sample_table while table.rowCount(): self.sample_table.removeRow(0) # ### # ### GATING GUI LOGIC # ### def activate_gate(self) -> None: """Activates/deactivates buttons related to gating.""" if self.sender().objectName() == 'no_gate': self.cytof_gates_value.setEnabled(False) self.rnaseq_gates_value.setEnabled(False) self.export_gated.setEnabled(False) self.export_gated.setChecked(False) elif self.sender().objectName() == 'cytof': self.cytof_gates_value.setEnabled(True) self.rnaseq_gates_value.setEnabled(False) self.export_gated.setEnabled(True) elif self.sender().objectName() == 'rnaseq': self.cytof_gates_value.setEnabled(False) self.rnaseq_gates_value.setEnabled(True) self.export_gated.setEnabled(True) # ### # ### CONNECT SCOUTS TO ANALYTICAL MODULES # ### def run(self) -> None: """Runs SCOUTS as a Worker, based on user input in the GUI.""" try: data = self.parse_input() except Exception as error: trace = traceback.format_exc() self.propagate_error((error, trace)) else: data['widget'] = self worker = Worker(func=start_scouts, **data) worker.signals.started.connect(self.analysis_has_started) worker.signals.finished.connect(self.analysis_has_finished) worker.signals.success.connect(self.success_message) worker.signals.error.connect(self.propagate_error) self.threadpool.start(worker) def parse_input(self) -> Dict: """Returns user input on the GUI as a dictionary.""" # Input and output input_dict = { 'input_file': str(self.input_path.text()), 'output_folder': str(self.output_path.text()) } if not input_dict['input_file'] or not input_dict['output_folder']: raise NoIOPathError # Set cutoff by reference or by sample rule input_dict['cutoff_rule'] = self.cutoff_group.checkedButton( ).objectName() # 'sample', 'ref', 'sample ref' # Outliers for each individual marker or any marker in row input_dict['marker_rule'] = self.markers_group.checkedButton( ).objectName() # 'single', 'any', 'single any' # Tukey factor used for calculating cutoff input_dict['tukey_factor'] = float( self.tukey_group.checkedButton().text()) # '1.5', '3.0' # Output settings input_dict['export_csv'] = True if self.output_csv.isChecked( ) else False input_dict['export_excel'] = True if self.output_excel.isChecked( ) else False input_dict['single_excel'] = True if self.single_excel.isChecked( ) else False # Retrieve samples from sample table input_dict['sample_list'] = [] for tuples in self.yield_samples_from_table(): input_dict['sample_list'].append(tuples) if not input_dict['sample_list']: raise NoSampleError # Set gate cutoff (if any) input_dict['gating'] = self.gating_group.checkedButton().objectName( ) # 'no_gate', 'cytof', 'rnaseq' input_dict['gate_cutoff_value'] = None if input_dict['gating'] != 'no_gate': input_dict['gate_cutoff_value'] = getattr( self, f'{input_dict["gating"]}_gates_value').value() input_dict['export_gated'] = True if self.export_gated.isChecked( ) else False # Generate results for non-outliers input_dict['non_outliers'] = False if self.not_outliers.isChecked(): input_dict['non_outliers'] = True # Generate results for bottom outliers input_dict['bottom_outliers'] = False if self.bottom_outliers.isChecked(): input_dict['bottom_outliers'] = True # return dictionary with all gathered inputs return input_dict def yield_samples_from_table( self) -> Generator[Tuple[str, str], None, None]: """Yields sample names from the sample table.""" table = self.sample_table for cell in range(table.rowCount()): sample_name = table.item(cell, 0).text() sample_type = table.item(cell, 1).text() yield sample_name, sample_type # ### # ### MESSAGE BOXES # ### def analysis_has_started(self) -> None: """Disables run button while SCOUTS analysis is underway.""" self.run_button.setText(' Working...') self.run_button.setEnabled(False) def analysis_has_finished(self) -> None: """Enables run button after SCOUTS analysis has finished.""" self.run_button.setEnabled(True) self.run_button.setText(' Run!') def success_message(self) -> None: """Info message box used when SCOUTS finished without errors.""" title = "Analysis finished!" mes = "Your analysis has finished. No errors were reported." if self.stacked_pages.isEnabled() is True: QMessageBox.information(self, title, mes) def memory_warning(self) -> None: """Warning message box used when user wants to generate a single excel file.""" if self.sender().isChecked(): title = 'Memory warning!' mes = ( "Depending on your dataset, this option can consume a LOT of memory and take" " a long time to process. Please make sure that your computer can handle it!" ) QMessageBox.information(self, title, mes) def same_sample(self) -> None: """Error message box used when the user tries to input the same sample twice in the sample table.""" title = 'Error: sample name already in table' mes = ( "Sorry, you can't do this because this sample name is already in the table. " "Please select a different name.") QMessageBox.critical(self, title, mes) def more_than_one_reference(self) -> None: """Error message box used when the user tries to input two reference samples in the sample table.""" title = "Error: more than one reference selected" mes = ( "Sorry, you can't do this because there is already a reference column in the table. " "Please remove it before adding a reference.") QMessageBox.critical(self, title, mes) def confirm_clear_data(self) -> bool: """Question message box used to confirm user action of clearing sample table.""" title = 'Confirm Action' mes = "Table will be cleared. Are you sure?" reply = QMessageBox.question(self, title, mes, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: return True return False # ### # ### EXCEPTIONS & ERRORS # ### def propagate_error(self, error: Tuple[Exception, str]) -> None: """Calls the appropriate error message box based on type of Exception raised.""" if isinstance(error[0], NoIOPathError): self.no_io_path_error_message() elif isinstance(error[0], NoReferenceError): self.no_reference_error_message() elif isinstance(error[0], NoSampleError): self.no_sample_error_message() elif isinstance(error[0], PandasInputError): self.pandas_input_error_message() elif isinstance(error[0], SampleNamingError): self.sample_naming_error_message() else: self.generic_error_message(error) def no_io_path_error_message(self) -> None: """Message displayed when the user did not include an input file path, or an output folder path.""" title = 'Error: no file/folder' message = ("Sorry, no input file and/or output folder was provided. " "Please add the path to the necessary file/folder.") QMessageBox.critical(self, title, message) def no_reference_error_message(self) -> None: """Message displayed when the user wants to analyse cutoff based on a reference, but did not specify what sample corresponds to the reference.""" title = "Error: No reference selected" message = ( "Sorry, no reference sample was found on the sample list, but analysis was set to " "reference. Please add a reference sample, or change the rule for cutoff calculation." ) QMessageBox.critical(self, title, message) def no_sample_error_message(self) -> None: """Message displayed when the user did not add any samples to the sample table.""" title = "Error: No samples selected" message = ( "Sorry, the analysis cannot be performed because no sample names were input. " "Please add your sample names.") QMessageBox.critical(self, title, message) def pandas_input_error_message(self) -> None: """Message displayed when the input file cannot be read (likely because it is not a Excel or csv file).""" title = 'Error: unexpected input file' message = ( "Sorry, the input file could not be read. Please make sure that " "the data is save in a valid format (supported formats are: " ".csv, .xlsx).") QMessageBox.critical(self, title, message) def sample_naming_error_message(self) -> None: """Message displayed when none of the sample names passed by the user are found in the input DataFrame.""" title = 'Error: sample names not in input file' message = ( "Sorry, your sample names were not found in the input file. Please " "make sure that the names were typed correctly (case-sensitive).") QMessageBox.critical(self, title, message) def generic_error_message(self, error: Tuple[Exception, str]) -> None: """Error message box used to display any error message (including traceback) for any uncaught errors.""" title = 'An error occurred!' name, trace = error QMessageBox.critical(self, title, f"{str(name)}\n\nfull traceback:\n{trace}") def not_implemented_error_message(self) -> None: """Error message box used when the user accesses a functionality that hasn't been implemented yet.""" title = "Not yet implemented" mes = "Sorry, this functionality has not been implemented yet." QMessageBox.critical(self, title, mes) # ### # ### HELP & QUIT # ### @staticmethod def get_help() -> None: """Opens SCOUTS documentation on the browser. Called when the user clicks the "help" button""" webbrowser.open('https://scouts.readthedocs.io/en/master/') def closeEvent(self, event: QEvent) -> None: """Defines the message box for when the user wants to quit SCOUTS.""" title = 'Quit SCOUTS' mes = "Are you sure you want to quit?" reply = QMessageBox.question(self, title, mes, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self.stacked_pages.setEnabled(False) message = self.quit_message() waiter = Waiter(waiter_func=self.threadpool.activeThreadCount) waiter.signals.started.connect(message.show) waiter.signals.finished.connect(message.destroy) waiter.signals.finished.connect(sys.exit) self.threadpool.start(waiter) event.ignore() def quit_message(self) -> QDialog: """Displays a window while SCOUTS is exiting""" message = QDialog(self) message.setWindowTitle('Exiting SCOUTS') message.resize(300, 50) label = QLabel('SCOUTS is exiting, please wait...', message) label.setStyleSheet(self.style['label']) label.adjustSize() label.setAlignment(Qt.AlignCenter) label.move(int((message.width() - label.width()) / 2), int((message.height() - label.height()) / 2)) return message
class Example(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): QToolTip.setFont(QFont('SansSerif', 12)) Title = QLabel('Выберите параметры обучения нейросети') Title.setToolTip('Для корректной работы функций обучения и оптимизации \n' 'в каталоге с программой должны находится подкаталоги \n' 'train, val, test. В каждом из этих каталогов должно \n' 'быть столько подкаталогов с изображениями, сколько \n' 'необходимо распознавать классов') Title1 = QLabel('Выберите параметры исследования изображения') Title1.setToolTip('Для корректной работы функции классификации и локализации \n' 'изображения для исследования и модель нейросети должны \n' 'находиться в одном каталоге с программой, формат изображения \n' '.jpg, формат модели нейросети .h5, указывать формат в полях не нужно') TitleEp = QLabel('Эпохи:') TitleEp.setToolTip('Эпоха — один «проход» данных через нейросеть. \n' 'Изменение этого параметра позволяет избежать \n' 'недообученности или переобученности нейросети') TitleBa = QLabel('Размер мини-выборки:') TitleBa.setToolTip('Количество изображений загружаемых в нейросеть ' 'за раз') TitleTr = QLabel('Количество изображений для обучения:') TitleTr.setToolTip('Количество изображений во всех подкаталогах ' 'каталога train') TitleVa = QLabel('Количество изображений для проверки:') TitleVa.setToolTip('Количество изображений во всех подкаталогах ' 'каталога val') TitleTe = QLabel('Количество изображений для тестирования:') TitleTe.setToolTip('Количество изображений во всех подкаталогах ' 'каталога test') TitleSc = QLabel('Масштабирование пирамиды изображений:') TitleSc.setToolTip('Коэффициент масштабирования изображения') TitleSs = QLabel('Шаг скользящего окна:') TitleSs.setToolTip('Расстояние в пикселях, на которое смещается \n' 'окно классификатора по изображению за одну итерацию') TitleNi = QLabel('Имя изображения для исследования:') TitleNi.setToolTip('Наименование изображения без расширения. \n' 'Изображение должно иметь формат jpg и \n' 'находится в одном каталоге с программой, иначе \n' 'необходимо указывать полный путь до изображения') TitleNm = QLabel('Имя модели нейросети:') TitleNm.setToolTip('Наименование модели нейросети без расширения. \n' 'Модель должна иметь формат h5 и находится \n' 'в одном каталоге с программой, иначе необходимо \n' 'указывать полный путь до модели нейросети') TitleCl = QLabel('Количество классов:') self.InputEpochs = QSpinBox(self) self.InputEpochs.setRange(5,50) self.InputEpochs.setValue(10) self.InputBatch = QSpinBox(self) self.InputBatch.setRange(5,100) self.InputBatch.setValue(15) self.InputTrain = QSpinBox(self) self.InputTrain.setRange(100,100000) self.InputTrain.setValue(1050) self.InputValidation = QSpinBox(self) self.InputValidation.setRange(100,100000) self.InputValidation.setValue(225) self.InputTest = QSpinBox(self) self.InputTest.setRange(100,100000) self.InputTest.setValue(225) self.InputScale = QDoubleSpinBox(self) self.InputScale.setRange(1.1,5) self.InputScale.setValue(1.5) self.InputStep = QSpinBox(self) self.InputStep.setRange(1,150) self.InputStep.setValue(30) self.InputClass = QSpinBox(self) self.InputClass.setRange(3,20) self.InputClass.setValue(3) self.ImageName = QLineEdit("ImageName", self) self.ImageName.setMaxLength(20) self.ModelName = QLineEdit("TestModel", self) self.ModelName.setMaxLength(20) btn = QPushButton('Обучить нейросеть', self) btn.setToolTip('В результате обучения модель нейросети ' 'будет сохранена в каталоге с программой') btn.resize(btn.sizeHint()) btn.clicked.connect(self.buttonClicked) btn1 = QPushButton('Оптимизировать нейросеть', self) btn1.setToolTip('В результате оптимизации 3 лучшие модели ' 'будут сохранены в каталоге с программой') btn1.resize(btn1.sizeHint()) btn1.clicked.connect(self.buttonClicked1) btn2 = QPushButton('Поиск и распознавание', self) btn2.resize(btn2.sizeHint()) btn2.clicked.connect(self.buttonClicked2) grid = QGridLayout(self) grid.setSpacing(10) grid.addWidget(Title, 1, 0, 1, 4) grid.addWidget(TitleEp, 2, 0) grid.addWidget(TitleBa, 3, 0) grid.addWidget(TitleCl, 4, 0) grid.addWidget(self.InputEpochs, 2, 1) grid.addWidget(self.InputBatch, 3, 1) grid.addWidget(self.InputClass, 4, 1) grid.addWidget(TitleTr, 2, 2) grid.addWidget(TitleVa, 3, 2) grid.addWidget(TitleTe, 4, 2) grid.addWidget(self.InputTrain, 2, 3) grid.addWidget(self.InputValidation, 3, 3) grid.addWidget(self.InputTest, 4, 3) grid.addWidget(btn, 5, 0, 1, 2) grid.addWidget(btn1, 5, 2, 1, 2) grid.addWidget(Title1, 6, 0, 1, 4) grid.addWidget(TitleSc, 7, 0) grid.addWidget(self.InputScale, 7, 1) grid.addWidget(TitleSs, 7, 2) grid.addWidget(self.InputStep, 7, 3) grid.addWidget(TitleNi, 8, 0) grid.addWidget(self.ImageName, 8, 1) grid.addWidget(TitleNm, 8, 2) grid.addWidget(self.ModelName, 8, 3) grid.addWidget(btn2, 9, 1, 1, 2) self.setLayout(grid) self.setWindowTitle('Система поиска и распознавания сорных растений') self.setWindowIcon(QIcon('icon.png')) self.show() def buttonClicked(self): train_dir = 'train' # Каталог с данными для обучения val_dir = 'val' # Каталог с данными для проверки test_dir = 'test' # Каталог с данными для тестирования img_width, img_height = 150, 150 # Размеры изображения input_shape = (img_width, img_height, 3) # Размерность тензора на основе изображения для входных данных в нейронную сеть epochs = self.InputEpochs.value() Nclasses = self.InputClass.value() batch_size = self.InputBatch.value() nb_train_samples = self.InputTrain.value() nb_validation_samples = self.InputValidation.value() nb_test_samples = self.InputTest.value() model = Sequential() model.add(Conv2D(32, (3, 3), input_shape=input_shape)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(64, (3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(64, (3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(128, (3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Flatten()) model.add(Dense(768)) model.add(Activation('selu')) model.add(Dropout(0.5)) model.add(Dense(Nclasses)) model.add(Activation('softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) datagen = ImageDataGenerator(rescale=1. / 255) train_generator = datagen.flow_from_directory( train_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='categorical') val_generator = datagen.flow_from_directory( val_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='categorical') test_generator = datagen.flow_from_directory( test_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='categorical') model.fit_generator( train_generator, steps_per_epoch=nb_train_samples // batch_size, epochs=epochs, validation_data=val_generator, validation_steps=nb_validation_samples // batch_size) scores = model.evaluate_generator(test_generator, nb_test_samples // batch_size) print("Аккуратность на тестовых данных: %.2f%%" % (scores[1]*100)) model.save('TestModel.h5') def buttonClicked1(self): train_dir = 'train' val_dir = 'val' test_dir = 'test' img_width, img_height = 150, 150 input_shape = (img_width, img_height, 3) epochs = self.InputEpochs.value() Nclasses = self.InputClass.value() batch_size = self.InputBatch.value() nb_train_samples = self.InputTrain.value() nb_validation_samples = self.InputValidation.value() nb_test_samples = self.InputTest.value() l=0 def build_model(hp): model = Sequential() num_hidden_layers = hp.Int('num_hidden_layers', 1, 3, default=1) num_conv_layers = hp.Int('num_conv_layers', 2, 6, default=2) model.add(Conv2D(32, (3, 3), input_shape=input_shape)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) for i in range(num_conv_layers): filters = hp.Int('filters'+str(i), 32, 64, step=16) model.add(Conv2D(filters,(3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(128, (3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Flatten()) for j in range(num_hidden_layers): model.add(Dense(units=hp.Int('units_hiddenNeurons_'+str(j), min_value=128, max_value=1024, step=64), activation=hp.Choice('activation'+str(j),values=['relu','tanh','elu','selu']))) model.add(Dropout(0.5)) model.add(Dense(Nclasses)) model.add(Activation('softmax')) model.compile( loss='categorical_crossentropy', optimizer=hp.Choice('optimizer', values=['adam','rmsprop','SGD'],default='adam'), metrics=['accuracy']) return model tuner = RandomSearch( build_model, objective='val_accuracy', max_trials=15, directory='test_directory') tuner.search_space_summary() datagen = ImageDataGenerator(rescale=1. / 255) train_generator = datagen.flow_from_directory( train_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='categorical') val_generator = datagen.flow_from_directory( val_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='categorical') test_generator = datagen.flow_from_directory( test_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='categorical') tuner.search( train_generator, steps_per_epoch=nb_train_samples // batch_size, epochs=epochs, validation_data=val_generator, validation_steps=nb_validation_samples // batch_size) tuner.results_summary() models = tuner.get_best_models(num_models=3) for model in models: model.summary() l=l+1 scores = model.evaluate_generator(test_generator, nb_test_samples // batch_size) model.save('bestmodel_'+str(l)+'.h5') print("Аккуратность на тестовых данных: %.2f%%" % (scores[1]*100)) def buttonClicked2(self): def pyramid(image, scale, minSize=(150, 150)): yield image while True: w = int(image.shape[1] / scale) image = imutils.resize(image, width=w) if image.shape[0] < minSize[1] or image.shape[1] < minSize[0]: break yield image def sliding_window(image, stepSize, windowSize): # сдвиг окна по изображению for y in range(0, image.shape[0], stepSize): for x in range(0, image.shape[1], stepSize): yield (x, y, image[y:y + windowSize[1], x:x + windowSize[0]]) # расстояние между точками def dist(x,y): return np.sqrt(np.sum(np.square(np.array(center(x))-np.array(center(y))))) # вычисляем точку-центр рамки def center(frame): (x, y, x2, y2, _ , _ , _ ) = frame return [ x+((x2-x)//2), y2+((y-y2)//2) ] # очистка списка найденных рамок def clean(X, i=0, max_dist=150): # выбираем основной элемент X[i] j = i+1 # и, следующий за ним, элемент X[j] if i <= len(X) and i > 0: max_dist *= X[i][6] while(j<len(X)): # для всех элементов j (!= i) d = dist( X[i], X[j] ) # считаем расстояние между точками i,j if ( d < max_dist ): # i,j в одном кластере if ( X[i][4] >= X[j][4] ): # сравниваем рейтинг del X[j] # удаляем элемент j else: del X[i] # удаляем основной элемент i X = clean(X, i=i, max_dist=max_dist) # рекурсивно повторяем уже без элемента i break else: j+=1 if ( i<(len(X)-1) ): # если есть ещё точки в других, относительно X[i], кластерах X = clean(X, i=i+1, max_dist=max_dist) # то выполняем проверку return X if __name__ == "__main__": k = 1 path = os.getcwd() classes = os.listdir(path + "/train") boxes = [] scale = self.InputScale.value() stepSize = self.InputStep.value() imagename = self.ImageName.text() modelname = self.ModelName.text() font = cv2.FONT_HERSHEY_SIMPLEX model = load_model(modelname+'.h5') img = cv2.imread(imagename+'.jpg') width1, height1, channels1 = img.shape if (width1 > 1000) or (height1 > 1000): img = cv2.resize(img, (0, 0), fx = 0.5, fy = 0.5) (winW, winH) = (150, 150) clone = img.copy() for resized in pyramid(img, scale): for (x, y, window) in sliding_window(resized, stepSize, windowSize=(winW, winH)): # если окно не соответствует нашему желаемому размеру окна, игнорируем его if window.shape[0] != winH or window.shape[1] != winW: continue img_tensor = image.img_to_array(window) # (height, width, channels) img_tensor = np.expand_dims(img_tensor, axis=0) # (1, height, width, channels), добавим измерение, потому что модель ожидает эту форму: (batch_size, height, width, channels) img_tensor /= 255. # imshow ожидает значений в диапазоне [0, 1] pred = model.predict(img_tensor) nclass = np.argmax(pred) pred = np.amax(pred) if pred >= 0.9: boxes.append([x*k, y*k, (x + winW)*k, (y + winH)*k, pred, classes[nclass], k]) k *= scale boxes = clean(boxes) for (x, y, x2, y2, score, classname, scales) in boxes: cv2.rectangle(clone, (int(x),int(y)),(int(x2), int(y2)),(0, 255, 0), 2) cv2.putText(clone, classname + " %.3f" % score, (int(x),int(y2)), font, 0.7, (0, 255, 0), thickness=2) cv2.imshow("Result", clone) cv2.waitKey(1) def closeEvent(self, event): reply = QMessageBox.question(self, 'Выход', "Вы уверены что хотите закрыть программу?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore()
class NGL_HKLViewer(QWidget): def __init__(self, parent=None): super(NGL_HKLViewer, self).__init__(parent) self.verbose = 0 self.UseOSbrowser = False self.jscriptfname = "" self.devmode = False for e in sys.argv: if "verbose" in e: self.verbose = e.split("verbose=")[1] if "UseOSbrowser" in e: self.UseOSbrowser = e.split("UseOSbrowser=")[1] if "jscriptfname" in e: self.jscriptfname = e.split("jscriptfname=")[1] if "devmode" in e: self.devmode = True self.zmq_context = None self.bufsize = 20000 self.originalPalette = QApplication.palette() self.openFileNameButton = QPushButton("Load reflection file") self.openFileNameButton.setDefault(True) self.openFileNameButton.clicked.connect(self.OpenReflectionsFile) self.debugbutton = QPushButton("Debug") self.debugbutton.clicked.connect(self.DebugInteractively) self.settingsbtn = QPushButton("Settings") self.settingsbtn.clicked.connect(self.SettingsDialog) self.mousemoveslider = QSlider(Qt.Horizontal) self.mousemoveslider.setMinimum(0) self.mousemoveslider.setMaximum(300) self.mousemoveslider.setValue(0) self.mousemoveslider.sliderReleased.connect( self.onFinalMouseSensitivity) self.mousemoveslider.valueChanged.connect(self.onMouseSensitivity) self.mousesensitxtbox = QLineEdit('') self.mousesensitxtbox.setReadOnly(True) self.fontspinBox = QDoubleSpinBox() self.fontspinBox.setSingleStep(1) self.fontspinBox.setRange(4, 50) self.font = QFont() self.font.setFamily(self.font.defaultFamily()) self.fontspinBox.setValue(self.font.pointSize()) #self.fontspinBox.setValue(self.font.pixelSize()) self.fontspinBox.valueChanged.connect(self.onFontsizeChanged) self.Fontsize_labeltxt = QLabel() self.Fontsize_labeltxt.setText("Font size:") self.cameraPerspectCheckBox = QCheckBox() self.cameraPerspectCheckBox.setText("Perspective camera") self.cameraPerspectCheckBox.clicked.connect(self.onCameraPerspect) self.cameraPerspectCheckBox.setCheckState(Qt.Unchecked) self.settingsform = SettingsForm(self) self.MillerComboBox = QComboBox() self.MillerComboBox.activated.connect(self.onMillerComboSelchange) #self.MillerComboBox.setSizeAdjustPolicy(QComboBox.AdjustToContents) self.MillerLabel = QLabel() self.MillerLabel.setText("Selected HKL Scene") self.HKLnameedit = QLineEdit('') self.HKLnameedit.setReadOnly(True) self.textInfo = QTextEdit() self.textInfo.setLineWrapMode(QTextEdit.NoWrap) self.textInfo.setReadOnly(True) labels = [ "Label", "Type", "no. of HKLs", "Span of HKLs", "Min Max data", "Min Max sigmas", "d_min, d_max", "Symmetry unique", "Anomalous" ] self.millertable = QTableWidget(0, len(labels)) self.millertable.setHorizontalHeaderLabels(labels) self.millertable.horizontalHeader().setDefaultAlignment(Qt.AlignLeft) # don't allow editing this table self.millertable.setEditTriggers(QTableWidget.NoEditTriggers) self.createExpansionBox() self.createFileInfoBox() self.CreateSliceTabs() self.createRadiiScaleGroupBox() self.createBinsBox() self.CreateFunctionTabs() mainLayout = QGridLayout() mainLayout.addWidget(self.FileInfoBox, 0, 0) mainLayout.addWidget(self.MillerLabel, 1, 0) mainLayout.addWidget(self.MillerComboBox, 2, 0) mainLayout.addWidget(self.functionTabWidget, 3, 0) mainLayout.addWidget(self.settingsbtn, 4, 0, 1, 1) #import code, traceback; code.interact(local=locals(), banner="".join( traceback.format_stack(limit=10) ) ) if self.UseOSbrowser == False: self.BrowserBox = QWebEngineView() mainLayout.addWidget(self.BrowserBox, 0, 1, 5, 3) self.BrowserBox.setUrl("https://cctbx.github.io/") #self.BrowserBox.setUrl("https://webglreport.com/") #self.BrowserBox.loadFinished.connect(self.onLoadFinished) mainLayout.setColumnStretch(2, 1) mainLayout.setRowStretch(0, 1) mainLayout.setRowStretch(1, 0) mainLayout.setRowStretch(2, 1) mainLayout.setRowStretch(3, 1) mainLayout.setColumnStretch(4, 0) self.setLayout(mainLayout) self.setWindowTitle("HKL-Viewer") self.cctbxproc = None self.LaunchCCTBXPython() self.out = None self.err = None self.comboviewwidth = 0 self.hklscenes_arrays = [] self.array_infotpls = [] self.matching_arrays = [] self.bin_infotpls = None self.bin_opacities = None self.html_url = "" self.spacegroups = [] self.info = [] self.infostr = "" self.fileisvalid = False self.NewFileLoaded = False self.NewHKLscenes = False self.updatingNbins = False self.binstableitemchanges = False self.show() def SettingsDialog(self): self.settingsform.show() def update(self): if self.cctbxproc: if self.cctbxproc.stdout: print(self.cctbxproc.stdout.read().decode("utf-8")) if self.cctbxproc.stderr: print(self.cctbxproc.stderr.read().decode("utf-8")) if self.out: print(self.out.decode("utf-8")) if self.err: print(self.err.decode("utf-8")) if self.zmq_context: try: msg = self.socket.recv( flags=zmq.NOBLOCK ) #To empty the socket from previous messages msgstr = msg.decode() self.infodict = eval(msgstr) #print("received from cctbx: " + str(self.infodict)) if self.infodict: if self.infodict.get("hklscenes_arrays"): self.hklscenes_arrays = self.infodict.get( "hklscenes_arrays", []) if self.infodict.get("array_infotpls"): self.array_infotpls = self.infodict.get( "array_infotpls", []) if self.infodict.get("bin_data_label"): self.BinDataComboBox.setCurrentText( self.infodict["bin_data_label"]) if self.infodict.get("bin_infotpls"): self.bin_infotpls = self.infodict["bin_infotpls"] self.nbins = len(self.bin_infotpls) self.updatingNbins = True self.Nbins_spinBox.setValue(self.nbins) self.updatingNbins = False self.binstable.clearContents() self.binstable.setRowCount(self.nbins) for row, bin_infotpl in enumerate(self.bin_infotpls): for col, elm in enumerate(bin_infotpl): # only allow changing the last column with opacity values if col != 3: item = QTableWidgetItem(str(elm)) else: item = QTableWidgetItem() item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setCheckState(Qt.Checked) item.setFlags(item.flags() ^ Qt.ItemIsEditable) self.binstable.setItem(row, col, item) if self.bin_opacities: self.update_table_opacities() if self.infodict.get("bin_opacities"): self.bin_opacities = self.infodict["bin_opacities"] if self.binstable.rowCount() > 0: self.update_table_opacities() if self.infodict.get("html_url"): self.html_url = self.infodict["html_url"] if self.UseOSbrowser == False: self.BrowserBox.setUrl(self.html_url) # workaround for background colour bug in chromium # https://bugreports.qt.io/browse/QTBUG-41960 self.BrowserBox.page().setBackgroundColor( QColor(100, 100, 100, 1.0)) if self.infodict.get("spacegroups"): self.spacegroups = self.infodict.get("spacegroups", []) self.SpaceGroupComboBox.clear() self.SpaceGroupComboBox.addItems(self.spacegroups) if self.infodict.get("merge_data"): self.mergedata = self.infodict["merge_data"] currentinfostr = "" if self.infodict.get("info"): currentinfostr = self.infodict.get("info", []) if self.infodict.get("NewFileLoaded"): self.NewFileLoaded = self.infodict.get( "NewFileLoaded", False) if self.infodict.get("NewHKLscenes"): self.NewHKLscenes = self.infodict.get( "NewHKLscenes", False) self.fileisvalid = True #print("ngl_hkl_infodict: " + str(ngl_hkl_infodict)) if currentinfostr: #print(currentinfostr) self.infostr += currentinfostr + "\n" # display no more than self.bufsize bytes of text self.infostr = self.infostr[-self.bufsize:] self.textInfo.setPlainText(self.infostr) self.textInfo.verticalScrollBar().setValue( self.textInfo.verticalScrollBar().maximum()) if self.NewFileLoaded and self.NewHKLscenes: #if self.mergedata == True : val = Qt.CheckState.Checked #if self.mergedata == None : val = Qt.CheckState.PartiallyChecked #if self.mergedata == False : val = Qt.CheckState.Unchecked #self.mergecheckbox.setCheckState(val ) #print("got hklscenes: " + str(self.hklscenes_arrays)) self.MillerComboBox.clear() self.MillerComboBox.addItems( [e[3] for e in self.hklscenes_arrays]) self.MillerComboBox.setCurrentIndex( -1) # unselect the first item in the list self.comboviewwidth = 0 for e in self.hklscenes_arrays: self.comboviewwidth = max( self.comboviewwidth, self.MillerComboBox.fontMetrics().width(e[3])) self.MillerComboBox.view().setMinimumWidth( self.comboviewwidth) self.millertable.clearContents() self.millertable.setRowCount(len( self.hklscenes_arrays)) for n, millarr in enumerate(self.array_infotpls): for m, elm in enumerate(millarr): self.millertable.setItem( n, m, QTableWidgetItem(str(elm))) self.functionTabWidget.setDisabled(True) self.NewFileLoaded = False if self.NewHKLscenes: self.BinDataComboBox.clear() self.BinDataComboBox.addItems( ["Resolution"] + [e[3] for e in self.hklscenes_arrays]) self.BinDataComboBox.view().setMinimumWidth( self.comboviewwidth) #self.BinDataComboBox.setCurrentIndex(-1) # unselect the first item in the list self.NewHKLscenes = False except Exception as e: errmsg = str(e) if "Resource temporarily unavailable" not in errmsg: print(errmsg + traceback.format_exc(limit=10)) pass def onFinalMouseSensitivity(self): val = self.mousemoveslider.value() / 100.0 self.NGL_HKL_command( 'NGL_HKLviewer.viewer.NGL.mouse_sensitivity = %f' % val) def onMouseSensitivity(self): val = self.mousemoveslider.value() / 100.0 self.mousesensitxtbox.setText("%2.2f" % val) def onFontsizeChanged(self, val): font = app.font() font.setPointSize(val) app.setFont(font) self.settingsform.setFixedSize(self.settingsform.sizeHint()) def onCameraPerspect(self, val): if self.cameraPerspectCheckBox.isChecked(): self.NGL_HKL_command("NGL_HKLviewer.camera_type = perspective") else: self.NGL_HKL_command("NGL_HKLviewer.camera_type = orthographic") def MergeData(self): if self.mergecheckbox.checkState() == Qt.CheckState.Checked: self.NGL_HKL_command('NGL_HKLviewer.mergedata = True') if self.mergecheckbox.checkState() == Qt.CheckState.PartiallyChecked: self.NGL_HKL_command('NGL_HKLviewer.mergedata = None') if self.mergecheckbox.checkState() == Qt.CheckState.Unchecked: self.NGL_HKL_command('NGL_HKLviewer.mergedata = False') def ExpandToP1(self): if self.expandP1checkbox.isChecked(): self.NGL_HKL_command('NGL_HKLviewer.viewer.expand_to_p1 = True') else: self.NGL_HKL_command('NGL_HKLviewer.viewer.expand_to_p1 = False') def ExpandAnomalous(self): if self.expandAnomalouscheckbox.isChecked(): self.NGL_HKL_command( 'NGL_HKLviewer.viewer.expand_anomalous = True') else: self.NGL_HKL_command( 'NGL_HKLviewer.viewer.expand_anomalous = False') def showSysAbsent(self): if self.sysabsentcheckbox.isChecked(): self.NGL_HKL_command( 'NGL_HKLviewer.viewer.show_systematic_absences = True') else: self.NGL_HKL_command( 'NGL_HKLviewer.viewer.show_systematic_absences = False') def showMissing(self): if self.missingcheckbox.isChecked(): self.NGL_HKL_command('NGL_HKLviewer.viewer.show_missing = True') else: self.NGL_HKL_command('NGL_HKLviewer.viewer.show_missing = False') def showOnlyMissing(self): if self.onlymissingcheckbox.isChecked(): self.NGL_HKL_command( 'NGL_HKLviewer.viewer.show_only_missing = True') else: self.NGL_HKL_command( 'NGL_HKLviewer.viewer.show_only_missing = False') def showSlice(self): if self.showslicecheckbox.isChecked(): self.NGL_HKL_command('NGL_HKLviewer.viewer.slice_mode = True') if self.expandP1checkbox.isChecked(): self.NGL_HKL_command("""NGL_HKLviewer.viewer { expand_to_p1 = True inbrowser = False } """) if self.expandAnomalouscheckbox.isChecked(): self.NGL_HKL_command("""NGL_HKLviewer.viewer { expand_anomalous = True inbrowser = False } """) else: self.NGL_HKL_command("""NGL_HKLviewer.viewer { slice_mode = False inbrowser = True } """) def onSliceComboSelchange(self, i): rmin = self.array_infotpls[self.MillerComboBox.currentIndex()][3][0][i] rmax = self.array_infotpls[self.MillerComboBox.currentIndex()][3][1][i] self.sliceindexspinBox.setRange(rmin, rmax) self.NGL_HKL_command("NGL_HKLviewer.viewer.slice_axis = %s" % self.sliceaxis[i]) def onSliceIndexChanged(self, val): self.sliceindex = val self.NGL_HKL_command("NGL_HKLviewer.viewer.slice_index = %d" % self.sliceindex) def onBindataComboSelchange(self, i): if self.BinDataComboBox.currentText(): if self.BinDataComboBox.currentIndex() > 0: bin_scene_label = str(self.BinDataComboBox.currentIndex() - 1) else: bin_scene_label = "Resolution" self.NGL_HKL_command("NGL_HKLviewer.bin_scene_label = %s" % bin_scene_label) def update_table_opacities(self, allalpha=None): bin_opacitieslst = eval(self.bin_opacities) self.binstable_isready = False for binopacity in bin_opacitieslst: if not allalpha: alpha = float(binopacity.split(",")[0]) else: alpha = allalpha bin = int(binopacity.split(",")[1]) item = QTableWidgetItem() item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) if alpha < 0.5: item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) item.setFlags(item.flags() ^ Qt.ItemIsEditable) self.binstable.setItem(bin, 3, item) self.binstable_isready = True def SetOpaqueAll(self): if self.binstableitemchanges: return bin_opacitieslst = eval(self.bin_opacities) nbins = len(bin_opacitieslst) sum = 0 for binopacity in bin_opacitieslst: sum += float(binopacity.split(",")[0]) if sum >= nbins: self.OpaqueAllCheckbox.setCheckState(Qt.Checked) if sum == 0: self.OpaqueAllCheckbox.setCheckState(Qt.Unchecked) if sum > 0.0 and sum < nbins: self.OpaqueAllCheckbox.setCheckState(Qt.PartiallyChecked) def onBinsTableItemChanged(self, item): row = item.row() column = item.column() try: if item.checkState() == Qt.Unchecked: newval = 0 else: newval = 1.0 if column == 3 and self.binstable_isready: # changing opacity assert (newval <= 1.0 and newval >= 0.0) bin_opacitieslst = eval(self.bin_opacities) bin_opacitieslst[row] = str(newval) + ', ' + str(row) self.bin_opacities = str(bin_opacitieslst) self.SetOpaqueAll() self.NGL_HKL_command( 'NGL_HKLviewer.viewer.NGL.bin_opacities = "%s"' % self.bin_opacities) except Exception as e: print(str(e)) #self.binstable.currentItem().setText( self.currentSelectedBinsTableVal) def onBinsTableItemSelectionChanged(self): row = self.binstable.currentItem().row() column = self.binstable.currentItem().column() self.currentSelectedBinsTableVal = self.binstable.currentItem().text() #print( "in itemSelectionChanged " + self.currentSelectedBinsTableVal) def onOpaqueAll(self): self.binstableitemchanges = True bin_opacitieslst = eval(self.bin_opacities) nbins = len(bin_opacitieslst) bin_opacitieslst = [] self.binstable_isready = False if self.OpaqueAllCheckbox.isChecked(): for i in range(nbins): bin_opacitieslst.append("1.0, %d" % i) else: for i in range(nbins): bin_opacitieslst.append("0.0, %d" % i) self.bin_opacities = str(bin_opacitieslst) self.NGL_HKL_command('NGL_HKLviewer.viewer.NGL.bin_opacities = "%s"' % self.bin_opacities) self.binstableitemchanges = False self.binstable_isready = True """ def onLoadFinished(self, val): pass #print("web page finished loading now") def onBinsTableitemActivated(self, item): row = item.row() column = item.column() currentval = item.text() #print( "in itemActivated " + currentval) def onBinsTableCellentered(self, row, col): pass #print( "in Cellentered " + self.binstable.currentItem().text() ) def onBinsTableCellPressed(self, row, col): pass #print( "in CellPressed " + self.binstable.currentItem().text() ) """ def onNbinsChanged(self, val): self.nbins = val if not self.updatingNbins: # avoid possible endless loop to cctbx self.NGL_HKL_command("NGL_HKLviewer.nbins = %d" % self.nbins) def onRadiiScaleChanged(self, val): self.radii_scale = val self.NGL_HKL_command(""" NGL_HKLviewer.viewer { nth_power_scale_radii = %f scale = %f } """ % (self.nth_power_scale, self.radii_scale)) def onPowerScaleChanged(self, val): self.nth_power_scale = val self.NGL_HKL_command(""" NGL_HKLviewer.viewer { nth_power_scale_radii = %f scale = %f } """ % (self.nth_power_scale, self.radii_scale)) def onManualPowerScale(self): if self.ManualPowerScalecheckbox.isChecked(): self.NGL_HKL_command( 'NGL_HKLviewer.viewer.nth_power_scale_radii = %f' % self.nth_power_scale) self.power_scale_spinBox.setEnabled(True) else: self.NGL_HKL_command( 'NGL_HKLviewer.viewer.nth_power_scale_radii = -1.0') self.power_scale_spinBox.setEnabled(False) self.nth_power_scale = -1.0 def OpenReflectionsFile(self): options = QFileDialog.Options() fileName, filtr = QFileDialog.getOpenFileName( self, "Load reflections file", "", "All Files (*);;MTZ Files (*.mtz);;CIF (*.cif)", "", options) if fileName: self.HKLnameedit.setText(fileName) #self.infostr = "" self.textInfo.setPlainText("") self.fileisvalid = False self.NGL_HKL_command('NGL_HKLviewer.filename = "%s"' % fileName) self.MillerComboBox.clear() self.BinDataComboBox.clear() def createExpansionBox(self): self.SpaceGroupComboBox = QComboBox() self.SpaceGroupComboBox.activated.connect(self.SpacegroupSelchange) self.SpacegroupLabel = QLabel() self.SpacegroupLabel.setText("Space Subgroups") self.mergecheckbox = QCheckBox() self.mergecheckbox.setText("Merge data") #self.mergecheckbox.setTristate (True) self.mergecheckbox.clicked.connect(self.MergeData) self.expandP1checkbox = QCheckBox() self.expandP1checkbox.setText("Expand to P1") self.expandP1checkbox.clicked.connect(self.ExpandToP1) self.expandAnomalouscheckbox = QCheckBox() self.expandAnomalouscheckbox.setText("Show Friedel pairs") self.expandAnomalouscheckbox.clicked.connect(self.ExpandAnomalous) self.sysabsentcheckbox = QCheckBox() self.sysabsentcheckbox.setText("Show Systematic Absences") self.sysabsentcheckbox.clicked.connect(self.showSysAbsent) self.missingcheckbox = QCheckBox() self.missingcheckbox.setText("Show Missing") self.missingcheckbox.clicked.connect(self.showMissing) self.onlymissingcheckbox = QCheckBox() self.onlymissingcheckbox.setText("Only Show Missing") self.onlymissingcheckbox.clicked.connect(self.showOnlyMissing) self.ExpansionBox = QGroupBox("Expansions") layout = QGridLayout() layout.addWidget(self.SpacegroupLabel, 0, 0) layout.addWidget(self.SpaceGroupComboBox, 0, 1) #layout.addWidget(self.mergecheckbox, 1, 0) layout.addWidget(self.expandP1checkbox, 1, 0) layout.addWidget(self.expandAnomalouscheckbox, 1, 1) layout.addWidget(self.sysabsentcheckbox, 2, 0) layout.addWidget(self.missingcheckbox, 3, 0) layout.addWidget(self.onlymissingcheckbox, 3, 1) layout.setRowStretch(0, 0) layout.setRowStretch(1, 0) layout.setRowStretch(2, 0) layout.setRowStretch(3, 1) self.ExpansionBox.setLayout(layout) def CreateSliceTabs(self): self.showslicecheckbox = QCheckBox() self.showslicecheckbox.setText("Show Slice") self.showslicecheckbox.clicked.connect(self.showSlice) self.sliceindexspinBox = QDoubleSpinBox() self.sliceindex = 0 self.sliceindexspinBox.setValue(self.sliceindex) self.sliceindexspinBox.setDecimals(0) self.sliceindexspinBox.setSingleStep(1) self.sliceindexspinBox.setRange(0, 20) self.sliceindexspinBox.valueChanged.connect(self.onSliceIndexChanged) self.SliceLabelComboBox = QComboBox() self.SliceLabelComboBox.activated.connect(self.onSliceComboSelchange) self.sliceaxis = ["h", "k", "l"] self.SliceLabelComboBox.addItems(self.sliceaxis) self.sliceTabWidget = QTabWidget() tab1 = QWidget() layout1 = QGridLayout() layout1.addWidget(self.showslicecheckbox, 0, 0, 1, 1) layout1.addWidget(self.SliceLabelComboBox, 0, 1, 1, 1) layout1.addWidget(self.sliceindexspinBox, 0, 2, 1, 1) tab1.setLayout(layout1) tab2 = QWidget() layout2 = QGridLayout() self.hvec_spinBox = QDoubleSpinBox(self.sliceTabWidget) self.hvecval = 2.0 self.hvec_spinBox.setValue(self.hvecval) self.hvec_spinBox.setDecimals(2) self.hvec_spinBox.setSingleStep(0.5) self.hvec_spinBox.setRange(-100.0, 10.0) self.hvec_spinBox.valueChanged.connect(self.onHvecChanged) self.hvec_Label = QLabel() self.hvec_Label.setText("H") layout2.addWidget(self.hvec_Label, 0, 0, 1, 1) layout2.addWidget(self.hvec_spinBox, 0, 1, 1, 1) self.kvec_spinBox = QDoubleSpinBox(self.sliceTabWidget) self.kvecval = 0.0 self.kvec_spinBox.setValue(self.kvecval) self.kvec_spinBox.setDecimals(2) self.kvec_spinBox.setSingleStep(0.5) self.kvec_spinBox.setRange(-100.0, 100.0) self.kvec_spinBox.valueChanged.connect(self.onKvecChanged) self.kvec_Label = QLabel() self.kvec_Label.setText("K") layout2.addWidget(self.kvec_Label, 1, 0, 1, 1) layout2.addWidget(self.kvec_spinBox, 1, 1, 1, 1) self.lvec_spinBox = QDoubleSpinBox(self.sliceTabWidget) self.lvecval = 0.0 self.lvec_spinBox.setValue(self.lvecval) self.lvec_spinBox.setDecimals(2) self.lvec_spinBox.setSingleStep(0.5) self.lvec_spinBox.setRange(-100.0, 100.0) self.lvec_spinBox.valueChanged.connect(self.onLvecChanged) self.lvec_Label = QLabel() self.lvec_Label.setText("L") layout2.addWidget(self.lvec_Label, 2, 0, 1, 1) layout2.addWidget(self.lvec_spinBox, 2, 1, 1, 1) self.hkldist_spinBox = QDoubleSpinBox(self.sliceTabWidget) self.hkldistval = 0.0 self.hkldist_spinBox.setValue(self.hkldistval) self.hkldist_spinBox.setDecimals(2) self.hkldist_spinBox.setSingleStep(0.5) self.hkldist_spinBox.setRange(-100.0, 100.0) self.hkldist_spinBox.valueChanged.connect(self.onHKLdistChanged) self.hkldist_Label = QLabel() self.hkldist_Label.setText("Distance from Origin") layout2.addWidget(self.hkldist_Label, 3, 0, 1, 1) layout2.addWidget(self.hkldist_spinBox, 3, 1, 1, 1) self.clipwidth_spinBox = QDoubleSpinBox(self.sliceTabWidget) self.clipwidthval = 0.5 self.clipwidth_spinBox.setValue(self.clipwidthval) self.clipwidth_spinBox.setDecimals(2) self.clipwidth_spinBox.setSingleStep(0.05) self.clipwidth_spinBox.setRange(0.0, 100.0) self.clipwidth_spinBox.valueChanged.connect(self.onClipwidthChanged) self.clipwidth_Label = QLabel() self.clipwidth_Label.setText("Clip Plane Width") layout2.addWidget(self.clipwidth_Label, 4, 0, 1, 1) layout2.addWidget(self.clipwidth_spinBox, 4, 1, 1, 1) self.ClipBox = QGroupBox("Normal Vector to Clip Plane") self.ClipBox.setLayout(layout2) layout3 = QGridLayout() self.ClipPlaneChkBox = QCheckBox(self.sliceTabWidget) self.ClipPlaneChkBox.setText( "Use clip plane normal to HKL vector pointing out") self.ClipPlaneChkBox.clicked.connect(self.onClipPlaneChkBox) layout3.addWidget(self.ClipPlaneChkBox, 0, 0) layout3.addWidget(self.ClipBox, 1, 0) tab2.setLayout(layout3) self.sliceTabWidget.addTab(tab1, "Explicit Slicing") self.sliceTabWidget.addTab(tab2, "Clip Plane Slicing") self.ClipBox.setDisabled(True) def onClipPlaneChkBox(self): if self.ClipPlaneChkBox.isChecked(): self.ClipBox.setDisabled(False) philstr = """NGL_HKLviewer.normal_clip_plane { h = %s k = %s l = %s hkldist = %s clipwidth = %s } NGL_HKLviewer.viewer.NGL.fixorientation = %s """ %(self.hvecval, self.kvecval, self.lvecval, self.hkldistval, self.clipwidthval, \ str(self.fixedorientcheckbox.isChecked()) ) self.NGL_HKL_command(philstr) else: self.ClipBox.setDisabled(True) self.NGL_HKL_command( "NGL_HKLviewer.normal_clip_plane.clipwidth = None") def onClipwidthChanged(self, val): self.clipwidthval = val self.NGL_HKL_command("NGL_HKLviewer.normal_clip_plane.clipwidth = %f" % self.clipwidthval) def onHKLdistChanged(self, val): self.hkldistval = val self.NGL_HKL_command("NGL_HKLviewer.normal_clip_plane.hkldist = %f" % self.hkldistval) def onHvecChanged(self, val): self.hvecval = val self.NGL_HKL_command("NGL_HKLviewer.normal_clip_plane.h = %f" % self.hvecval) def onKvecChanged(self, val): self.kvecval = val self.NGL_HKL_command("NGL_HKLviewer.normal_clip_plane.k = %f" % self.kvecval) def onLvecChanged(self, val): self.lvecval = val self.NGL_HKL_command("NGL_HKLviewer.normal_clip_plane.l = %f" % self.lvecval) def onFixedorient(self): self.NGL_HKL_command('NGL_HKLviewer.viewer.NGL.fixorientation = %s' \ %str(self.fixedorientcheckbox.isChecked())) def onMillerComboSelchange(self, i): self.NGL_HKL_command("NGL_HKLviewer.scene_id = %d" % i) #self.MillerComboBox.setCurrentIndex(i) if self.MillerComboBox.currentText(): self.functionTabWidget.setEnabled(True) self.expandAnomalouscheckbox.setEnabled(True) # don' allow anomalous expansion for data that's already anomalous for arrayinfo in self.array_infotpls: isanomalous = arrayinfo[-1] label = arrayinfo[0] if isanomalous and label == self.MillerComboBox.currentText( )[:len(label)]: self.expandAnomalouscheckbox.setDisabled(True) else: self.functionTabWidget.setDisabled(True) self.SpaceGroupComboBox.clear() self.SpaceGroupComboBox.addItems(self.spacegroups) # need to supply issymunique flag in infotuple #if self.hklscenes_arrays[ i ][6] == 0: # self.mergecheckbox.setEnabled(True) #else: # self.mergecheckbox.setEnabled(False) def createFileInfoBox(self): self.FileInfoBox = QGroupBox("Reflection File Information") layout = QGridLayout() layout.addWidget(self.openFileNameButton, 0, 0, 1, 2) if self.devmode: layout.addWidget(self.debugbutton, 0, 2, 1, 1) layout.addWidget(self.HKLnameedit, 1, 0, 1, 3) layout.addWidget(self.millertable, 2, 0, 1, 3) layout.addWidget(self.textInfo, 3, 0, 1, 3) #layout.setColumnStretch(1, 2) self.FileInfoBox.setLayout(layout) def createRadiiScaleGroupBox(self): self.RadiiScaleGroupBox = QGroupBox("Radii Size of HKL Spheres") self.ManualPowerScalecheckbox = QCheckBox() self.ManualPowerScalecheckbox.setText( "Manual Power Scaling of Sphere Radii") self.ManualPowerScalecheckbox.clicked.connect(self.onManualPowerScale) self.power_scale_spinBox = QDoubleSpinBox(self.RadiiScaleGroupBox) self.nth_power_scale = 0.5 self.power_scale_spinBox.setValue(self.nth_power_scale) self.power_scale_spinBox.setDecimals(2) self.power_scale_spinBox.setSingleStep(0.05) self.power_scale_spinBox.setRange(0.0, 1.0) self.power_scale_spinBox.valueChanged.connect(self.onPowerScaleChanged) self.power_scale_spinBox.setEnabled(False) self.powerscaleLabel = QLabel() self.powerscaleLabel.setText("Power scale Factor") self.radii_scale_spinBox = QDoubleSpinBox(self.RadiiScaleGroupBox) self.radii_scale = 1.0 self.radii_scale_spinBox.setValue(self.radii_scale) self.radii_scale_spinBox.setDecimals(1) self.radii_scale_spinBox.setSingleStep(0.1) self.radii_scale_spinBox.setRange(0.2, 2.0) self.radii_scale_spinBox.valueChanged.connect(self.onRadiiScaleChanged) self.radiiscaleLabel = QLabel() self.radiiscaleLabel.setText("Linear Scale Factor") layout = QGridLayout() layout.addWidget(self.ManualPowerScalecheckbox, 1, 0, 1, 2) layout.addWidget(self.powerscaleLabel, 2, 0, 1, 2) layout.addWidget(self.power_scale_spinBox, 2, 1, 1, 2) layout.addWidget(self.radiiscaleLabel, 3, 0, 1, 2) layout.addWidget(self.radii_scale_spinBox, 3, 1, 1, 2) layout.setColumnStretch(0, 1) layout.setColumnStretch(1, 0) self.RadiiScaleGroupBox.setLayout(layout) def createBinsBox(self): self.binstable = QTableWidget(0, 4) self.binstable_isready = False labels = [ "no. of HKLs", "lower bin value", "upper bin value", "opacity" ] self.binstable.setHorizontalHeaderLabels(labels) self.binstable.horizontalHeader().setDefaultAlignment(Qt.AlignLeft) self.bindata_labeltxt = QLabel() self.bindata_labeltxt.setText("Data binned:") self.Nbins_spinBox = QSpinBox() self.Nbins_spinBox.setSingleStep(1) self.Nbins_spinBox.setRange(1, 40) self.Nbins_spinBox.valueChanged.connect(self.onNbinsChanged) self.Nbins_labeltxt = QLabel() self.Nbins_labeltxt.setText("Number of bins:") self.OpaqueAllCheckbox = QCheckBox() #self.OpaqueAllCheckbox.setTristate() self.OpaqueAllCheckbox.setText("Show all data in bins") self.OpaqueAllCheckbox.clicked.connect(self.onOpaqueAll) self.binstable.itemChanged.connect(self.onBinsTableItemChanged) self.binstable.itemSelectionChanged.connect( self.onBinsTableItemSelectionChanged) self.BinDataComboBox = QComboBox() self.BinDataComboBox.activated.connect(self.onBindataComboSelchange) self.BinsGroupBox = QGroupBox("Bins") layout = QGridLayout() layout.addWidget(self.bindata_labeltxt, 0, 0) layout.addWidget(self.BinDataComboBox, 0, 1) layout.addWidget(self.Nbins_labeltxt, 0, 2) layout.addWidget(self.Nbins_spinBox, 0, 3) layout.addWidget(self.OpaqueAllCheckbox, 1, 2) layout.addWidget(self.binstable, 2, 0, 1, 4) layout.setColumnStretch(0, 0) layout.setColumnStretch(1, 2) layout.setColumnStretch(3, 1) self.BinsGroupBox.setLayout(layout) def DebugInteractively(self): import code, traceback code.interact(local=locals(), banner="".join(traceback.format_stack(limit=10))) def CreateFunctionTabs(self): self.functionTabWidget = QTabWidget() tab1 = QWidget() layout1 = QGridLayout() layout1.addWidget(self.ExpansionBox, 0, 0) layout1.setRowStretch(0, 0) tab1.setLayout(layout1) tab2 = QWidget() layout2 = QGridLayout() self.fixedorientcheckbox = QCheckBox(self.sliceTabWidget) self.fixedorientcheckbox.setText( "Fix orientation but allow zoom and translation") self.fixedorientcheckbox.clicked.connect(self.onFixedorient) layout2.addWidget(self.fixedorientcheckbox, 0, 0) layout2.addWidget(self.sliceTabWidget, 1, 0) tab2.setLayout(layout2) tab3 = QWidget() layout3 = QGridLayout() layout3.addWidget(self.RadiiScaleGroupBox, 0, 0) tab3.setLayout(layout3) tab4 = QWidget() layout4 = QGridLayout() layout4.addWidget(self.BinsGroupBox, 0, 0) tab4.setLayout(layout4) self.functionTabWidget.addTab(tab1, "Expand") self.functionTabWidget.addTab(tab2, "Slice") self.functionTabWidget.addTab(tab3, "Size") self.functionTabWidget.addTab(tab4, "Bins") self.functionTabWidget.setDisabled(True) def SpacegroupSelchange(self, i): self.NGL_HKL_command("NGL_HKLviewer.spacegroup_choice = %d" % i) def find_free_port(self): import socket s = socket.socket() s.bind(('', 0)) # Bind to a free port provided by the host. port = s.getsockname()[1] s.close() return port def LaunchCCTBXPython(self): self.sockport = self.find_free_port() self.zmq_context = zmq.Context() self.socket = self.zmq_context.socket(zmq.PAIR) self.socket.bind("tcp://127.0.0.1:%s" % self.sockport) try: msg = self.socket.recv( flags=zmq.NOBLOCK) #To empty the socket from previous messages except Exception as e: pass cmdargs = 'cctbx.python.bat -i -c "from crys3d.hklview import cmdlineframes;' \ + ' myHKLview = cmdlineframes.HKLViewFrame(useGuiSocket=%s, high_quality=True,' %self.sockport \ + ' jscriptfname = \'%s\', ' %self.jscriptfname \ + ' verbose=%s, UseOSBrowser= %s )"\n' %(self.verbose, str(self.UseOSbrowser)) self.cctbxproc = subprocess.Popen(cmdargs, shell=True, stdin=subprocess.PIPE, stdout=sys.stdout, stderr=sys.stderr) #time.sleep(1) def NGL_HKL_command(self, cmdstr): #print("sending:\n" + cmdstr) self.socket.send(bytes(cmdstr, "utf-8"))
class WidgetConfig(QGroupBox): def __init__(self): super(WidgetConfig, self).__init__() HEIGHT = 40 grid = QGridLayout() # 使用默认摄像头复选框 self.check_camera = QCheckBox('Use default camera') self.check_camera.setChecked(False) self.check_camera.stateChanged.connect(self.slot_check_camera) grid.addWidget(self.check_camera, 0, 0, 1, 3) # 一行三列 # 选择视频文件 label_video = QLabel('Detect File') self.line_video = QLineEdit() if 'video' in GLOBAL.config: self.line_video.setText(GLOBAL.config['video']) self.line_video.setFixedHeight(HEIGHT) self.line_video.setEnabled(False) self.line_video.editingFinished.connect( lambda: GLOBAL.record_config({'video': self.line_video.text()})) self.btn_video = QPushButton('Choose') self.btn_video.setFixedHeight(HEIGHT) self.btn_video.setEnabled(False) self.btn_video.clicked.connect(self.choose_video_file) self.slot_check_camera() grid.addWidget(label_video, 1, 0) grid.addWidget(self.line_video, 1, 1) grid.addWidget(self.btn_video, 1, 2) # 选择权重文件 label_weights = QLabel('Weights File') self.line_weights = QLineEdit() if 'weights' in GLOBAL.config: self.line_weights.setText(GLOBAL.config['weights']) self.line_weights.setFixedHeight(HEIGHT) self.line_weights.editingFinished.connect(lambda: GLOBAL.record_config( {'weights': self.line_weights.text()})) self.btn_weights = QPushButton('Choose') self.btn_weights.setFixedHeight(HEIGHT) self.btn_weights.clicked.connect(self.choose_weights_file) grid.addWidget(label_weights, 2, 0) grid.addWidget(self.line_weights, 2, 1) grid.addWidget(self.btn_weights, 2, 2) # 是否使用GPU label_device = QLabel('CUDA device') self.line_device = QLineEdit('gpu') if 'device' in GLOBAL.config: self.line_device.setText(GLOBAL.config['device']) else: self.line_device.setText('cpu') self.line_device.setPlaceholderText('cpu or 0 or 0,1,2,3') self.line_device.setFixedHeight(HEIGHT) self.line_device.editingFinished.connect( lambda: GLOBAL.record_config({'device': self.line_device.text()})) grid.addWidget(label_device, 3, 0) grid.addWidget(self.line_device, 3, 1, 1, 2) # 设置图像大小 label_size = QLabel('Img Size') self.combo_size = QComboBox() self.combo_size.setFixedHeight(HEIGHT) self.combo_size.setStyleSheet( 'QAbstractItemView::item {height: 40px;}') self.combo_size.setView(QListView()) self.combo_size.addItem('320', 320) self.combo_size.addItem('416', 416) self.combo_size.addItem('480', 480) self.combo_size.addItem('544', 544) self.combo_size.addItem('640', 640) self.combo_size.setCurrentIndex(2) self.combo_size.currentIndexChanged.connect( lambda: GLOBAL.record_config( {'img_size': self.combo_size.currentData()})) grid.addWidget(label_size, 4, 0) grid.addWidget(self.combo_size, 4, 1, 1, 2) #choose net camera label_stream = QLabel('NetVedioStream') self.combo_stream = QComboBox() self.combo_stream.setFixedHeight(HEIGHT) self.combo_stream.setStyleSheet( 'QAbstractItemView::item {height: 40px;}') self.combo_stream.setView(QListView()) self.combo_stream.addItem('rtsp://*****:*****@192.168.0.65/', 'rtsp://*****:*****@192.168.0.65/') self.combo_stream.addItem('rtsp://*****:*****@192.168.0.66/', 'rtsp://*****:*****@192.168.0.66/') self.combo_stream.addItem('rtsp://*****:*****@192.168.0.67/', 'rtsp://*****:*****@192.168.0.67/') self.combo_stream.addItem('rtsp://*****:*****@192.168.0.68/', 'rtsp://*****:*****@192.168.0.68/') self.combo_stream.addItem('rtsp://*****:*****@192.168.0.65/', 'rtsp://*****:*****@192.168.0.65/') self.combo_stream.setCurrentIndex(0) self.combo_stream.currentIndexChanged.connect( lambda: GLOBAL.record_config( {'netstreamvedio': self.combo_stream.currentData()})) grid.addWidget(label_stream, 5, 0) grid.addWidget(self.combo_stream, 5, 1, 1, 2) # 设置置信度阈值 label_conf = QLabel('Confidence') self.spin_conf = QDoubleSpinBox() self.spin_conf.setFixedHeight(HEIGHT) self.spin_conf.setDecimals(1) self.spin_conf.setRange(0.1, 0.9) self.spin_conf.setSingleStep(0.1) if 'conf_thresh' in GLOBAL.config: self.spin_conf.setValue(GLOBAL.config['conf_thresh']) else: self.spin_conf.setValue(0.4) # 默认值 GLOBAL.record_config({'conf_thresh': 0.4}) self.spin_conf.valueChanged.connect(lambda: GLOBAL.record_config( {'conf_thresh': round(self.spin_conf.value(), 1)})) grid.addWidget(label_conf, 6, 0) grid.addWidget(self.spin_conf, 6, 1, 1, 2) # 设置IOU阈值 label_iou = QLabel('IOU') self.spin_iou = QDoubleSpinBox() self.spin_iou.setFixedHeight(HEIGHT) self.spin_iou.setDecimals(1) self.spin_iou.setRange(0.1, 0.9) self.spin_iou.setSingleStep(0.1) if 'iou_thresh' in GLOBAL.config: self.spin_iou.setValue(GLOBAL.config['iou_thresh']) else: self.spin_iou.setValue(0.5) # 默认值 GLOBAL.record_config({'iou_thresh': 0.5}) self.spin_iou.valueChanged.connect(lambda: GLOBAL.record_config( {'iou_thresh': round(self.spin_iou.value(), 1)})) grid.addWidget(label_iou, 7, 0) grid.addWidget(self.spin_iou, 7, 1, 1, 2) # class-agnostic NMS self.check_agnostic = QCheckBox('Agnostic') if 'agnostic' in GLOBAL.config: self.check_agnostic.setChecked(GLOBAL.config['agnostic']) else: self.check_agnostic.setChecked(True) self.check_agnostic.stateChanged.connect(lambda: GLOBAL.record_config( {'agnostic': self.check_agnostic.isChecked()})) grid.addWidget(self.check_agnostic, 8, 0, 1, 3) # 一行三列 # augmented inference self.check_augment = QCheckBox('Augment') if 'augment' in GLOBAL.config: self.check_augment.setChecked(GLOBAL.config['augment']) else: self.check_augment.setChecked(True) self.check_augment.stateChanged.connect(lambda: GLOBAL.record_config( {'augment': self.check_augment.isChecked()})) grid.addWidget(self.check_augment, 9, 0, 1, 3) # 一行三列 self.setLayout(grid) # 设置布局 def slot_check_camera(self): check = self.check_camera.isChecked() GLOBAL.record_config({'use_camera': check}) # 保存配置 if check: self.line_video.setEnabled(False) self.btn_video.setEnabled(False) else: self.line_video.setEnabled(True) self.btn_video.setEnabled(True) def choose_weights_file(self): """从系统中选择权重文件""" file = QFileDialog.getOpenFileName( self, "Pre-trained YOLOv5 Weights", "./", "Weights Files (*.pt);;All Files (*)") if file[0] != '': self.line_weights.setText(file[0]) GLOBAL.record_config({'weights': file[0]}) def choose_video_file(self): """从系统中选择视频文件""" file = QFileDialog.getOpenFileName(self, "Video Files", "./", "Video Files (*)") if file[0] != '': self.line_video.setText(file[0]) GLOBAL.record_config({'video': file[0]}) def save_config(self): """保存当前的配置到配置文件""" config = { 'use_camera': self.check_camera.isChecked(), 'video': self.line_video.text(), 'weights': self.line_weights.text(), 'device': self.line_device.text(), 'img_size': self.combo_size.currentData(), 'conf_thresh': round(self.spin_conf.value(), 1), 'iou_thresh': round(self.spin_iou.value(), 1), 'agnostic': self.check_agnostic.isChecked(), 'augment': self.check_augment.isChecked(), 'netstreamvedio': self.combo_stream.currentData() } GLOBAL.record_config(config)
class FirstAlgorithm(QDialog): executed = Signal(dict, list) defaultParameters = { 'offset filter': True, 'supply filter': True, 'LPF highcut': 3, 'BPF lowcut': 8, 'BPF highcut': 30, 'art thres': 100, 'window': 25, 'prev sec': 5, 'hmt larger': 3, 'hmt larger mean': 2, } def __init__(self, dataManager, chartManager, parent=None, inputList=None, initParam=None): QDialog.__init__(self, parent) self.dataManager = dataManager self.fs = self.dataManager.getFs() self.chartManager = chartManager self.setWindowTitle('Algorithms') self.inputList = inputList if initParam: self.parameters = initParam print('initParam') else: self.parameters = self.defaultParameters print('defaultParameters') #Setup Layouts self.mainLayout = QVBoxLayout(self) self.setupInLayout() self.setupSettings() self.setupBtnBoxLayout() def setupInLayout(self): inGroup = QGroupBox('Input to process') inLayout = QFormLayout(inGroup) self.inTree = DataSelector(self.dataManager, inputList=self.inputList) inLayout.addWidget(QLabel('Select Input')) inLayout.addWidget(self.inTree) self.mainLayout.addWidget(inGroup) def setupSettings(self): spinBoxLayout = QFormLayout() self.removeOffset = QCheckBox('Remove DC offset') self.removeOffset.setChecked(self.parameters['offset filter']) spinBoxLayout.addRow(self.removeOffset) self.removeSupply = QCheckBox('Remove 50Hz') self.removeSupply.setChecked(self.parameters['supply filter']) spinBoxLayout.addRow(self.removeSupply) ## self.lpfHighcut = QSpinBox() ## self.lpfHighcut.setMaximum(10000) ## self.lpfHighcut.setValue(self.parameters['LPF highcut']) ## spinBoxLayout.addRow('LPF highcut frequency:', self.lpfHighcut) self.bpfLowcut = QSpinBox() self.bpfLowcut.setMaximum(10000) self.bpfLowcut.setValue(self.parameters['BPF lowcut']) spinBoxLayout.addRow('BPF lowcut frequency:', self.bpfLowcut) self.bpfHighcut = QSpinBox() self.bpfHighcut.setMaximum(10000) self.bpfHighcut.setValue(self.parameters['BPF highcut']) spinBoxLayout.addRow('BPF highcut frequency:', self.bpfHighcut) ## self.ArtefactsThreshold = QSpinBox() ## self.ArtefactsThreshold.setMaximum(10000) ## self.ArtefactsThreshold.setValue(self.parameters['art thres']) ## spinBoxLayout.addRow('Artefacts Threshold:', self.ArtefactsThreshold) self.algWindowSize = QSpinBox() self.algWindowSize.setMaximum(10000) self.algWindowSize.setValue(self.parameters['window']) spinBoxLayout.addRow('Window (samples):', self.algWindowSize) self.hmtLargerMean = QDoubleSpinBox() self.hmtLargerMean.setMinimum(0.1) self.hmtLargerMean.setMaximum(10000) self.hmtLargerMean.setValue(self.parameters['hmt larger mean']) spinBoxLayout.addRow('How many times larger than mean:', self.hmtLargerMean) self.prevSeconds = QDoubleSpinBox() self.prevSeconds.setMinimum(0.1) self.prevSeconds.setMaximum(10000) self.prevSeconds.setValue(self.parameters['prev sec']) spinBoxLayout.addRow('Number of previous seconds:', self.prevSeconds) self.hmtLarger = QDoubleSpinBox() self.hmtLarger.setMinimum(0.1) self.hmtLarger.setMaximum(10000) self.hmtLarger.setValue(self.parameters['hmt larger']) spinBoxLayout.addRow('How many times larger than previous:', self.hmtLarger) gBox = QGroupBox('Settings') gBox.setLayout(spinBoxLayout) self.mainLayout.addWidget(gBox) def setupBtnBoxLayout(self): bottomLayout = QHBoxLayout() self.progBar = QProgressBar() self.progBar.setVisible(False) bottomLayout.addWidget(self.progBar) buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Close) buttonBox.accepted.connect(self.okBtnBox) buttonBox.rejected.connect(self.close) bottomLayout.addWidget(buttonBox) self.mainLayout.addLayout(bottomLayout) def okBtnBox(self): inStruct = self.inTree.getSelectedStruct() inDataStruct = self.dataManager.getData(inStruct, inv=True) self.progBar.setVisible(True) self.progBar.setValue(0) nProgres = 5 progStep = 100.0 / nProgres progress = 0 wName = list(inDataStruct.keys())[0] gName = list(inDataStruct[wName].keys())[0] ## Input inData = np.copy(inDataStruct[wName][gName]) ## Noises if self.removeOffset.isChecked(): inData = [signal.detrend(data, type='constant') for data in inData] if self.removeSupply.isChecked(): b, a = filterCalc(order=5, bandarr=[48, 52], fs=self.fs, btype='bandstop', ftype='butter') inData = [signal.lfilter(b, a, data) for data in inData] winLen = self.algWindowSize.value() ## ## Artefacts ## lpfHighcut = self.lpfHighcut.value() ## order=5 ## ## b, a = filterCalc(order, [lpfHighcut], self.fs, 'low', 'butter') ## lpfData = [] ## for data in inData: ## newData = signal.lfilter(b, a, data) ## lpfData.append(newData) ## ## ## ## segNmbr = int(np.shape(lpfData)[1]/winLen) ## artefactsThreshold = self.ArtefactsThreshold.value() ## ## lpfWinData = np.copy(lpfData) ## lpfWinData = [np.array_split(ch, segNmbr) for ch in lpfWinData] ## ## for ch in lpfWinData: ## for seg in ch: ## seg.fill(max(seg)- min(seg)) ## progress += progStep self.progBar.setValue(progress) ## #### thresLpfData = np.copy(lpfWinData) ## thresLpfData = deepcopy(lpfWinData) ## ## for x in thresLpfData: ## for y in x: ## if y[0] >= artefactsThreshold: ## y.fill(1) ## else: ## y.fill(0) ## ## lpfWinData = [np.concatenate(ch) for ch in lpfWinData] ## thresLpfData = [np.concatenate(ch) for ch in thresLpfData] ## progress += progStep self.progBar.setValue(progress) ## Spikes bpfLowcut = self.bpfLowcut.value() bpfHighcut = self.bpfHighcut.value() order = 4 b, a = filterCalc(order, [bpfLowcut, bpfHighcut], self.fs, 'band', 'butter') bpfData = [] for data in inData: newData = signal.lfilter(b, a, data) bpfData.append(newData) segNmbr = int(np.shape(bpfData)[1] / winLen) prevSeconds = self.prevSeconds.value() prevWindows = int(prevSeconds * self.fs / winLen) progress += progStep self.progBar.setValue(progress) bpfWinData = np.copy(bpfData) bpfWinData = [np.array_split(ch, segNmbr) for ch in bpfWinData] chi = 0 for ch in bpfWinData: for seg in ch: seg.fill(max(seg) - min(seg)) progress += progStep self.progBar.setValue(progress) hmtLarger = self.hmtLarger.value() hmtLargerMean = self.hmtLargerMean.value() ## thresBpfData = np.copy(bpfWinData) thresBpfData = deepcopy(bpfWinData) for ch in range(len(bpfWinData)): channelMean = self.hmtLargerMean.value() * np.mean(inData[ch]) for i in range(prevWindows, len(bpfWinData[ch])): if bpfWinData[ch][i][0] > channelMean: prev = [ bpfWinData[ch][j][0] for j in range(i - prevWindows, i) ] if bpfWinData[ch][i][0] > hmtLarger * np.mean(prev): thresBpfData[ch][i].fill(1) else: thresBpfData[ch][i].fill(0) else: thresBpfData[ch][i].fill(0) progress += progStep self.progBar.setValue(progress) for ch in thresBpfData: for i in range(prevWindows): ch[i].fill(0) bpfWinData = [np.concatenate(ch) for ch in bpfWinData] thresBpfData = [np.concatenate(ch) for ch in thresBpfData] spikeMap = [np.sum(x) / winLen for x in thresBpfData] nazwaPliku = str(winLen) + '_' + str(prevSeconds) + '_' + str( hmtLarger) + '.csv' with open(nazwaPliku, 'w', encoding='utf-8') as csvfile: csvwriter = csv.writer(csvfile) csvwriter.writerow(spikeMap) ## for ch in spikeMap ## csvwriter.writerow(spikeMap) ## Add data or replace existing swsName = 'Algorithm output' chNames = inStruct[wName][gName] if swsName not in self.dataManager.getDataGroups(): self.dataManager.createDataGroup(swsName, 'Algorithm') self.dataManager.addChannels(swsName, chNames) self.dataManager.addSignal(swsName, 'Input Signal', inData, chNames) ## self.dataManager.addSignal(swsName, 'LPF', lpfData, chNames) ## self.dataManager.addSignal(swsName, 'LPF+Window+Peak-to-peak', lpfWinData, chNames) ## self.dataManager.addSignal(swsName, 'LPF+Threshold - Artefacts', thresLpfData, chNames) self.dataManager.addSignal(swsName, 'BPF', bpfData, chNames) self.dataManager.addSignal(swsName, 'BPF+Window+Peak-to-peak', bpfWinData, chNames) self.dataManager.addSignal(swsName, 'BPF+Threshold - Spikes', thresBpfData, chNames) else: self.dataManager.silentChangeSignal(swsName, 'Input Signal', inData, chNames) ## self.dataManager.silentChangeSignal(swsName, 'LPF', lpfData, chNames) ## self.dataManager.silentChangeSignal(swsName, 'LPF+Window+P2P', lpfWinData, chNames) ## self.dataManager.silentChangeSignal(swsName, 'LPF+Threshold - Artefacts', thresLpfData, chNames) self.dataManager.silentChangeSignal(swsName, 'BPF', bpfData, chNames) self.dataManager.silentChangeSignal(swsName, 'BPF+Window+Peak-to-peak', bpfWinData, chNames) self.dataManager.silentChangeSignal(swsName, 'BPF+Threshold - Spikes', thresBpfData, chNames) self.chartManager.setMask('Algorithm output', 'BPF+Threshold - Spikes') self.chartManager.setMap(spikeMap) self.parameters = { 'offset filter': self.removeOffset.isChecked(), 'supply filter': self.removeSupply.isChecked(), ## 'LPF highcut' : lpfHighcut, 'BPF lowcut': bpfLowcut, 'BPF highcut': bpfHighcut, ## 'art thres' : artefactsThreshold, 'window': winLen, 'prev sec': prevSeconds, 'hmt larger': hmtLarger, 'hmt larger mean': hmtLargerMean, } self.executed.emit(self.parameters, self.inputList)
def createDoubleSpinBox(): spinbox = QDoubleSpinBox() spinbox.setDecimals(3) spinbox.setValue(0.000) spinbox.setAlignment(Qt.AlignCenter) return spinbox
class ColorDialog(QDialog): def __init__(self, model, font_metric, parent=None): super().__init__(parent) self.setWindowTitle('Color Options') self.model = model self.font_metric = font_metric self.main_window = parent self.createDialogLayout() def createDialogLayout(self): self.createGeneralTab() self.cellTable = self.createDomainTable(self.main_window.cellsModel) self.matTable = self.createDomainTable(self.main_window.materialsModel) self.tabs = { 'cell': self.createDomainTab(self.cellTable), 'material': self.createDomainTab(self.matTable), 'temperature': self.createPropertyTab('temperature'), 'density': self.createPropertyTab('density') } self.tab_bar = QTabWidget() self.tab_bar.setMaximumHeight(800) self.tab_bar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.tab_bar.addTab(self.generalTab, 'General') self.tab_bar.addTab(self.tabs['cell'], 'Cells') self.tab_bar.addTab(self.tabs['material'], 'Materials') self.tab_bar.addTab(self.tabs['temperature'], 'Temperature') self.tab_bar.addTab(self.tabs['density'], 'Density') self.createButtonBox() self.colorDialogLayout = QVBoxLayout() self.colorDialogLayout.addWidget(self.tab_bar) self.colorDialogLayout.addWidget(self.buttonBox) self.setLayout(self.colorDialogLayout) def createGeneralTab(self): main_window = self.main_window # Masking options self.maskingCheck = QCheckBox('') self.maskingCheck.stateChanged.connect(main_window.toggleMasking) button_width = self.font_metric.boundingRect("XXXXXXXXXX").width() self.maskColorButton = QPushButton() self.maskColorButton.setCursor(QtCore.Qt.PointingHandCursor) self.maskColorButton.setFixedWidth(button_width) self.maskColorButton.setFixedHeight(self.font_metric.height() * 1.5) self.maskColorButton.clicked.connect(main_window.editMaskingColor) # Highlighting options self.hlCheck = QCheckBox('') self.hlCheck.stateChanged.connect(main_window.toggleHighlighting) self.hlColorButton = QPushButton() self.hlColorButton.setCursor(QtCore.Qt.PointingHandCursor) self.hlColorButton.setFixedWidth(button_width) self.hlColorButton.setFixedHeight(self.font_metric.height() * 1.5) self.hlColorButton.clicked.connect(main_window.editHighlightColor) self.alphaBox = QDoubleSpinBox() self.alphaBox.setRange(0, 1) self.alphaBox.setSingleStep(.05) self.alphaBox.valueChanged.connect(main_window.editAlpha) self.seedBox = QSpinBox() self.seedBox.setRange(1, 999) self.seedBox.valueChanged.connect(main_window.editSeed) # General options self.bgButton = QPushButton() self.bgButton.setCursor(QtCore.Qt.PointingHandCursor) self.bgButton.setFixedWidth(button_width) self.bgButton.setFixedHeight(self.font_metric.height() * 1.5) self.bgButton.clicked.connect(main_window.editBackgroundColor) self.colorbyBox = QComboBox(self) self.colorbyBox.addItem("material") self.colorbyBox.addItem("cell") self.colorbyBox.addItem("temperature") self.colorbyBox.addItem("density") self.colorbyBox.currentTextChanged[str].connect( main_window.editColorBy) self.universeLevelBox = QComboBox(self) self.universeLevelBox.addItem('all') for i in range(self.model.max_universe_levels): self.universeLevelBox.addItem(str(i)) self.universeLevelBox.currentTextChanged[str].connect( main_window.editUniverseLevel) # Overlap plotting self.overlapCheck = QCheckBox('', self) overlap_connector = partial(main_window.toggleOverlaps) self.overlapCheck.stateChanged.connect(overlap_connector) self.overlapColorButton = QPushButton() self.overlapColorButton.setCursor(QtCore.Qt.PointingHandCursor) self.overlapColorButton.setFixedWidth(button_width) self.overlapColorButton.setFixedHeight(self.font_metric.height() * 1.5) self.overlapColorButton.clicked.connect(main_window.editOverlapColor) self.colorResetButton = QPushButton("&Reset Colors") self.colorResetButton.setCursor(QtCore.Qt.PointingHandCursor) self.colorResetButton.clicked.connect(main_window.resetColors) formLayout = QFormLayout() formLayout.setAlignment(QtCore.Qt.AlignHCenter) formLayout.setFormAlignment(QtCore.Qt.AlignHCenter) formLayout.setLabelAlignment(QtCore.Qt.AlignLeft) formLayout.addRow('Masking:', self.maskingCheck) formLayout.addRow('Mask Color:', self.maskColorButton) formLayout.addRow(HorizontalLine()) formLayout.addRow('Highlighting:', self.hlCheck) formLayout.addRow('Highlight Color:', self.hlColorButton) formLayout.addRow('Highlight Alpha:', self.alphaBox) formLayout.addRow('Highlight Seed:', self.seedBox) formLayout.addRow(HorizontalLine()) formLayout.addRow('Background Color: ', self.bgButton) formLayout.addRow(HorizontalLine()) formLayout.addRow('Show Overlaps:', self.overlapCheck) formLayout.addRow('Overlap Color:', self.overlapColorButton) formLayout.addRow(HorizontalLine()) formLayout.addRow('Color Plot By:', self.colorbyBox) formLayout.addRow('Universe Level:', self.universeLevelBox) formLayout.addRow(self.colorResetButton, None) generalLayout = QHBoxLayout() innerWidget = QWidget() generalLayout.setAlignment(QtCore.Qt.AlignVCenter) innerWidget.setLayout(formLayout) generalLayout.addStretch(1) generalLayout.addWidget(innerWidget) generalLayout.addStretch(1) self.generalTab = QWidget() self.generalTab.setLayout(generalLayout) def createDomainTable(self, domainmodel): domainTable = QTableView() domainTable.setModel(domainmodel) domainTable.setItemDelegate(DomainDelegate(domainTable)) domainTable.verticalHeader().setVisible(False) domainTable.resizeColumnsToContents() domainTable.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) domainTable.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Stretch) return domainTable def createDomainTab(self, domaintable): domainTab = QWidget() domainTab.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) domainLayout = QVBoxLayout() domainLayout.addWidget(domaintable) domainTab.setLayout(domainLayout) return domainTab def createPropertyTab(self, property_kind): propertyTab = QWidget() propertyTab.property_kind = property_kind propertyTab.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) propertyLayout = QVBoxLayout() propertyTab.minMaxCheckBox = QCheckBox() propertyTab.minMaxCheckBox.setCheckable(True) connector1 = partial(self.main_window.toggleUserMinMax, property=property_kind) propertyTab.minMaxCheckBox.stateChanged.connect(connector1) propertyTab.minBox = ScientificDoubleSpinBox(self) propertyTab.minBox.setMaximum(1E9) propertyTab.minBox.setMinimum(0) propertyTab.maxBox = ScientificDoubleSpinBox(self) propertyTab.maxBox.setMaximum(1E9) propertyTab.maxBox.setMinimum(0) connector2 = partial(self.main_window.editColorbarMin, property_type=property_kind) propertyTab.minBox.valueChanged.connect(connector2) connector3 = partial(self.main_window.editColorbarMax, property_type=property_kind) propertyTab.maxBox.valueChanged.connect(connector3) propertyTab.colormapBox = QComboBox(self) cmaps = sorted(m for m in mcolormaps.datad if not m.endswith("_r")) for cmap in cmaps: propertyTab.colormapBox.addItem(cmap) connector = partial(self.main_window.editColorMap, property_type=property_kind) propertyTab.colormapBox.currentTextChanged[str].connect(connector) propertyTab.dataIndicatorCheckBox = QCheckBox() propertyTab.dataIndicatorCheckBox.setCheckable(True) connector4 = partial(self.main_window.toggleDataIndicatorCheckBox, property=property_kind) propertyTab.dataIndicatorCheckBox.stateChanged.connect(connector4) propertyTab.colorBarScaleCheckBox = QCheckBox() propertyTab.colorBarScaleCheckBox.setCheckable(True) connector5 = partial(self.main_window.toggleColorbarScale, property=property_kind) propertyTab.colorBarScaleCheckBox.stateChanged.connect(connector5) formLayout = QFormLayout() formLayout.setAlignment(QtCore.Qt.AlignHCenter) formLayout.setFormAlignment(QtCore.Qt.AlignHCenter) formLayout.setLabelAlignment(QtCore.Qt.AlignLeft) formLayout.addRow('Colormap:', propertyTab.colormapBox) formLayout.addRow('Custom Min/Max', propertyTab.minMaxCheckBox) formLayout.addRow('Data Indicator', propertyTab.dataIndicatorCheckBox) formLayout.addRow('Log Scale', propertyTab.colorBarScaleCheckBox) formLayout.addRow(HorizontalLine()) formLayout.addRow('Max: ', propertyTab.maxBox) formLayout.addRow('Min: ', propertyTab.minBox) propertyTab.setLayout(formLayout) return propertyTab def updateDataIndicatorVisibility(self): av = self.model.activeView for key, val in av.data_indicator_enabled.items(): self.tabs[key].dataIndicatorCheckBox.setChecked(val) def updateColorMaps(self): cmaps = self.model.activeView.colormaps for key, val in cmaps.items(): idx = self.tabs[key].colormapBox.findText( val, QtCore.Qt.MatchFixedString) if idx >= 0: self.tabs[key].colormapBox.setCurrentIndex(idx) def updateColorMinMax(self): minmax = self.model.activeView.user_minmax for key, val in minmax.items(): self.tabs[key].minBox.setValue(val[0]) self.tabs[key].maxBox.setValue(val[1]) custom_minmax = self.model.activeView.use_custom_minmax for key, val, in custom_minmax.items(): self.tabs[key].minMaxCheckBox.setChecked(val) self.tabs[key].minBox.setEnabled(val) self.tabs[key].maxBox.setEnabled(val) def updateColorbarScale(self): av = self.model.activeView for key, val in av.color_scale_log.items(): self.tabs[key].colorBarScaleCheckBox.setChecked(val) def createButtonBox(self): applyButton = QPushButton("Apply Changes") applyButton.clicked.connect(self.main_window.applyChanges) closeButton = QPushButton("Close") closeButton.clicked.connect(self.hide) buttonLayout = QHBoxLayout() buttonLayout.addStretch(1) buttonLayout.addWidget(applyButton) buttonLayout.addWidget(closeButton) self.buttonBox = QWidget() self.buttonBox.setLayout(buttonLayout) def updateDialogValues(self): self.updateMasking() self.updateMaskingColor() self.updateColorMaps() self.updateColorMinMax() self.updateColorbarScale() self.updateDataIndicatorVisibility() self.updateHighlighting() self.updateHighlightColor() self.updateAlpha() self.updateSeed() self.updateBackgroundColor() self.updateColorBy() self.updateUniverseLevel() self.updateDomainTabs() self.updateOverlap() self.updateOverlapColor() def updateMasking(self): masking = self.model.activeView.masking self.maskingCheck.setChecked(masking) self.maskColorButton.setDisabled(not masking) if masking: self.cellTable.showColumn(4) self.matTable.showColumn(4) else: self.cellTable.hideColumn(4) self.matTable.hideColumn(4) def updateMaskingColor(self): color = self.model.activeView.maskBackground style_values = "border-radius: 8px; background-color: rgb{}" self.maskColorButton.setStyleSheet(style_values.format(str(color))) def updateHighlighting(self): highlighting = self.model.activeView.highlighting self.hlCheck.setChecked(highlighting) self.hlColorButton.setDisabled(not highlighting) self.alphaBox.setDisabled(not highlighting) self.seedBox.setDisabled(not highlighting) if highlighting: self.cellTable.showColumn(5) self.cellTable.hideColumn(2) self.cellTable.hideColumn(3) self.matTable.showColumn(5) self.matTable.hideColumn(2) self.matTable.hideColumn(3) else: self.cellTable.hideColumn(5) self.cellTable.showColumn(2) self.cellTable.showColumn(3) self.matTable.hideColumn(5) self.matTable.showColumn(2) self.matTable.showColumn(3) def updateHighlightColor(self): color = self.model.activeView.highlightBackground style_values = "border-radius: 8px; background-color: rgb{}" self.hlColorButton.setStyleSheet(style_values.format(str(color))) def updateAlpha(self): self.alphaBox.setValue(self.model.activeView.highlightAlpha) def updateSeed(self): self.seedBox.setValue(self.model.activeView.highlightSeed) def updateBackgroundColor(self): color = self.model.activeView.domainBackground self.bgButton.setStyleSheet("border-radius: 8px;" "background-color: rgb%s" % (str(color))) def updateOverlapColor(self): color = self.model.activeView.overlap_color self.overlapColorButton.setStyleSheet("border-radius: 8px;" "background-color: rgb%s" % (str(color))) def updateOverlap(self): colorby = self.model.activeView.colorby overlap_val = self.model.activeView.color_overlaps if colorby in ('cell', 'material'): self.overlapCheck.setChecked(overlap_val) def updateColorBy(self): colorby = self.model.activeView.colorby self.colorbyBox.setCurrentText(colorby) self.overlapCheck.setEnabled(colorby in ("cell", "material")) self.universeLevelBox.setEnabled(colorby == 'cell') def updateUniverseLevel(self): level = self.model.activeView.level if level == -1: self.universeLevelBox.setCurrentText('all') else: self.universeLevelBox.setCurrentText(str(level)) def updateDomainTabs(self): self.cellTable.setModel(self.main_window.cellsModel) self.matTable.setModel(self.main_window.materialsModel)
class TrainingPanel(Panel): def __init__(self, datasets, testing_panel, threads): super().__init__() if isinstance(testing_panel, TestingPanel): self.testing_panel = testing_panel else: raise TypeError('"testing_panel" must be the instance of ' '"TestingPanel"') self.datasets = datasets self.threads = threads self.__set_execution_ui() self.__set_options_ui() self.__set_outputs_ui() self.__set_graphic_ui() def __set_execution_ui(self): group_box = QGroupBox('Training Execution') inner_layout = QHBoxLayout() group_box.setLayout(inner_layout) self.data_selector = QComboBox() self.data_selector.addItems(list(self.datasets.keys())) self.data_selector.setStatusTip('Select the training dataset.') self.start_btn = QPushButton('Train') self.start_btn.setStatusTip('Start training.') self.start_btn.clicked.connect(self.__run) self.stop_btn = QPushButton('Stop') self.stop_btn.setStatusTip('Force the training stop running.') self.stop_btn.setDisabled(True) self.multicore_cb = QCheckBox('Multicore') self.multicore_cb.setStatusTip('Use multiprocessing in calculating ' 'fitting for populations.') self.multicore_cb.setChecked(True) inner_layout.addWidget(self.data_selector, 1) inner_layout.addWidget(self.start_btn) inner_layout.addWidget(self.stop_btn) inner_layout.addWidget(self.multicore_cb) self._layout.addWidget(group_box) def __set_options_ui(self): group_box = QGroupBox('Training Options') inner_layout = QFormLayout() group_box.setLayout(inner_layout) self.iter_times = QSpinBox() self.iter_times.setRange(1, 1000000) self.iter_times.setValue(200) self.iter_times.setStatusTip('The total iterating times for training.') self.population_size = QSpinBox() self.population_size.setRange(1, 100000) self.population_size.setValue(100) self.population_size.setStatusTip('The population size for the PSO.') self.inertia_weight = QDoubleSpinBox() self.inertia_weight.setRange(0, 50) self.inertia_weight.setValue(1) self.inertia_weight.setSingleStep(0.1) self.inertia_weight.setStatusTip('The inertia weight of the velocity ' ' for each individual.') self.cognitive_const_rand_upper = QDoubleSpinBox() self.cognitive_const_rand_upper.setRange(0, 50) self.cognitive_const_rand_upper.setValue(2) self.cognitive_const_rand_upper.setSingleStep(0.1) self.cognitive_const_rand_upper.setStatusTip( 'The random upper bound for cognitive accelerate constant.') self.social_const_rand_upper = QDoubleSpinBox() self.social_const_rand_upper.setRange(0, 50) self.social_const_rand_upper.setValue(3) self.social_const_rand_upper.setSingleStep(0.1) self.social_const_rand_upper.setStatusTip( 'The random upper bound for social accelerate constant.') self.v_max = QDoubleSpinBox() self.v_max.setRange(0.5, 100) self.v_max.setValue(5) self.v_max.setSingleStep(1) self.v_max.setStatusTip('The maximum of velocity for each individual.') self.nneuron = QSpinBox() self.nneuron.setRange(1, 100) self.nneuron.setValue(6) self.nneuron.setStatusTip('The number of RBFN neuron.') self.sd_max = QDoubleSpinBox() self.sd_max.setRange(0.01, 20) self.sd_max.setValue(10) self.sd_max.setSingleStep(0.1) self.sd_max.setStatusTip('The random range maximum of standard ' 'deviation of each neuron in RBFN (only for ' 'initialization).') inner_layout.addRow('Iterating Times:', self.iter_times) inner_layout.addRow('Population Size:', self.population_size) inner_layout.addRow('Inertia Weight:', self.inertia_weight) inner_layout.addRow('Cognitive Const Upper:', self.cognitive_const_rand_upper) inner_layout.addRow('Social Const Upper:', self.social_const_rand_upper) inner_layout.addRow('Maximum of Velocity:', self.v_max) inner_layout.addRow('Number of Neuron:', self.nneuron) inner_layout.addRow('Maximum of SD:', self.sd_max) self._layout.addWidget(group_box) def __set_outputs_ui(self): group_box = QGroupBox('Training Details') inner_layout = QFormLayout() group_box.setLayout(inner_layout) self.current_iter_time = QLabel('--') self.current_error = QLabel('--') self.avg_error = QLabel('--') self.global_best_error = QLabel('--') self.total_best_error = QLabel('--') self.progressbar = QProgressBar() self.current_iter_time.setAlignment(Qt.AlignCenter) self.current_error.setAlignment(Qt.AlignCenter) self.avg_error.setAlignment(Qt.AlignCenter) self.global_best_error.setAlignment(Qt.AlignCenter) self.total_best_error.setAlignment(Qt.AlignCenter) self.current_iter_time.setStatusTip('The current iterating time of ' 'the PSO.') self.current_error.setStatusTip('The current error from the fitting ' 'function. ("( )": normalized error)') self.avg_error.setStatusTip('The average error from the fitting ' 'function in current iteration. ("( )": ' 'normalized error)') self.global_best_error.setStatusTip( 'The error of global best individual from the fitting function in ' 'current iteration. ("( )": normalized error)') self.total_best_error.setStatusTip( 'The error of total best individual from the fitting function in ' 'training. ("( )": normalized error)') inner_layout.addRow('Current Iterating Time:', self.current_iter_time) inner_layout.addRow('Current Error:', self.current_error) inner_layout.addRow('Average Error:', self.avg_error) inner_layout.addRow('Global Best Error:', self.global_best_error) inner_layout.addRow('Total Best Error:', self.total_best_error) inner_layout.addRow(self.progressbar) self._layout.addWidget(group_box) def __set_graphic_ui(self): group_box = QGroupBox('Error Line Charts:') inner_layout = QVBoxLayout() group_box.setLayout(inner_layout) self.err_chart = ErrorLineChart(1) self.err_chart.setStatusTip('The history of error from the fitting ' 'of the PSO for each data.') self.__err_x = 1 self.iter_err_chart = ErrorLineChart( 3, ('Avg', 'Global Best', 'Total Best')) self.iter_err_chart.setStatusTip('The history of average and least ' 'error from the fitting of the PSO ' 'for each iteration.') self.iter_err_chart.setMinimumHeight(150) inner_layout.addWidget(QLabel('Current Error')) inner_layout.addWidget(self.err_chart) inner_layout.addWidget(QLabel('Average Error')) inner_layout.addWidget(self.iter_err_chart) self._layout.addWidget(group_box) @Slot() def __init_widgets(self): self.start_btn.setDisabled(True) self.stop_btn.setEnabled(True) self.multicore_cb.setDisabled(True) self.data_selector.setDisabled(True) self.iter_times.setDisabled(True) self.population_size.setDisabled(True) self.inertia_weight.setDisabled(True) self.cognitive_const_rand_upper.setDisabled(True) self.social_const_rand_upper.setDisabled(True) self.v_max.setDisabled(True) self.nneuron.setDisabled(True) self.sd_max.setDisabled(True) self.err_chart.clear() self.iter_err_chart.clear() self.__err_x = 1 @Slot() def __reset_widgets(self): self.start_btn.setEnabled(True) self.stop_btn.setDisabled(True) self.multicore_cb.setEnabled(True) self.data_selector.setEnabled(True) self.iter_times.setEnabled(True) self.population_size.setEnabled(True) self.inertia_weight.setEnabled(True) self.cognitive_const_rand_upper.setEnabled(True) self.social_const_rand_upper.setEnabled(True) self.v_max.setEnabled(True) self.nneuron.setEnabled(True) self.sd_max.setEnabled(True) self.progressbar.setMinimum(0) self.progressbar.setMaximum(100) @Slot() def __indicate_busy(self): self.progressbar.setMinimum(0) self.progressbar.setMaximum(0) @Slot(int) def __show_current_iter_time(self, value): self.current_iter_time.setText(str(value + 1)) self.progressbar.setValue(value + 1) @Slot(float) def __show_current_error(self, value): self.current_error.setText('{:.5f} ({:.5f})'.format(value, value / 40)) self.err_chart.append_point(self.__err_x, value) self.__err_x += 1 @Slot(float, float, float) def __show_iter_error(self, avg, glob, total): self.avg_error.setText('{:.5f} ({:.5f})'.format(avg, avg / 40)) self.global_best_error.setText( '{:.5f} ({:.5f})'.format(glob, glob / 40)) self.total_best_error.setText( '{:.5f} ({:.5f})'.format(total, total / 40)) self.iter_err_chart.append_point( int(self.current_iter_time.text()), total, 2) self.iter_err_chart.append_point( int(self.current_iter_time.text()), glob, 1) self.iter_err_chart.append_point( int(self.current_iter_time.text()), avg, 0) def __run(self): self.progressbar.setMaximum(self.iter_times.value()) self.__current_dataset = self.datasets[ self.data_selector.currentText()] self.__pso = PSO(self.iter_times.value(), self.population_size.value(), self.inertia_weight.value(), self.cognitive_const_rand_upper.value(), self.social_const_rand_upper.value(), self.v_max.value(), self.nneuron.value(), self.__current_dataset, self.sd_max.value(), is_multicore=self.multicore_cb.isChecked()) self.threads.append(self.__pso) self.stop_btn.clicked.connect(self.__pso.stop) self.__pso.started.connect(self.__init_widgets) self.__pso.finished.connect(self.__reset_widgets) self.__pso.sig_current_iter_time.connect(self.__show_current_iter_time) self.__pso.sig_current_error.connect(self.__show_current_error) self.__pso.sig_iter_error.connect(self.__show_iter_error) self.__pso.sig_indicate_busy.connect(self.__indicate_busy) self.__pso.sig_console.connect(self.testing_panel.print_console) self.__pso.sig_rbfn.connect(self.testing_panel.load_rbfn) self.__pso.start()
def _gui(self): layout = QVBoxLayout() self.setLayout(layout) slider_layout = QHBoxLayout() layout.addLayout(slider_layout) slider = ClickSlider(self) slider.setOrientation(Qt.Horizontal) slider.valueChanged.connect(self._slider_changed) slider_layout.addWidget(slider) self.slider = slider max_time = QLabel(self) self.max_time = max_time max_time.setText('--:--') slider_layout.addWidget(max_time) form = QFormLayout() layout.addLayout(form) time = QTimeEdit(self) time.setDisplayFormat('mm:ss.zzz') time.setAlignment(Qt.AlignRight) time.timeChanged.connect(self._edit_time_changed) self.time = time form.addRow(QLabel('Time', self),time) speed = QDoubleSpinBox(self) speed.setMinimum(0.1) speed.setMaximum(2.0) speed.setSingleStep(0.1) speed.setValue(1) speed.setAlignment(Qt.AlignRight) self.speed = speed speed.valueChanged.connect(self._speed_changed) form.addRow(QLabel('Speed', self), speed) playback = QHBoxLayout() layout.addLayout(playback) back_fast = QToolButton(self) playback.addWidget(back_fast) abf = QAction('<<', back_fast) abf.setShortcut(QKeySequence(Qt.Key_F1)) abf.triggered.connect(self.back_fast) back_fast.setDefaultAction(abf) back = QToolButton(self) playback.addWidget(back) ab = QAction('<', back) ab.setShortcut(QKeySequence(Qt.Key_F2)) ab.triggered.connect(self.back) back.setDefaultAction(ab) play = QToolButton(self) playback.addWidget(play) self.play_button = play play.setCheckable(True) play.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) ap = QAction('Play', play) ap.setShortcut(QKeySequence(Qt.Key_F5)) ap.triggered.connect(self.play) play.setDefaultAction(ap) forward = QToolButton(self) playback.addWidget(forward) af = QAction('>', forward) af.setShortcut(QKeySequence(Qt.Key_F3)) af.triggered.connect(self.forward) forward.setDefaultAction(af) forward_fast = QToolButton(self) playback.addWidget(forward_fast) aff = QAction('>>', forward_fast) aff.setShortcut(QKeySequence(Qt.Key_F4)) aff.triggered.connect(self.forward_fast) forward_fast.setDefaultAction(aff) speed_up = QAction('Speed up', forward_fast) speed_up.setShortcut(QKeySequence(Qt.Key_F7)) speed_up.triggered.connect(self.speed.stepUp) speed_down = QAction('Speed down', forward_fast) speed_down.setShortcut(QKeySequence(Qt.Key_F6)) speed_down.triggered.connect(self.speed.stepDown) self.utils = [ slider, back_fast, back, play, forward, forward_fast, time, speed ] self.playback_actions = [abf, ab, ap, af, aff, speed_down, speed_up] self.set_enabled_utils(False) self.prevent = False self.timer = QTimer(self) self.timer.timeout.connect(self._update) self.timer.start(1) self.player = None