class ValueEdit(QWidget): def __init__(self, title, unit, number_type='double', parent=None): super(ValueEdit, self).__init__(parent) self.title = QLabel(title) self.title.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.unit = QLabel(unit) self.unit.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) if number_type == 'double': self.edit = QDoubleSpinBox() elif number_type == 'int': self.edit = QSpinBox() else: raise ValueError( 'Editor can either be for double or integer values') self.edit.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.edit.setKeyboardTracking(False) self.edit.setAlignment(Qt.AlignRight) self.edit.setButtonSymbols(QAbstractSpinBox.NoButtons) def set_range_decimals(self, min, max, decimals=3): self.edit.setRange(min, max) if isinstance(self.edit, QDoubleSpinBox): self.edit.setDecimals(decimals) def set_single_step_size(self, step=.1): self.edit.setSingleStep(step) def value(self): return self.edit.value() @Slot(float) def setValue(self, value): self.edit.setValue(value)
def _layout(self): layout = QFormLayout(self) self.setLayout(layout) tooltip_label = QLabel("Display tooltips as", self) tooltip_field = QComboBox(self) tooltip_field.insertItem(0, "Disabled") tooltip_field.insertItem(1, "Notification") # XXX: Check support tooltip_field.insertItem(2, "Cursor tooltip") tooltip_field.currentIndexChanged.connect(self.preview_tooltip) layout.addRow(tooltip_label, tooltip_field) border_field = QCheckBox("Styled window border", self) border_field.setDisabled(sys.platform == "darwin") border_field.setChecked(sys.platform != "darwin") layout.addRow(None, border_field) # I think this doesn't belong to "Interface" # either rename it to settings or make separate game tab capturei_label = QLabel("Capture interval", self) capturei_layout = QHBoxLayout() capturei_field = QSpinBox(self) capturei_field.setRange(100, 2000) capturei_field.setSingleStep(100) capturei_field.setValue(100) capturei_layout.addWidget(capturei_field, 1) capturei_unit = QLabel("ms", self) capturei_layout.addWidget(capturei_unit, 0) layout.addRow(capturei_label, capturei_layout)
class MainWindow(QMainWindow): def __init__(self): super().__init__() form = QFormLayout() self.track_id = QSpinBox() self.track_id.setRange(0, 2147483647) self.track_id.setDisabled(True) self.name = QLineEdit() self.album = QComboBox() self.media_type = QComboBox() self.genre = QComboBox() self.composer = QLineEdit() self.milliseconds = QSpinBox() self.milliseconds.setRange(0, 2147483647) # <1> self.milliseconds.setSingleStep(1) self.bytes = QSpinBox() self.bytes.setRange(0, 2147483647) self.bytes.setSingleStep(1) self.unit_price = QDoubleSpinBox() self.unit_price.setRange(0, 999) self.unit_price.setSingleStep(0.01) self.unit_price.setPrefix("$") form.addRow(QLabel("Track ID"), self.track_id) form.addRow(QLabel("Track name"), self.name) form.addRow(QLabel("Composer"), self.composer) form.addRow(QLabel("Milliseconds"), self.milliseconds) form.addRow(QLabel("Bytes"), self.bytes) form.addRow(QLabel("Unit Price"), self.unit_price) self.model = QSqlTableModel(db=db) self.mapper = QDataWidgetMapper() # <2> self.mapper.setModel(self.model) self.mapper.addMapping(self.track_id, 0) # <3> self.mapper.addMapping(self.name, 1) self.mapper.addMapping(self.composer, 5) self.mapper.addMapping(self.milliseconds, 6) self.mapper.addMapping(self.bytes, 7) self.mapper.addMapping(self.unit_price, 8) self.model.setTable("Track") self.model.select() # <4> self.mapper.toFirst() # <5> self.setMinimumSize(QSize(400, 400)) widget = QWidget() widget.setLayout(form) self.setCentralWidget(widget)
def __init__(self, comms, parent): super().__init__(parent) toolbar = QHBoxLayout() clearButton = QPushButton("Clear") clearButton.clicked.connect(self.clear.emit) level = QComboBox() level.addItems(LEVELS) level.currentIndexChanged.connect(self.changeLevel.emit) maxBlock = QSpinBox() maxBlock.setMaximum(1000000) maxBlock.setSingleStep(10) maxBlock.valueChanged.connect(self.setSize.emit) maxBlock.setValue(1000) maxBlock.setMinimumWidth(100) toolbar.addWidget(clearButton) toolbar.addWidget(QLabel("Level")) toolbar.addWidget(level) toolbar.addWidget(QLabel("Current")) def addLevelLabel(comm): currentLevel = QLabel("---") toolbar.addWidget(currentLevel) comm.logLevel.connect(lambda data: currentLevel.setText(LEVELS[ _levelToIndex(data.level)])) if _isCommArray(comms): for comm in comms: addLevelLabel(comm) else: addLevelLabel(comms) toolbar.addWidget(QLabel("Max lines")) toolbar.addWidget(maxBlock) toolbar.addStretch() if issubclass(type(parent), QDockWidget): floatButton = QPushButton( self.style().standardIcon(QStyle.SP_TitleBarNormalButton), "") def _toggleFloating(): parent.setFloating(not parent.isFloating()) floatButton.clicked.connect(_toggleFloating) closeButton = QPushButton( self.style().standardIcon(QStyle.SP_TitleBarCloseButton), "") closeButton.clicked.connect(parent.close) toolbar.addWidget(floatButton) toolbar.addWidget(closeButton) self.setLayout(toolbar)
def __init__(self): super().__init__() widget = QSpinBox() # QDoubleSpinBox() widget.setRange(-10, 10) widget.setPrefix("$") widget.setSuffix("c") widget.setSingleStep(1) # 0.1 widget.valueChanged.connect(self.value_changed) widget.valueChanged[str].connect(self.value_changed_str) self.setCentralWidget(widget)
class IntEditor(QWidget): def __init__(self, minval, maxval, stepsize=1, parent=None): super(IntEditor, self).__init__(parent) # Editfield self.edit = QSpinBox(self) self.edit.setKeyboardTracking(False) self.edit.setRange(minval, maxval) self.edit.setSingleStep(stepsize) self.edit.setMinimumWidth(70) # Slider self.slider = QSlider(orientation=Qt.Vertical, parent=self) self.slider.setRange(minval, maxval) self.slider.setSingleStep(stepsize) self.slider.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) # Ticks q_ticks = QSlider.TicksBelow self.slider.setTickPosition(q_ticks) tick_amount = 6 tick_interval = int(np.floor((maxval - minval) / tick_amount)) self.slider.setTickInterval(tick_interval) # ...labels font = QFont() font.setPointSize(8) tick_str1 = QLabel('{:d}'.format(minval), self) tick_str2 = QLabel('{:d}'.format(maxval), self) tick_str1.setFont(font) tick_str2.setFont(font) slider_tick_layout = QGridLayout() tick_str1.setAlignment(Qt.AlignBottom | Qt.AlignLeft) tick_str2.setAlignment(Qt.AlignTop | Qt.AlignLeft) slider_tick_layout.addWidget(self.slider, 0, 0, tick_amount, 1) slider_tick_layout.addWidget(tick_str2, 0, 1, 1, 1) slider_tick_layout.addWidget(tick_str1, tick_amount - 1, 1, 1, 1) # Layout layout = QVBoxLayout() layout.addLayout(slider_tick_layout) layout.addWidget(self.edit) self.setLayout(layout) # Signals and slots self.edit.valueChanged.connect(self.slider.setValue) self.slider.valueChanged.connect(self.edit.setValue)
def __init__(self): super().__init__() self.setWindowTitle("Line edit") widget = QSpinBox() # QDoubleSpinBox is for floats widget.setMinimum(-10) widget.setMaximum(3) # or: widget.setRange(-10, 3) widget.setPrefix("$") widget.setSuffix("c") widget.setSingleStep(3) widget.valueChanged.connect(self.value_changed) widget.valueChanged[str].connect(self.value_changed_str) self.setCentralWidget(widget)
class MillerArrayTableForm(QDialog): def __init__(self, parent=None): super(MillerArrayTableForm, self).__init__(parent.window) self.setWindowFlag(Qt.WindowContextHelpButtonHint, False) self.setWindowTitle("Tabulated Reflection Data") self.precision_spinBox = QSpinBox() self.precision_spinBox.setSingleStep(1) self.precision_spinBox.setRange(1, 20) self.precision_spinBox.setValue(3) self.precision_spinBox.valueChanged.connect(parent.onPrecisionChanged) precision_labeltxt = QLabel() precision_labeltxt.setText("Precision:") self.SortComboBox = QComboBox() self.SortComboBox.activated.connect(parent.onSortComboBoxSelchange) sort_labeltxt = QLabel() sort_labeltxt.setText("Sort according to:") self.sortChkbox = QCheckBox() self.sortChkbox.setCheckState(Qt.Unchecked) self.sortChkbox.setText("Ascending order") self.sortChkbox.clicked.connect(parent.onSortChkbox) self.myGroupBox = QGroupBox() self.layout = QGridLayout() self.layout.addWidget(precision_labeltxt, 0, 0, 1, 1) self.layout.addWidget(self.precision_spinBox, 0, 1, 1, 1) self.layout.addWidget(sort_labeltxt, 0, 2, 1, 1) self.layout.addWidget(self.SortComboBox, 0, 3, 1, 1) self.layout.addWidget(self.sortChkbox, 0, 4, 1, 1) self.layout.addWidget(parent.millerarraytable, 1, 0, 1, 5) self.layout.setColumnStretch(0, 0) self.layout.setColumnStretch(1, 0) self.layout.setColumnStretch(2, 0) self.layout.setColumnStretch(3, 0) self.layout.setColumnStretch(4, 1) self.myGroupBox.setLayout(self.layout) self.mainLayout = QGridLayout() self.mainLayout.addWidget(self.myGroupBox, 0, 0) self.setLayout(self.mainLayout) def eventFilter(self, source, event): if (event.type() == QEvent.KeyPress and event.matches(QKeySequence.Copy)): self.parent().parent.millerarraytable.copySelection() return True return super(MillerArrayTableForm, self).eventFilter(source, event)
def __init__(self): super().__init__() self.setWindowTitle("My App") widget = QSpinBox() # Or: widget = QDoubleSpinBox() widget.setMinimum(-10) widget.setMaximum(3) # Or: widget.setRange(-10,3) widget.setPrefix("$") widget.setSuffix("c") widget.setSingleStep(3) # Or e.g. 0.5 for QDoubleSpinBox widget.valueChanged.connect(self.value_changed) widget.textChanged.connect(self.value_changed_str) self.setCentralWidget(widget)
class DaemonIntervalControls(QWidget): def __init__(self, tool): QWidget.__init__(self) self.tool = tool self.layout = QBoxLayout(QBoxLayout.LeftToRight) self.layout.addWidget(QLabel("Update interval: ")) self.period_select = QSpinBox() self.period_select.setRange(100, 1000 * 60 * 240) # 240 seconds max self.period_select.setSingleStep(100) self.layout.addWidget(self.period_select) self.layout.addWidget(QLabel("ms")) self.layout.setStretch(1, 1) self.setLayout(self.layout)
def create_rating_ticker(self, i, rating): """Creates the rating ticker that is displayed on the Local Storage View tab's QTableView. Each QSpinBox is mapped to a row, representing the rating of the patch. i: The current row the buttons are being created for. rating: The current rating associated with the selected patch. """ if rating < 0 or rating > 5: raise errors.SortingError(rating, 901) rate_tkr = QSpinBox() rate_tkr.setValue(rating) rate_tkr.setRange(0, 5) rate_tkr.setSingleStep(1) rate_tkr.valueChanged.connect(self.update_rating) rate_tkr.setFont(self.ui.table_PS.horizontalHeader().font()) self.ui.table_local.setCellWidget(i, 4, rate_tkr)
class StyleWidget(QWidget): style_changed = Signal(SvgStyle) def __init__(self, parent=None): super(StyleWidget, self).__init__(parent) self.label_line_length = QLabel(u'Длина линии') self.spinbox_line_length = QSpinBox() self.spinbox_line_length.setMinimum(10) self.spinbox_line_length.setMaximum(250) self.spinbox_line_length.setSingleStep(10) self.spinbox_line_length.setValue(200) self.label_line_width = QLabel(u'Толщина линии') self.spinbox_line_width = QSpinBox() self.spinbox_line_width.setMinimum(1) self.spinbox_line_width.setMaximum(10) self.spinbox_line_width.setSingleStep(1) self.spinbox_line_width.setValue(2) self.label_circle_rad = QLabel(u'Радиус круга') self.spinbox_circle_rad = QSpinBox() self.spinbox_circle_rad.setMinimum(1) self.spinbox_circle_rad.setMaximum(30) self.spinbox_circle_rad.setSingleStep(1) self.spinbox_circle_rad.setValue(10) self.label_line_color = QLabel(u'Цвет линии') self.combobox_line_color = QComboBox() self.combobox_line_color.addItem(u'черный', 'black') self.combobox_line_color.addItem(u'красный', 'red') self.combobox_line_color.addItem(u'зеленый', 'green') self.combobox_line_color.addItem(u'синий', 'blue') self.checkbox_normalize_circle_rad = QCheckBox(u'Круги одинакового размера') layout = QGridLayout() layout.addWidget(self.label_line_length, 0, 0, Qt.AlignRight) layout.addWidget(self.spinbox_line_length, 0, 1) layout.addWidget(self.label_line_width, 1, 0, Qt.AlignRight) layout.addWidget(self.spinbox_line_width, 1, 1) layout.addWidget(self.label_circle_rad, 2, 0, Qt.AlignRight) layout.addWidget(self.spinbox_circle_rad, 2, 1) layout.addWidget(self.label_line_color, 3, 0, Qt.AlignRight) layout.addWidget(self.combobox_line_color, 3, 1) layout.addWidget(self.checkbox_normalize_circle_rad, 4, 0, 1, 2) layout.setRowStretch(5, 1) self.setLayout(layout) self.spinbox_line_length.valueChanged.connect(self.on_change) self.spinbox_line_width.valueChanged.connect(self.on_change) self.spinbox_circle_rad.valueChanged.connect(self.on_change) self.combobox_line_color.activated.connect(self.on_change) self.checkbox_normalize_circle_rad.stateChanged.connect(self.on_change) self.on_change() @Slot() def on_change(self): style = SvgStyle() style.line_length = self.spinbox_line_length.value() style.line_width = self.spinbox_line_width.value() style.circle_stroke_width = self.spinbox_line_width.value() style.circle_rad = self.spinbox_circle_rad.value() style.line_color = self.combobox_line_color.currentData(Qt.UserRole) style.circle_stroke_color = self.combobox_line_color.currentData(Qt.UserRole) style.circle_rad_normalization = not self.checkbox_normalize_circle_rad.isChecked() self.style_changed.emit(style)
def __init__(self, m1m3): super().__init__() self.m1m3 = m1m3 self.layout = QVBoxLayout() dataLayout = QGridLayout() self.layout.addLayout(dataLayout) self.setLayout(self.layout) dataLayout.addWidget(QLabel("<b>Hardpoint</b>"), 0, 0) for hp in range(1, 7): dataLayout.addWidget(QLabel(f"<b>{hp}</b>"), 0, hp) self.variables = { "stepsQueued": ("Steps queued", UnitLabel()), "stepsCommanded": ("Steps commanded", UnitLabel()), "encoder": ("Encoder", UnitLabel()), "measuredForce": ("Measured force", Force(".03f")), "displacement": ("Displacement", Mm()), } row = 1 def addRow(textValue, row): ret = [] dataLayout.addWidget(QLabel(textValue[0]), row, 0) for hp in range(6): label = copy.copy(textValue[1]) dataLayout.addWidget(label, row, 1 + hp) ret.append(label) return ret for k, v in self.variables.items(): setattr(self, k, addRow(v, row)) row += 1 dataLayout.addWidget(QLabel("Encoder offsets"), row, 0) self.hpOffsets = [] for hp in range(6): sb = QSpinBox() sb.setRange(-(1 << 16), 1 << 16) sb.setSingleStep(100) dataLayout.addWidget(sb, row, 1 + hp) self.hpOffsets.append(sb) row += 1 self.moveHPButton = QPushButton("Move") self.moveHPButton.clicked.connect(self._moveHP) dataLayout.addWidget(self.moveHPButton, row, 1, 1, 3) reset = QPushButton("Reset") reset.clicked.connect(self._reset) dataLayout.addWidget(reset, row, 4, 1, 3) row += 1 self.monitorData = { "breakawayLVDT": ("Breakaway LVDT", UnitLabel(".02f")), "displacementLVDT": ("Displacement LVDT", UnitLabel(".02f")), "breakawayPressure": ("Breakaway Pressure", UnitLabel(".02f")), "pressureSensor1": ("Pressure Sensor 1", UnitLabel(".04f")), "pressureSensor2": ("Pressure Sensor 2", UnitLabel(".04f")), "pressureSensor3": ("Pressure Sensor 3", UnitLabel(".04f")), } for k, v in self.monitorData.items(): setattr(self, k, addRow(v, row)) row += 1 self.warnings = { "majorFault": ("Major fault", WarningLabel()), "minorFault": ("Minor fault", WarningLabel()), "faultOverride": ("Fault override", WarningLabel()), "mainCalibrationError": ("Main calibration error", WarningLabel()), "backupCalibrationError": ("Backup calibration error", WarningLabel()), "limitSwitch1Operated": ("Limit switch 1", WarningLabel()), "limitSwitch2Operated": ("Limit switch 2", WarningLabel()), } for k, v in self.warnings.items(): setattr(self, k, addRow(v, row)) row += 1 dataLayout.addWidget(QLabel("Motion state"), row, 0) self.hpStates = [] for hp in range(6): self.hpStates.append(QLabel()) dataLayout.addWidget(self.hpStates[hp], row, hp + 1) row += 1 self.forces = { "forceMagnitude": ("Total force", Force()), "fx": ("Force X", Force()), "fy": ("Force Y", Force()), "fz": ("Force Z", Force()), "mx": ("Moment X", Moment()), "my": ("Moment Y", Moment()), "mz": ("Moment Z", Moment()), } dataLayout.addWidget(QLabel(), row, 0) row += 1 def addDataRow(variables, row, col=0): for k, v in variables.items(): dataLayout.addWidget(QLabel(f"<b>{v[0]}</b>"), row, col) setattr(self, k, v[1]) dataLayout.addWidget(v[1], row + 1, col) col += 1 addDataRow(self.forces, row) row += 2 dataLayout.addWidget(QLabel(), row, 0) row += 1 self.positions = { "xPosition": ("Position X", Mm()), "yPosition": ("Position Y", Mm()), "zPosition": ("Position Z", Mm()), "xRotation": ("Rotation X", Mm()), "yRotation": ("Rotation Y", Mm()), "zRotation": ("Rotation Z", Mm()), } addDataRow(self.positions, row, 1) self.layout.addStretch() self.m1m3.detailedState.connect(self.detailedState) self.m1m3.hardpointActuatorData.connect(self.hardpointActuatorData) self.m1m3.hardpointActuatorState.connect(self.hardpointActuatorState) self.m1m3.hardpointMonitorData.connect(self.hardpointMonitorData) self.m1m3.hardpointActuatorWarning.connect( self.hardpointActuatorWarning)
def get_widget_by_type(obj): if type(obj) == int: widget = QSpinBox() widget.setRange(INT_RANGE_MIN, INT_RANGE_MAX) widget.setSingleStep(INT_RANGE_STEP) widget.setValue(obj) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) elif type(obj) == float: widget = QDoubleSpinBox() widget.setRange(FLOAT_RANGE_MIN, FLOAT_RANGE_MAX) widget.setSingleStep(FLOAT_RANGE_STEP) widget.setValue(obj) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) elif type(obj) == str: widget = QLineEdit() widget.setText(obj) elif type(obj) == bool: widget = QCheckBox() widget.setChecked(obj) elif type(obj) == list: widget = QComboBox() widget.addItems(obj) widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) elif type(obj) == tuple: widget = QFrame() if len(obj) <= 3: box = QHBoxLayout() else: box = QVBoxLayout() box.setMargin(0) for item in obj: value_widget = QLabel(f"{item}") value_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) value_widget.setObjectName("tuple") box.addWidget(value_widget) widget.setLayout(box) elif type(obj) == dict: widget = QFrame() # If less than 3 items, lay it out horizontally else vertically # if len(obj) <= 3: # box = QHBoxLayout() # else: # box = QVBoxLayout() # box.setMargin(0) grid = QGridLayout() grid.setMargin(0) row = 0 for key in obj: label = QLabel(f"{key.capitalize()}:") grid.addWidget(label, row, 0) value_widget = get_widget_by_type(obj[key]) value_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) grid.addWidget(value_widget, row, 1) try: value_widget.setRange(INT_RANGE_MIN, INT_RANGE_MAX) value_widget.setSingleStep(INT_RANGE_STEP) value_widget.setValue(obj[key]) except: pass row += 1 widget.setLayout(grid) # TODO: Lists inside of lists. Should probably use QTreeView # elif type(obj) == list: # widget = [] # for l in obj: # widget.append(QComboBox()) # widget[-1].addItems(obj) # widget[-1].setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) return widget
class MainWindow(QMainWindow): def __init__(self): super().__init__() layout = QVBoxLayout() form = QFormLayout() self.track_id = QSpinBox() self.track_id.setDisabled(True) self.name = QLineEdit() self.album = QComboBox() self.media_type = QComboBox() self.genre = QComboBox() self.composer = QLineEdit() self.milliseconds = QSpinBox() self.milliseconds.setRange(0, 2147483647) self.milliseconds.setSingleStep(1) self.bytes = QSpinBox() self.bytes.setRange(0, 2147483647) self.bytes.setSingleStep(1) self.unit_price = QDoubleSpinBox() self.unit_price.setRange(0, 999) self.unit_price.setSingleStep(0.01) self.unit_price.setPrefix("$") form.addRow(QLabel("Track ID"), self.track_id) form.addRow(QLabel("Track name"), self.name) form.addRow(QLabel("Composer"), self.composer) form.addRow(QLabel("Milliseconds"), self.milliseconds) form.addRow(QLabel("Bytes"), self.bytes) form.addRow(QLabel("Unit Price"), self.unit_price) self.model = QSqlTableModel(db=db) self.mapper = QDataWidgetMapper() self.mapper.setModel(self.model) self.mapper.addMapping(self.track_id, 0) self.mapper.addMapping(self.name, 1) self.mapper.addMapping(self.composer, 5) self.mapper.addMapping(self.milliseconds, 6) self.mapper.addMapping(self.bytes, 7) self.mapper.addMapping(self.unit_price, 8) self.model.setTable("Track") self.model.select() self.mapper.toFirst() self.setMinimumSize(QSize(400, 400)) controls = QHBoxLayout() # tag::controls[] prev_rec = QPushButton("Previous") prev_rec.clicked.connect(self.mapper.toPrevious) next_rec = QPushButton("Next") next_rec.clicked.connect(self.mapper.toNext) save_rec = QPushButton("Save Changes") save_rec.clicked.connect(self.mapper.submit) # end::controls[] controls.addWidget(prev_rec) controls.addWidget(next_rec) controls.addWidget(save_rec) layout.addLayout(form) layout.addLayout(controls) widget = QWidget() widget.setLayout(layout) self.setCentralWidget(widget)
class CourseEditDialog(QDialog): def __init__(self, course, is_new=False): super().__init__(GlobalAccess().get_main_window()) assert (isinstance(course, Course)) self.current_object = course self.is_new = is_new def exec_(self): self.init_ui() self.set_values_from_model() return super().exec_() def init_ui(self): self.setWindowTitle(_('Course properties')) self.setWindowIcon(QIcon(config.ICON)) self.setSizeGripEnabled(False) self.setModal(True) self.layout = QFormLayout(self) self.label_name = QLabel(_('Name')) self.item_name = QLineEdit() self.item_name.textChanged.connect(self.check_name) self.layout.addRow(self.label_name, self.item_name) self.label_length = QLabel(_('Length(m)')) self.item_length = QSpinBox() self.item_length.setMaximum(100000) self.item_length.setSingleStep(100) self.item_length.setValue(0) self.layout.addRow(self.label_length, self.item_length) self.label_climb = QLabel(_('Climb')) self.item_climb = QSpinBox() self.item_climb.setValue(0) self.item_climb.setMaximum(10000) self.item_climb.setSingleStep(10) self.layout.addRow(self.label_climb, self.item_climb) self.label_control_qty = QLabel(_('Point count')) self.item_control_qty = QSpinBox() self.item_control_qty.setDisabled(True) self.layout.addRow(self.label_control_qty, self.item_control_qty) self.label_controls = QLabel( '{}\n\n31 150\n32 200\n33\n34 500\n...\n90 150'.format( _('Controls'))) self.item_controls = QTextEdit() self.item_controls.setTabChangesFocus(True) self.layout.addRow(self.label_controls, self.item_controls) def cancel_changes(): self.close() def apply_changes(): try: self.apply_changes_impl() except Exception as e: logging.error(str(e)) self.close() button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = button_box.button(QDialogButtonBox.Ok) self.button_ok.setText(_('OK')) self.button_ok.clicked.connect(apply_changes) self.button_cancel = button_box.button(QDialogButtonBox.Cancel) self.button_cancel.setText(_('Cancel')) self.button_cancel.clicked.connect(cancel_changes) self.layout.addRow(button_box) self.show() def check_name(self): name = self.item_name.text() self.button_ok.setDisabled(False) if name and name != self.current_object.name: org = find(race().courses, name=name) if org: self.button_ok.setDisabled(True) def set_values_from_model(self): self.item_name.setText(self.current_object.name) if self.current_object.length: self.item_length.setValue(self.current_object.length) if self.current_object.climb: self.item_climb.setValue(self.current_object.climb) if self.current_object.controls: self.item_control_qty.setValue(len(self.current_object.controls)) for i in self.current_object.controls: assert isinstance(i, CourseControl) self.item_controls.append('{} {}'.format( i.code, i.length if i.length else '')) def apply_changes_impl(self): course = self.current_object if self.is_new: race().courses.insert(0, course) if course.name != self.item_name.text(): course.name = self.item_name.text() if course.length != self.item_length.value(): course.length = self.item_length.value() if course.climb != self.item_climb.value(): course.climb = self.item_climb.value() text = self.item_controls.toPlainText() course.controls.clear() for i in text.split('\n'): control = CourseControl() if i is None or len(i) == 0: continue control.code = i.split()[0] if len(i.split()) > 1: try: control.length = int(i.split()[1]) except Exception as e: logging.error(str(e)) control.length = 0 course.controls.append(control) obj = race() ResultChecker.check_all() ResultCalculation(obj).process_results() RaceSplits(obj).generate() ScoreCalculation(obj).calculate_scores() Teamwork().send(course.to_dict())
class TestWidget(QWidget): def __init__(self, parent=None, options=None): super(TestWidget, self).__init__(parent) self.options = options self.results = [] self.tests = [] self.index = 0 self.total_tests = 1 self.total_try = 0 self.total_spin = QSpinBox(self) self.total_spin.setMinimum(10) self.total_spin.setMaximum(1000) self.total_spin.setValue(self.options.total_default) self.total_spin.setSingleStep(10) # for sync two total spins self.options.total_spin_test = self.total_spin self.index_label = QLabel(self.tr('Test #')) self.start = QPushButton(self.tr('Start')) self.stop = QPushButton(self.tr('Stop')) self.next = QPushButton(self.tr('Next')) self.clear = QPushButton(self.tr('Clear')) self.correct = QLabel() self.smile_face = QPixmap(self.resource_path('./images/smile.png')) self.sad_face = QPixmap(self.resource_path('./images/sad.png')) self.correct.setPixmap(self.smile_face) self.start.setDefault(True) self.next.setEnabled(False) self.clear.setEnabled(False) self.stop.setEnabled(False) self.formula = QLineEdit() self.formula.setAlignment(Qt.AlignRight) self.formula.setReadOnly(True) min_height = 50 self.formula.setMinimumHeight(min_height) self.answer = QLineEdit() self.answer.setMinimumHeight(min_height) self.answer.setFixedWidth(70) self.answer.setValidator(QIntValidator()) self.keyboard_cb = QCheckBox(self.tr('Keyboard')) self.keyboard_cb.setChecked(True) self.keyboard_cb.stateChanged.connect(self.show_keyboard) keyboard_layout = QGridLayout() for i, row in enumerate( ((7, 8, 9), (4, 5, 6), (1, 2, 3), (0, 'C', '='))): for j, key in enumerate(row): btn = QPushButton(str(key)) if key == 'C': btn.clicked.connect(self.key_clear) elif key == '=': btn.clicked.connect(self.next_test) else: btn.clicked.connect(self.key_enter) keyboard_layout.addWidget(btn, i, j) self.keyboard_box = QGroupBox() self.keyboard_box.setLayout(keyboard_layout) layout = QGridLayout() row = 0 hbox = QHBoxLayout() hbox.addWidget(QLabel(self.tr('Total tests'))) hbox.addWidget(self.total_spin) layout.addWidget(self.index_label, row, 0) layout.addLayout(hbox, row, 1) layout.addWidget(self.start, row, 2) layout.addWidget(self.stop, row, 3) layout.addWidget(self.keyboard_cb, row, 4) layout.addWidget(self.keyboard_box, row, 5, 3, 1) row += 1 layout.addWidget(self.formula, row, 0, 1, 3) layout.addWidget(self.answer, row, 3) layout.addWidget(self.correct, row, 4) row += 1 layout.addWidget(self.clear, row, 3) layout.addWidget(self.next, row, 4) row += 1 layout.setColumnStretch(0, 1) self.setLayout(layout) self.start.clicked.connect(self.start_test) self.stop.clicked.connect(self.stop_test) self.next.clicked.connect(self.next_test) self.clear.clicked.connect(self.key_clear) self.total_spin.valueChanged.connect(self.sync_total) self.answer.returnPressed.connect(self.next_test) def resource_path(self, relative_path): try: # PyInstaller creates a temp folder and stores path in _MEIPASS base_path = sys._MEIPASS except Exception: base_path = os.path.abspath(".") if re.match(r'Windows', platform.platform()): return str(PureWindowsPath("%s/%s" % (base_path, relative_path))) else: return os.path.join(base_path, relative_path) @Slot() def show_keyboard(self): if self.keyboard_cb.isChecked(): self.keyboard_box.setVisible(True) else: self.keyboard_box.setVisible(False) @Slot() def key_clear(self): self.answer.clear() @Slot() def key_enter(self): self.answer.setText('%s%s' % (self.answer.text(), self.sender().text())) @Slot() def sync_total(self): self.options.total.setValue(self.total_spin.value()) @Slot() def next_test(self): answer = self.answer.text() if not answer: self.options.err_dialog(self.tr('must answer before click next')) return None last_formula = self.tests[self.index] correct = int(answer) == formula.eval_expr(last_formula) if correct: self.correct.setPixmap(self.smile_face) self.answer.clear() self.index += 1 if self.index < self.total_tests: self.set_test(self.index) self.index_label.setText(self.tr('Test %d' % (self.index + 1))) else: self.correct.setPixmap(self.sad_face) self.total_try += 1 msg = self.tr('Last: %s = %s Rate: %s' % (last_formula, answer, self.correct_rate())) self.status_bar.showMessage(self.convert_operator(msg)) if self.index == self.total_tests: self.show_summary() self.stop_test() return def correct_rate(self): return '{0:.0%}'.format(self.index / self.total_try) def show_summary(self): msg = self.tr('Total attempt: %s Correct: %s Rate: %s' % (self.total_try, self.index, self.correct_rate())) self.options.info_dialog(msg) @Slot() def start_test(self): (filename, upper_limit, lower_limit, n_number, total_tests, operators) = self.options.collect_input() self.total_tests = total_tests # skip filename check in test mode filename = True err_msg = self.options.check_input(filename, upper_limit, lower_limit, operators) if err_msg: self.options.err_dialog(err_msg) else: self.tests, self.results = formula.gen_test( operators, upper_limit, lower_limit, n_number, total_tests) for w in (self.next, self.start, self.stop, self.clear): self.toggle_enable(w) self.set_test(self.index) self.index_label.setText(self.tr('Test %d' % (self.index + 1))) self.next.setDefault(True) def set_test(self, index): text = self.convert_operator(self.tests[index]) self.formula.setText(text) def convert_operator(self, formula): text = re.sub(r'\*', '×', formula) return re.sub(r'\/', '÷', text) def toggle_enable(self, w): if w.isEnabled(): w.setEnabled(False) else: w.setEnabled(True) @Slot() def stop_test(self): for w in (self.next, self.start, self.stop, self.clear): self.toggle_enable(w) self.formula.clear() self.answer.clear() self.status_bar.clearMessage() self.index = 0 self.correct_try = 0 self.total_try = 0 self.index_label.setText(self.tr('Test #'))
class SheepGame(QWidget): def __init__(self): super().__init__() self.layout = QVBoxLayout() self.n_move = 0 self.title = QLabel() self.layout.addWidget(self.title) self.layoutSheeps = QHBoxLayout() self.widgetSheeps = QWidget() self.sheep_selected = None self.init_sheeps(5) self.update_title() self.update_sheep() self.widgetSheeps.setLayout(self.layoutSheeps) self.layout.addWidget(self.widgetSheeps) self.num = QSpinBox() self.num.setRange(3, 7) self.num.setSingleStep(2) self.num.setValue(5) self.num.valueChanged.connect(self.modify_num_sheep) self.layout.addWidget(self.num) self.btn_auto = QPushButton("Auto") self.btn_auto.clicked.connect(self.auto) self.layout.addWidget(self.btn_auto) self.setLayout(self.layout) def is_victory(self): if self.get_sheep(len(self.sheeps) // 2).color != Sheep.EMPTY: return False for x1 in range(len(self.sheeps)): for x2 in range(x1 + 1, len(self.sheeps)): if self.get_sheep(x1).color == Sheep.WHITE and self.get_sheep( x2).color == Sheep.BLACK: return False return True def update_title(self): if self.is_victory(): self.title.setText(f"Victoire en {self.n_move} tours") for sheep in self.sheeps: sheep.changed_color(True, Qt.green) else: self.title.setText(f"Tours {self.n_move}") def get_sheep(self, position): for sheep in self.sheeps: if sheep.position == position: return sheep return None def modify_num_sheep(self, size): for i in reversed(range(self.layoutSheeps.count())): self.layoutSheeps.itemAt(i).widget().setParent(None) self.n_move = 0 self.init_sheeps(size) self.update_sheep() self.update_title() def init_sheeps(self, size): self.sheeps = [] for i in range(0, size // 2): self.sheeps.append(Sheep(Sheep.WHITE, i)) self.sheeps.append(Sheep(Sheep.EMPTY, size // 2)) for i in range(0, size // 2): self.sheeps.append(Sheep(Sheep.BLACK, i + 1 + size // 2)) for sheep in self.sheeps: sheep.selected.connect(self.move) def move(self, sheep): if self.sheep_selected is None: self.sheep_selected = sheep self.sheep_selected.changed_color(True) elif self.sheep_selected == sheep: self.sheep_selected.changed_color(False) self.sheep_selected = None else: if self.move_possible(self.sheep_selected, sheep): self.switch(self.sheep_selected, sheep) self.n_move += 1 self.sheep_selected.changed_color(False) else: self.sheep_selected.changed_color(True, Qt.red) QTimer.singleShot(1000, self.sheep_selected.changed_color) self.sheep_selected = None self.update_sheep() self.update_title() def switch(self, sheep1, sheep2): tmp = sheep1.position sheep1.position = sheep2.position sheep2.position = tmp self.update_sheep() self.update_title() def move_possible(self, sheep1, sheep2): dif = sheep1.position - sheep2.position if sheep2.color != Sheep.EMPTY: return False elif dif > 0 and abs(dif) <= 2 and sheep1.color == Sheep.BLACK: return True elif dif < 0 and abs(dif) <= 2 and sheep1.color == Sheep.WHITE: return True return False def update_sheep(self): for sheep in sorted(self.sheeps, key=lambda x: x.position): self.layoutSheeps.addWidget(sheep) def move_auto(self, transfered, i=1): transfer = transfered[i] if self.get_sheep(transfer[0] - 1).color == Sheep.EMPTY: self.move(self.get_sheep(transfer[1] - 1)) self.move(self.get_sheep(transfer[0] - 1)) else: self.move(self.get_sheep(transfer[0] - 1)) self.move(self.get_sheep(transfer[1] - 1)) self.update_sheep() self.update_title() if i != len(transfered) - 1: QTimer.singleShot(500, lambda: self.move_auto(transfered, i + 1)) def auto(self): from minizinc import Instance, Model, Solver mouton = Model("./mouton.mzn") gecode = Solver.lookup("gecode") instance = Instance(gecode, mouton) instance["N"] = len(self.sheeps) // 2 if instance["N"] == 1: instance["max_steps"] = 4 elif instance["N"] == 2: instance["max_steps"] = 9 else: instance["max_steps"] = 16 result = instance.solve() if result is not None: self.move_auto(result["transfered"]) else: QMessageBox.information(self, "Solution", f"Le problème est insoluble")
class GroupEditDialog(QDialog): def __init__(self, group, is_new=False): super().__init__(GlobalAccess().get_main_window()) assert (isinstance(group, Group)) self.current_object = group self.is_new = is_new self.time_format = 'hh:mm:ss' def exec_(self): self.init_ui() self.set_values_from_model() return super().exec_() def init_ui(self): self.setWindowTitle(_('Group properties')) self.setWindowIcon(QIcon(config.ICON)) self.setSizeGripEnabled(False) self.setModal(True) self.layout = QFormLayout(self) self.label_name = QLabel(_('Name')) self.item_name = QLineEdit() self.item_name.textChanged.connect(self.check_name) self.layout.addRow(self.label_name, self.item_name) self.label_full_name = QLabel(_('Full name')) self.item_full_name = QLineEdit() self.layout.addRow(self.label_full_name, self.item_full_name) self.label_course = QLabel(_('Course')) self.item_course = AdvComboBox() self.item_course.addItems(get_race_courses()) self.layout.addRow(self.label_course, self.item_course) self.label_is_any_course = QLabel(_('Is any course')) self.item_is_any_course = QCheckBox() self.item_is_any_course.stateChanged.connect(self.is_any_course_update) self.layout.addRow(self.label_is_any_course, self.item_is_any_course) self.label_sex = QLabel(_('Sex')) self.item_sex = AdvComboBox() self.item_sex.addItems(Sex.get_titles()) self.layout.addRow(self.label_sex, self.item_sex) self.label_age_min = QLabel(_('Min age')) self.item_age_min = QSpinBox() # self.layout.addRow(self.label_age_min, self.item_age_min) self.label_age_max = QLabel(_('Max age')) self.item_age_max = QSpinBox() # self.layout.addRow(self.label_age_max, self.item_age_max) self.label_year_min = QLabel(_('Min year')) self.item_year_min = QSpinBox() self.item_year_min.setMaximum(date.today().year) self.item_year_min.editingFinished.connect(self.year_change) self.layout.addRow(self.label_year_min, self.item_year_min) self.label_year_max = QLabel(_('Max year')) self.item_year_max = QSpinBox() self.item_year_max.setMaximum(date.today().year) self.item_year_max.editingFinished.connect(self.year_change) self.layout.addRow(self.label_year_max, self.item_year_max) self.label_max_time = QLabel(_('Max time')) self.item_max_time = QTimeEdit() self.item_max_time.setDisplayFormat(self.time_format) self.layout.addRow(self.label_max_time, self.item_max_time) self.label_corridor = QLabel(_('Start corridor')) self.item_corridor = QSpinBox() self.layout.addRow(self.label_corridor, self.item_corridor) self.label_corridor_order = QLabel(_('Order in corridor')) self.item_corridor_order = QSpinBox() self.layout.addRow(self.label_corridor_order, self.item_corridor_order) self.label_start_interval = QLabel(_('Start interval')) self.item_start_interval = QTimeEdit() self.item_start_interval.setDisplayFormat(self.time_format) self.layout.addRow(self.label_start_interval, self.item_start_interval) self.label_price = QLabel(_('Start fee')) self.item_price = QSpinBox() self.item_price.setSingleStep(50) self.item_price.setMaximum(Limit.PRICE) self.layout.addRow(self.label_price, self.item_price) self.type_label = QLabel(_('Type')) self.type_combo = AdvComboBox() self.type_combo.addItems(RaceType.get_titles()) self.layout.addRow(self.type_label, self.type_combo) self.rank_checkbox = QCheckBox(_('Rank calculation')) self.rank_button = QPushButton(_('Configuration')) self.layout.addRow(self.rank_checkbox, self.rank_button) def cancel_changes(): self.close() def apply_changes(): try: self.apply_changes_impl() except Exception as e: logging.error(str(e)) self.close() button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = button_box.button(QDialogButtonBox.Ok) self.button_ok.setText(_('OK')) self.button_ok.clicked.connect(apply_changes) self.button_cancel = button_box.button(QDialogButtonBox.Cancel) self.button_cancel.setText(_('Cancel')) self.button_cancel.clicked.connect(cancel_changes) self.layout.addRow(button_box) self.show() self.button_ok.setFocus() def check_name(self): name = self.item_name.text() self.button_ok.setDisabled(False) if name and name != self.current_object.name: group = find(race().groups, name=name) if group: self.button_ok.setDisabled(True) def year_change(self): """ Convert 2 digits of year to 4 2 -> 2002 11 - > 2011 33 -> 1933 56 -> 1956 98 - > 1998 0 -> 0 exception! """ widget = self.sender() assert isinstance(widget, QSpinBox) year = widget.value() if 0 < year < 100: cur_year = date.today().year new_year = cur_year - cur_year % 100 + year if new_year > cur_year: new_year -= 100 widget.setValue(new_year) def is_any_course_update(self): if self.item_is_any_course.isChecked(): self.item_course.setDisabled(True) else: self.item_course.setDisabled(False) def set_values_from_model(self): self.item_name.setText(self.current_object.name) if self.current_object.long_name: self.item_full_name.setText(self.current_object.long_name) if self.current_object.course: self.item_course.setCurrentText(self.current_object.course.name) if self.current_object.sex: self.item_sex.setCurrentText(self.current_object.sex.get_title()) if self.current_object.min_age: self.item_age_min.setValue(self.current_object.min_age) if self.current_object.max_age: self.item_age_max.setValue(self.current_object.max_age) if self.current_object.min_year: self.item_year_min.setValue(self.current_object.min_year) if self.current_object.max_year: self.item_year_max.setValue(self.current_object.max_year) if self.current_object.max_time: self.item_max_time.setTime( time_to_qtime(self.current_object.max_time)) if self.current_object.start_interval: self.item_start_interval.setTime( time_to_qtime(self.current_object.start_interval)) if self.current_object.start_corridor: self.item_corridor.setValue(self.current_object.start_corridor) if self.current_object.order_in_corridor: self.item_corridor_order.setValue( self.current_object.order_in_corridor) if self.current_object.price: self.item_price.setValue(self.current_object.price) self.item_is_any_course.setChecked(self.current_object.is_any_course) self.rank_checkbox.setChecked(self.current_object.ranking.is_active) self.type_combo.setCurrentText(race().get_type( self.current_object).get_title()) def rank_configuration(): group = self.current_object GroupRankingDialog(group).exec_() self.rank_button.clicked.connect(rank_configuration) def apply_changes_impl(self): group = self.current_object assert (isinstance(group, Group)) if self.is_new: race().groups.insert(0, group) if group.name != self.item_name.text(): group.name = self.item_name.text() if group.long_name != self.item_full_name.text(): group.long_name = self.item_full_name.text() if (group.course is not None and group.course.name != self.item_course.currentText()) \ or (group.course is None and len(self.item_course.currentText()) > 0): group.course = find(race().courses, name=self.item_course.currentText()) if group.sex.get_title() != self.item_sex.currentText(): group.sex = Sex(self.item_sex.currentIndex()) if group.min_age != self.item_age_min.value(): group.min_age = self.item_age_min.value() if group.max_age != self.item_age_max.value(): group.max_age = self.item_age_max.value() if group.min_year != self.item_year_min.value(): group.min_year = self.item_year_min.value() if group.max_year != self.item_year_max.value(): group.max_year = self.item_year_max.value() if group.start_corridor != self.item_corridor.value(): group.start_corridor = self.item_corridor.value() if group.order_in_corridor != self.item_corridor_order.value(): group.order_in_corridor = self.item_corridor_order.value() if group.price != self.item_price.value(): group.price = self.item_price.value() time = time_to_otime(self.item_start_interval.time()) if group.start_interval != time: group.start_interval = time time = time_to_otime(self.item_max_time.time()) if group.max_time != time: group.max_time = time if group.ranking.is_active != self.rank_checkbox.isChecked(): group.ranking.is_active = self.rank_checkbox.isChecked() t = RaceType.get_by_name(self.type_combo.currentText()) selected_type = t if t is not None else group.get_type() if group.get_type() != selected_type: group.set_type(selected_type) group.is_any_course = self.item_is_any_course.isChecked() ResultCalculation(race()).set_rank(group) Teamwork().send(group.to_dict())
class SaveWidget(QWidget): def __init__(self, parent=None): super(SaveWidget, self).__init__(parent) # Create widgets self.lower_label = QLabel(self.tr('Min')) self.lower_spin = QSpinBox(self) self.lower_spin.setMinimum(1) self.upper_label = QLabel(self.tr('Max')) self.upper_default = 10 self.upper_spin = QSpinBox(self) self.upper_spin.setMinimum(1) self.upper_spin.setValue(self.upper_default) self.n_label = QLabel(self.tr('Number per formula')) self.n_spin = QSpinBox(self) self.n_spin.setMinimum(2) self.n_spin.setMaximum(4) self.total_label = QLabel(self.tr('Total tests')) self.total = QSpinBox(self) self.total.setMinimum(10) self.total.setMaximum(1000) self.total_default = 100 self.total.setValue(self.total_default) self.total.setSingleStep(10) self.plus_cb = QCheckBox('+') self.minus_cb = QCheckBox('-') self.multi_cb = QCheckBox('×') self.divide_cb = QCheckBox('÷') self.plus_cb.setChecked(True) self.minus_cb.setChecked(True) self.file_name = self.tr('%s/kidsmath.xlsx' % str(Path.home())) self.file_label = QLabel(self.tr('Save File:')) self.file = QLineEdit(self.tr(self.file_name)) self.browse_btn = QPushButton(self.tr('Browse')) self.save_btn = QPushButton(self.tr('Save')) # Create layout and add widgets hbox = QHBoxLayout() hbox.addWidget(self.plus_cb) hbox.addWidget(self.minus_cb) hbox.addWidget(self.multi_cb) hbox.addWidget(self.divide_cb) self.group_box = QGroupBox(self.tr('Operators:')) self.group_box.setLayout(hbox) layout = QGridLayout() row = 0 layout.addWidget(QLabel(self.tr('Setting:')), row, 0) layout.addWidget(self.lower_label, row, 1) layout.addWidget(self.lower_spin, row, 2) layout.addWidget(self.upper_label, row, 3) layout.addWidget(self.upper_spin, row, 4) layout.addWidget(self.n_label, row, 5) layout.addWidget(self.n_spin, row, 6) layout.addWidget(self.total_label, row, 7) layout.addWidget(self.total, row, 8) layout.addWidget(self.group_box, row, 9, 1, 2) row += 1 layout.addWidget(self.file_label, row, 0) layout.addWidget(self.file, row, 1, 1, 8) layout.addWidget(self.browse_btn, row, 9) layout.addWidget(self.save_btn, row, 10) layout.setColumnStretch(8, 1) self.setLayout(layout) self.browse_btn.clicked.connect(self.set_file) self.save_btn.clicked.connect(self.save_file) self.total.valueChanged.connect(self.sync_total) @Slot() def sync_total(self): self.total_spin_test.setValue(self.total.value()) @Slot() def set_file(self): self.file_name, f = QFileDialog.getOpenFileName( self, self.tr('Save Ffile'), filter=self.tr('Spreadsheets (*.xlsx)')) if self.file_name: self.file.setText(self.file_name) @Slot() def save_file(self): (filename, upper_limit, lower_limit, n_number, total_tests, operators) = self.collect_input() err_msg = self.check_input(filename, upper_limit, lower_limit, operators) if err_msg: self.err_dialog(err_msg) else: split_num = formula.gen_split(n_number) tests, results = formula.gen_test(operators, upper_limit, lower_limit, n_number, total_tests) formula.gen_xlsx(filename, tests, results, n_number, split_num) msg = '%s generated!\n' % filename self.info_dialog(msg) def check_input(self, filename, upper_limit, lower_limit, operators): err_msg = '' if not filename: err_msg += self.tr('missing file name\n') if upper_limit < lower_limit: err_msg += self.tr( 'wrong setting, min number is larger than max number\n') if len(operators) == 0: err_msg += self.tr('at least one operator must be checked\n') return err_msg def collect_input(self): filename = self.file.text() upper_limit = self.upper_spin.value() lower_limit = self.lower_spin.value() n_number = self.n_spin.value() total_tests = self.total.value() operators = self.collect_operators() return (filename, upper_limit, lower_limit, n_number, total_tests, operators) def info_dialog(self, msg): dial = QMessageBox() dial.setText(msg) dial.exec_() def err_dialog(self, msg): err = QErrorMessage() err.showMessage(msg) err.exec_() def collect_operators(self): operators = [] if self.plus_cb.isChecked(): operators.append('+') if self.minus_cb.isChecked(): operators.append('-') if self.multi_cb.isChecked(): operators.append('*') if self.divide_cb.isChecked(): operators.append('/') return operators
class MainWindow(QMainWindow): def __init__(self): super().__init__() hlayout = QHBoxLayout() layout = QVBoxLayout() self.filtert = QLineEdit() self.filtert.setPlaceholderText("Search...") self.table = QTableView() vlayout = QVBoxLayout() vlayout.addWidget(self.filtert) vlayout.addWidget(self.table) # Left/right pane. hlayout.addLayout(vlayout) hlayout.addLayout(layout) form = QFormLayout() self.track_id = QSpinBox() self.track_id.setDisabled(True) self.track_id.setRange(0, 2147483647) self.name = QLineEdit() self.album = QComboBox() self.media_type = QComboBox() self.genre = QComboBox() self.composer = QLineEdit() self.milliseconds = QSpinBox() self.milliseconds.setRange(0, 2147483647) self.milliseconds.setSingleStep(1) self.bytes = QSpinBox() self.bytes.setRange(0, 2147483647) self.bytes.setSingleStep(1) self.unit_price = QDoubleSpinBox() self.unit_price.setRange(0, 999) self.unit_price.setSingleStep(0.01) self.unit_price.setPrefix("$") form.addRow(QLabel("Track ID"), self.track_id) form.addRow(QLabel("Track name"), self.name) form.addRow(QLabel("Composer"), self.composer) form.addRow(QLabel("Milliseconds"), self.milliseconds) form.addRow(QLabel("Bytes"), self.bytes) form.addRow(QLabel("Unit Price"), self.unit_price) self.model = QSqlTableModel(db=db) self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(self.model) self.proxy_model.sort(1, Qt.AscendingOrder) self.proxy_model.setFilterKeyColumn(-1) # all columns self.table.setModel(self.proxy_model) # Update search when filter changes. self.filtert.textChanged.connect(self.proxy_model.setFilterFixedString) self.mapper = QDataWidgetMapper() self.mapper.setModel(self.proxy_model) self.mapper.addMapping(self.track_id, 0) self.mapper.addMapping(self.name, 1) self.mapper.addMapping(self.composer, 5) self.mapper.addMapping(self.milliseconds, 6) self.mapper.addMapping(self.bytes, 7) self.mapper.addMapping(self.unit_price, 8) self.model.setTable("Track") self.model.select() # Change the mapper selection using the table. self.table.selectionModel().currentRowChanged.connect( self.mapper.setCurrentModelIndex) self.mapper.toFirst() # tag::controls[] self.setMinimumSize(QSize(800, 400)) controls = QHBoxLayout() prev_rec = QPushButton("Previous") prev_rec.clicked.connect(self.mapper.toPrevious) next_rec = QPushButton("Next") next_rec.clicked.connect(self.mapper.toNext) save_rec = QPushButton("Save Changes") save_rec.clicked.connect(self.mapper.submit) controls.addWidget(prev_rec) controls.addWidget(next_rec) controls.addWidget(save_rec) layout.addLayout(form) layout.addLayout(controls) widget = QWidget() widget.setLayout(hlayout) self.setCentralWidget(widget)
class Widget(QWidget): def __init__(self): QWidget.__init__(self) # pciture manager self.manager_img = pictures.Manager() self.layers = [] self.btn_add_layer = QPushButton("Add layer") self.btn_remove_layer = QPushButton("Remove 1 layer") self.btn_clear = QPushButton("Clear All Layers") self.sbox_alpha = QSpinBox() self.sbox_alpha.setRange(0, 100) self.sbox_alpha.setValue(50) self.sbox_rgb = QSpinBox() self.sbox_rgb.setRange(0, 100) self.sbox_rgb.setValue(50) # connect buttons # Signals and Slots self.btn_add_layer.clicked.connect(self.add_layer) self.btn_remove_layer.clicked.connect(self.remove_layer) self.sbox_remove_index = QSpinBox() self.sbox_remove_index.setValue(0) self.sbox_remove_index.setRange(0, 0) self.sbox_remove_index.setSingleStep(1) self.lbl_index_remove = QLabel("index calque") self.btn_clear.clicked.connect(self.clear) self.pic_res = QLabel() # self.pic_1.setGeometry(QRect(10, 40, 500, 500)) # self.pic_1.setGeometry(QRect(10, 540, 500, 500)) # self.pic_1.setPixmap(pixmap) # self.pic_2.setPixmap(pixmap) # self.pic_res.setPixmap(pixmap) self.cbox_actions = QComboBox() self.cbox_definitions = QComboBox() self.setup_cbox() self.form_layout = QFormLayout() self.form_layout.addRow("alpha:", self.sbox_alpha) self.form_layout.addRow("rgb:", self.sbox_rgb) self.h_layout_add_layer = QHBoxLayout() self.h_layout_add_layer.addWidget(self.btn_add_layer) self.h_layout_add_layer.addWidget(self.cbox_actions) self.h_layout_add_layer.addWidget(self.cbox_definitions) self.h_layout_add_layer.addLayout(self.form_layout) self.h_layout_remove = QHBoxLayout() self.h_layout_remove.addWidget(self.btn_remove_layer) self.h_layout_remove.addWidget(self.lbl_index_remove) self.h_layout_remove.addWidget(self.sbox_remove_index) self.h_layout_remove.addWidget(self.btn_clear) self.v_layout = QVBoxLayout() self.v_layout.addWidget(self.pic_res) self.v_layout.addLayout(self.h_layout_add_layer) # self.v_layout.addLayout(self.form_layout) self.v_layout.addLayout(self.h_layout_remove) self.setLayout(self.v_layout) self.connect_signals() def connect_signals(self): self.manager_img.pixmap_updated.connect(self.pic_res.setPixmap) self.cbox_actions.currentTextChanged.connect( self.set_overlay_blendmode) self.cbox_definitions.currentTextChanged.connect(self.set_definition) def set_overlay_blendmode(self, blend_mode): self.manager_img.set_blend_mode(self.sbox_remove_index.value() - 1, self.cbox_actions.currentText()) def set_definition(self, str_def): self.manager_img.set_definition(self.cbox_definitions.currentText()) def remove_layer(self): self.manager_img.remove_layer(self.sbox_remove_index.value() - 1) self.sbox_remove_index.setMaximum(self.sbox_remove_index.maximum() - 1) if not self.manager_img.layers: self.sbox_remove_index.setRange(0, 0) def clear(self): self.layers = [] self.manager_img.clear() def setup_cbox(self): for action in pictures.BLEND_MODES: self.cbox_actions.addItem(action) for key, _ in pictures.DEFINITIONS.items(): self.cbox_definitions.addItem(key) def add_layer(self): path_filename = QFileDialog.getOpenFileName( self, "Choose Image", "/home/", ) #self.manager_img.set_alpha(0, self.sbox_alpha.value()) #self.manager_img.set_definition(self.cbox_definitions.currentText()) self.manager_img.add_layer(path_filename[0], blend_mode=self.cbox_actions.currentText()) self.sbox_remove_index.setRange(1, len(self.manager_img.layers)) self.sbox_remove_index.setValue(len(self.manager_img.layers))
class DomainDock(PlotterDock): """ Domain options dock """ def __init__(self, model, font_metric, parent=None): super().__init__(model, font_metric, parent) self.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea) # Create Controls self._createOriginBox() self._createOptionsBox() self._createResolutionBox() # Create submit button self.applyButton = QPushButton("Apply Changes") # Mac bug fix self.applyButton.setMinimumHeight(self.font_metric.height() * 1.6) self.applyButton.clicked.connect(self.main_window.applyChanges) # Create Zoom box self.zoomBox = QSpinBox() self.zoomBox.setSuffix(' %') self.zoomBox.setRange(25, 2000) self.zoomBox.setValue(100) self.zoomBox.setSingleStep(25) self.zoomBox.valueChanged.connect(self.main_window.editZoom) self.zoomLayout = QHBoxLayout() self.zoomLayout.addWidget(QLabel('Zoom:')) self.zoomLayout.addWidget(self.zoomBox) self.zoomLayout.setContentsMargins(0, 0, 0, 0) self.zoomWidget = QWidget() self.zoomWidget.setLayout(self.zoomLayout) # Create Layout self.dockLayout = QVBoxLayout() self.dockLayout.addWidget(QLabel("Geometry/Properties")) self.dockLayout.addWidget(HorizontalLine()) self.dockLayout.addWidget(self.originGroupBox) self.dockLayout.addWidget(self.optionsGroupBox) self.dockLayout.addWidget(self.resGroupBox) self.dockLayout.addWidget(HorizontalLine()) self.dockLayout.addWidget(self.zoomWidget) self.dockLayout.addWidget(HorizontalLine()) self.dockLayout.addStretch() self.dockLayout.addWidget(self.applyButton) self.dockLayout.addWidget(HorizontalLine()) self.optionsWidget = QWidget() self.optionsWidget.setLayout(self.dockLayout) self.setWidget(self.optionsWidget) def _createOriginBox(self): # X Origin self.xOrBox = QDoubleSpinBox() self.xOrBox.setDecimals(9) self.xOrBox.setRange(-99999, 99999) xbox_connector = partial(self.main_window.editSingleOrigin, dimension=0) self.xOrBox.valueChanged.connect(xbox_connector) # Y Origin self.yOrBox = QDoubleSpinBox() self.yOrBox.setDecimals(9) self.yOrBox.setRange(-99999, 99999) ybox_connector = partial(self.main_window.editSingleOrigin, dimension=1) self.yOrBox.valueChanged.connect(ybox_connector) # Z Origin self.zOrBox = QDoubleSpinBox() self.zOrBox.setDecimals(9) self.zOrBox.setRange(-99999, 99999) zbox_connector = partial(self.main_window.editSingleOrigin, dimension=2) self.zOrBox.valueChanged.connect(zbox_connector) # Origin Form Layout self.orLayout = QFormLayout() self.orLayout.addRow('X:', self.xOrBox) self.orLayout.addRow('Y:', self.yOrBox) self.orLayout.addRow('Z:', self.zOrBox) self.orLayout.setLabelAlignment(QtCore.Qt.AlignLeft) self.orLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) # Origin Group Box self.originGroupBox = QGroupBox('Origin') self.originGroupBox.setLayout(self.orLayout) def _createOptionsBox(self): # Width self.widthBox = QDoubleSpinBox(self) self.widthBox.setRange(.1, 99999) self.widthBox.setDecimals(9) self.widthBox.valueChanged.connect(self.main_window.editWidth) # Height self.heightBox = QDoubleSpinBox(self) self.heightBox.setRange(.1, 99999) self.heightBox.setDecimals(9) self.heightBox.valueChanged.connect(self.main_window.editHeight) # ColorBy 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( self.main_window.editColorBy) # Universe level (applies to cell coloring only) 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( self.main_window.editUniverseLevel) # Alpha self.domainAlphaBox = QDoubleSpinBox(self) self.domainAlphaBox.setValue(self.model.activeView.domainAlpha) self.domainAlphaBox.setSingleStep(0.05) self.domainAlphaBox.setDecimals(2) self.domainAlphaBox.setRange(0.0, 1.0) self.domainAlphaBox.valueChanged.connect(self.main_window.editPlotAlpha) # Visibility self.visibilityBox = QCheckBox(self) self.visibilityBox.stateChanged.connect( self.main_window.editPlotVisibility) # Outlines self.outlinesBox = QCheckBox(self) self.outlinesBox.stateChanged.connect(self.main_window.toggleOutlines) # Basis self.basisBox = QComboBox(self) self.basisBox.addItem("xy") self.basisBox.addItem("xz") self.basisBox.addItem("yz") self.basisBox.currentTextChanged.connect(self.main_window.editBasis) # Advanced Color Options self.colorOptionsButton = QPushButton('Color Options...') self.colorOptionsButton.setMinimumHeight(self.font_metric.height() * 1.6) self.colorOptionsButton.clicked.connect(self.main_window.showColorDialog) # Options Form Layout self.opLayout = QFormLayout() self.opLayout.addRow('Width:', self.widthBox) self.opLayout.addRow('Height:', self.heightBox) self.opLayout.addRow('Basis:', self.basisBox) self.opLayout.addRow('Color By:', self.colorbyBox) self.opLayout.addRow('Universe Level:', self.universeLevelBox) self.opLayout.addRow('Plot alpha:', self.domainAlphaBox) self.opLayout.addRow('Visible:', self.visibilityBox) self.opLayout.addRow('Outlines:', self.outlinesBox) self.opLayout.addRow(self.colorOptionsButton) self.opLayout.setLabelAlignment(QtCore.Qt.AlignLeft) self.opLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) # Options Group Box self.optionsGroupBox = QGroupBox('Options') self.optionsGroupBox.setLayout(self.opLayout) def _createResolutionBox(self): # Horizontal Resolution self.hResBox = QSpinBox(self) self.hResBox.setRange(1, 99999) self.hResBox.setSingleStep(25) self.hResBox.setSuffix(' px') self.hResBox.valueChanged.connect(self.main_window.editHRes) # Vertical Resolution self.vResLabel = QLabel('Pixel Height:') self.vResBox = QSpinBox(self) self.vResBox.setRange(1, 99999) self.vResBox.setSingleStep(25) self.vResBox.setSuffix(' px') self.vResBox.valueChanged.connect(self.main_window.editVRes) # Ratio checkbox self.ratioCheck = QCheckBox("Fixed Aspect Ratio", self) self.ratioCheck.stateChanged.connect(self.main_window.toggleAspectLock) # Resolution Form Layout self.resLayout = QFormLayout() self.resLayout.addRow(self.ratioCheck) self.resLayout.addRow('Pixel Width:', self.hResBox) self.resLayout.addRow(self.vResLabel, self.vResBox) self.resLayout.setLabelAlignment(QtCore.Qt.AlignLeft) self.resLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) # Resolution Group Box self.resGroupBox = QGroupBox("Resolution") self.resGroupBox.setLayout(self.resLayout) def updateDock(self): self.updateOrigin() self.updateWidth() self.updateHeight() self.updateColorBy() self.updateUniverseLevel() self.updatePlotAlpha() self.updatePlotVisibility() self.updateOutlines() self.updateBasis() self.updateAspectLock() self.updateHRes() self.updateVRes() def updateOrigin(self): self.xOrBox.setValue(self.model.activeView.origin[0]) self.yOrBox.setValue(self.model.activeView.origin[1]) self.zOrBox.setValue(self.model.activeView.origin[2]) def updateWidth(self): self.widthBox.setValue(self.model.activeView.width) def updateHeight(self): self.heightBox.setValue(self.model.activeView.height) def updateColorBy(self): self.colorbyBox.setCurrentText(self.model.activeView.colorby) if self.model.activeView.colorby != 'cell': self.universeLevelBox.setEnabled(False) else: self.universeLevelBox.setEnabled(True) def updateUniverseLevel(self): self.universeLevelBox.setCurrentIndex(self.model.activeView.level + 1) def updatePlotAlpha(self): self.domainAlphaBox.setValue(self.model.activeView.domainAlpha) def updatePlotVisibility(self): self.visibilityBox.setChecked(self.model.activeView.domainVisible) def updateOutlines(self): self.outlinesBox.setChecked(self.model.activeView.outlines) def updateBasis(self): self.basisBox.setCurrentText(self.model.activeView.basis) def updateAspectLock(self): aspect_lock = bool(self.model.activeView.aspectLock) self.ratioCheck.setChecked(aspect_lock) self.vResBox.setDisabled(aspect_lock) self.vResLabel.setDisabled(aspect_lock) def updateHRes(self): self.hResBox.setValue(self.model.activeView.h_res) def updateVRes(self): self.vResBox.setValue(self.model.activeView.v_res) def revertToCurrent(self): cv = self.model.currentView self.xOrBox.setValue(cv.origin[0]) self.yOrBox.setValue(cv.origin[1]) self.zOrBox.setValue(cv.origin[2]) self.widthBox.setValue(cv.width) self.heightBox.setValue(cv.height) def resizeEvent(self, event): self.main_window.resizeEvent(event) hideEvent = showEvent = moveEvent = resizeEvent
def __init_features_widget__(self, parent=None): self.__features_widget = QGroupBox("Features Layers Parameters", parent) # self.__features_widget.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) thickness_label = QLabel("Layer Thickness", self.__features_widget) self.features_thickness_edit = MyDiscreteStepsSpinBox( self.dlp_controller.get_step_length_microns(), self.__features_widget) self.features_thickness_edit.setSuffix(str('\u03BCm')) self.features_thickness_edit.setMaximum(1000000) self.features_thickness_edit.setMinimum(0) self.features_thickness_edit.setDecimals(3) self.features_thickness_edit.my_value_changed_signal.connect( self.dlp_controller.set_features_thickness) self.features_thickness_edit.setValue( self.dlp_controller.features_thickness * 1000) exposure_label = QLabel("Exposure Time", self.__features_widget) exposure_edit = QDoubleSpinBox(self.__features_widget) exposure_edit.setSuffix(str('ms')) exposure_edit.setMaximum(10000000) exposure_edit.setMinimum(0) exposure_edit.setDecimals(1) exposure_edit.setSingleStep(0.1) exposure_edit.setValue(self.dlp_controller.features_exposure) exposure_edit.valueChanged.connect( self.dlp_controller.set_features_exposure_time) amplitude_label = QLabel("Light Amplitude", self.__features_widget) amplitude_edit = QSpinBox(self.__features_widget) amplitude_edit.setMaximum(1600) amplitude_edit.setMinimum(0) amplitude_edit.setSingleStep(1) amplitude_edit.setValue(self.dlp_controller.features_amplitude) amplitude_edit.valueChanged.connect( self.dlp_controller.set_features_amplitude) burn_layers_label = QLabel("Burn Layers", self.__features_widget) burn_layers_edit = QSpinBox(self.__features_widget) burn_layers_edit.setMaximum(1000) burn_layers_edit.setMinimum(0) burn_layers_edit.setSingleStep(1) burn_layers_edit.setValue(self.dlp_controller.features_burn_layers) burn_layers_edit.valueChanged.connect( self.dlp_controller.set_features_burning_layers) burn_exposure_label = QLabel("Burn Exposure", self.__features_widget) burn_exposure_edit = QDoubleSpinBox(self.__features_widget) burn_exposure_edit.setSuffix(str('ms')) burn_exposure_edit.setMaximum(100000) burn_exposure_edit.setMinimum(0) burn_exposure_edit.setDecimals(1) burn_exposure_edit.setSingleStep(0.1) burn_exposure_edit.setValue(self.dlp_controller.features_burn_exposure) burn_exposure_edit.valueChanged.connect( self.dlp_controller.set_features_burning_exposure_time) burn_amplitude_label = QLabel("Burn Amplitude", self.__features_widget) burn_amplitude_edit = QSpinBox(self.__features_widget) burn_amplitude_edit.setMaximum(1600) burn_amplitude_edit.setMinimum(0) burn_amplitude_edit.setSingleStep(1) burn_amplitude_edit.setValue( self.dlp_controller.features_burn_amplitude) burn_amplitude_edit.valueChanged.connect( self.dlp_controller.set_features_burning_amplitude) select_layers_button = QPushButton("Select Features Images") select_layers_button.clicked.connect(self.load_features_images) features_layout = QGridLayout(self.__features_widget) features_layout.addWidget(thickness_label, 1, 0) features_layout.addWidget(self.features_thickness_edit, 1, 1) features_layout.addWidget(exposure_label, 2, 0) features_layout.addWidget(exposure_edit, 2, 1) features_layout.addWidget(amplitude_label, 3, 0) features_layout.addWidget(amplitude_edit, 3, 1) features_layout.addWidget(burn_layers_label, 4, 0) features_layout.addWidget(burn_layers_edit, 4, 1) features_layout.addWidget(burn_exposure_label, 5, 0) features_layout.addWidget(burn_exposure_edit, 5, 1) features_layout.addWidget(burn_amplitude_label, 6, 0) features_layout.addWidget(burn_amplitude_edit, 6, 1) features_layout.addWidget(select_layers_button, 7, 0, 1, 2) self.__features_widget.setLayout(features_layout)
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"))
def __init_advanced_features_widget__(self, parent=None): self.__advanced_widget = QGroupBox("Advanced Features Options", parent) fixed_layer_check = QCheckBox("Fixed Layer", self.__advanced_widget) fixed_layer_check.setChecked(self.dlp_controller.fixed_layer) fixed_layer_check.toggled.connect(self.dlp_controller.set_fixed_layer) incremental_amplitude_check = QCheckBox("Incremental Amplitude", self.__advanced_widget) incremental_amplitude_check.setChecked( self.dlp_controller.incremental_amplitude) incremental_amplitude_check.toggled.connect( self.dlp_controller.set_incremental_amplitude) starting_amplitude_label = QLabel("Starting Amplitude", self.__advanced_widget) starting_amplitude_edit = QSpinBox(self.__advanced_widget) starting_amplitude_edit.setMaximum(1000) starting_amplitude_edit.setMinimum(0) starting_amplitude_edit.setSingleStep(1) starting_amplitude_edit.setValue( self.dlp_controller.starting_incremental_amplitude) starting_amplitude_edit.valueChanged.connect( self.dlp_controller.set_starting_incremental_amplitude) amplitude_step_label = QLabel("Step Size", self.__advanced_widget) amplitude_step_edit = QSpinBox(self.__advanced_widget) amplitude_step_edit.setMaximum(1000) amplitude_step_edit.setMinimum(0) amplitude_step_edit.setSingleStep(1) amplitude_step_edit.setValue( self.dlp_controller.incremental_step_amplitude) amplitude_step_edit.valueChanged.connect( self.dlp_controller.set_incremental_step_amplitude) incremental_exposure_check = QCheckBox("Incremental Exposure", self.__advanced_widget) incremental_exposure_check.setChecked( self.dlp_controller.incremental_exposure) incremental_exposure_check.toggled.connect( self.dlp_controller.set_incremental_exposure) starting_exposure_label = QLabel("Starting Exposure", self.__advanced_widget) starting_exposure_edit = QDoubleSpinBox(self.__advanced_widget) starting_exposure_edit.setSuffix(str('ms')) starting_exposure_edit.setMaximum(100000) starting_exposure_edit.setMinimum(0) starting_exposure_edit.setDecimals(1) starting_exposure_edit.setSingleStep(0.1) starting_exposure_edit.valueChanged.connect( self.dlp_controller.set_starting_incremental_exposure) starting_exposure_edit.setValue( self.dlp_controller.starting_incremental_exposure) exposure_step_label = QLabel("Step Size", self.__advanced_widget) exposure_step_edit = QDoubleSpinBox(self.__advanced_widget) exposure_step_edit.setSuffix(str('ms')) exposure_step_edit.setMaximum(100000) exposure_step_edit.setMinimum(0) exposure_step_edit.setDecimals(1) exposure_step_edit.setSingleStep(0.1) exposure_step_edit.valueChanged.connect( self.dlp_controller.set_incremental_step_exposure) exposure_step_edit.setValue( self.dlp_controller.incremental_step_exposure) incremental_thickness_check = QCheckBox("Incremental Thickness", self.__advanced_widget) incremental_thickness_check.setChecked( self.dlp_controller.incremental_thickness) incremental_thickness_check.toggled.connect( self.dlp_controller.set_incremental_thickness) thickness_label = QLabel("Starting Thickness", self.__features_widget) self.starting_thickness_edit = MyDiscreteStepsSpinBox( self.dlp_controller.get_step_length_microns(), self.__features_widget) self.starting_thickness_edit.setSuffix(str('\u03BCm')) self.starting_thickness_edit.setMaximum(1000000) self.starting_thickness_edit.setMinimum(0) self.starting_thickness_edit.setDecimals(3) self.starting_thickness_edit.my_value_changed_signal.connect( self.dlp_controller.set_starting_incremental_thickness) self.starting_thickness_edit.setValue( self.dlp_controller.starting_incremental_thickness * 1000) thickness_step_label = QLabel("Step Size", self.__features_widget) self.thickness_step_edit = MyDiscreteStepsSpinBox( self.dlp_controller.get_step_length_microns(), self.__features_widget) self.thickness_step_edit.setSuffix(str('\u03BCm')) self.thickness_step_edit.setMaximum(1000000) self.thickness_step_edit.setMinimum(0) self.thickness_step_edit.setDecimals(3) self.thickness_step_edit.my_value_changed_signal.connect( self.dlp_controller.set_incremental_step_thickness) self.thickness_step_edit.setValue( self.dlp_controller.incremental_step_thickness * 1000) apply_grayscale_correction_check = QCheckBox("Grayscale Correction", self.__advanced_widget) apply_grayscale_correction_check.setChecked( self.dlp_controller.grayscale_correction) apply_grayscale_correction_check.toggled.connect( self.dlp_controller.set_grayscale_correction) grayscale_parameters_widget = QWidget(self.__features_widget) a_parameter_label = QLabel("\u03B1", grayscale_parameters_widget) alpha_parameter_edit = QDoubleSpinBox(grayscale_parameters_widget) alpha_parameter_edit.setMaximum(1000) alpha_parameter_edit.setMinimum(0) alpha_parameter_edit.setDecimals(3) alpha_parameter_edit.setSingleStep(0.001) alpha_parameter_edit.valueChanged.connect( self.dlp_controller.set_grayscale_alpha) alpha_parameter_edit.setValue(self.dlp_controller.grayscale_alpha) beta_parameter_label = QLabel("\u03B2", grayscale_parameters_widget) beta_parameter_edit = QDoubleSpinBox(grayscale_parameters_widget) beta_parameter_edit.setMaximum(1000) beta_parameter_edit.setMinimum(0) beta_parameter_edit.setDecimals(3) beta_parameter_edit.setSingleStep(0.001) beta_parameter_edit.valueChanged.connect( self.dlp_controller.set_grayscale_beta) beta_parameter_edit.setValue(self.dlp_controller.grayscale_beta) gamma_parameter_label = QLabel("\u03B3", grayscale_parameters_widget) gamma_parameter_edit = QDoubleSpinBox(grayscale_parameters_widget) gamma_parameter_edit.setMaximum(1000) gamma_parameter_edit.setMinimum(0) gamma_parameter_edit.setDecimals(3) gamma_parameter_edit.setSingleStep(0.001) gamma_parameter_edit.valueChanged.connect( self.dlp_controller.set_grayscale_gamma) gamma_parameter_edit.setValue(self.dlp_controller.grayscale_gamma) grayscale_parameters_layout = QHBoxLayout(grayscale_parameters_widget) grayscale_parameters_layout.addWidget(a_parameter_label) grayscale_parameters_layout.addWidget(alpha_parameter_edit) grayscale_parameters_layout.addWidget(beta_parameter_label) grayscale_parameters_layout.addWidget(beta_parameter_edit) grayscale_parameters_layout.addWidget(gamma_parameter_label) grayscale_parameters_layout.addWidget(gamma_parameter_edit) grayscale_parameters_widget.setLayout(grayscale_parameters_layout) advanced_features_layout = QGridLayout(self.__advanced_widget) advanced_features_layout.addWidget(incremental_amplitude_check, 1, 0, 1, 2) advanced_features_layout.addWidget(fixed_layer_check, 1, 3) advanced_features_layout.addWidget(starting_amplitude_label, 2, 0) advanced_features_layout.addWidget(starting_amplitude_edit, 2, 1) advanced_features_layout.addWidget(amplitude_step_label, 2, 2) advanced_features_layout.addWidget(amplitude_step_edit, 2, 3) advanced_features_layout.addWidget(incremental_exposure_check, 3, 0, 1, 2) advanced_features_layout.addWidget(starting_exposure_label, 4, 0) advanced_features_layout.addWidget(starting_exposure_edit, 4, 1) advanced_features_layout.addWidget(exposure_step_label, 4, 2) advanced_features_layout.addWidget(exposure_step_edit, 4, 3) advanced_features_layout.addWidget(incremental_thickness_check, 5, 0, 1, 2) advanced_features_layout.addWidget(thickness_label, 6, 0) advanced_features_layout.addWidget(self.starting_thickness_edit, 6, 1) advanced_features_layout.addWidget(thickness_step_label, 6, 2) advanced_features_layout.addWidget(self.thickness_step_edit, 6, 3) advanced_features_layout.addWidget(apply_grayscale_correction_check, 7, 0, 1, 2) advanced_features_layout.addWidget(grayscale_parameters_widget, 8, 0, 1, 4) self.__advanced_widget.setLayout(advanced_features_layout)
class OptionsDock(QDockWidget): def __init__(self, model, FM, parent=None): super(OptionsDock, self).__init__(parent) self.model = model self.FM = FM self.mw = parent self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) # Doesn't work? self.setAllowedAreas(QtCore.Qt.LeftDockWidgetArea | QtCore.Qt.RightDockWidgetArea) # Create Controls self.createOriginBox() self.createOptionsBox() self.createResolutionBox() # Create submit button self.applyButton = QPushButton("Apply Changes") self.applyButton.setMinimumHeight(self.FM.height() * 1.6) # Mac bug fix self.applyButton.clicked.connect(self.mw.applyChanges) # Create Zoom box self.zoomBox = QSpinBox() self.zoomBox.setSuffix(' %') self.zoomBox.setRange(25, 2000) self.zoomBox.setValue(100) self.zoomBox.setSingleStep(25) self.zoomBox.valueChanged.connect(self.mw.editZoom) self.zoomLayout = QHBoxLayout() self.zoomLayout.addWidget(QLabel('Zoom:')) self.zoomLayout.addWidget(self.zoomBox) self.zoomLayout.setContentsMargins(0, 0, 0, 0) self.zoomWidget = QWidget() self.zoomWidget.setLayout(self.zoomLayout) # Create Layout self.dockLayout = QVBoxLayout() self.dockLayout.addWidget(self.originGroupBox) self.dockLayout.addWidget(self.optionsGroupBox) self.dockLayout.addWidget(self.resGroupBox) self.dockLayout.addWidget(self.applyButton) self.dockLayout.addStretch() self.dockLayout.addWidget(HorizontalLine()) self.dockLayout.addWidget(self.zoomWidget) self.optionsWidget = QWidget() self.optionsWidget.setLayout(self.dockLayout) self.setWidget(self.optionsWidget) def createOriginBox(self): # X Origin self.xOrBox = QDoubleSpinBox() self.xOrBox.setDecimals(9) self.xOrBox.setRange(-99999, 99999) self.xOrBox.valueChanged.connect( lambda value: self.mw.editSingleOrigin(value, 0)) # Y Origin self.yOrBox = QDoubleSpinBox() self.yOrBox.setDecimals(9) self.yOrBox.setRange(-99999, 99999) self.yOrBox.valueChanged.connect( lambda value: self.mw.editSingleOrigin(value, 1)) # Z Origin self.zOrBox = QDoubleSpinBox() self.zOrBox.setDecimals(9) self.zOrBox.setRange(-99999, 99999) self.zOrBox.valueChanged.connect( lambda value: self.mw.editSingleOrigin(value, 2)) # Origin Form Layout self.orLayout = QFormLayout() self.orLayout.addRow('X:', self.xOrBox) self.orLayout.addRow('Y:', self.yOrBox) self.orLayout.addRow('Z:', self.zOrBox) #self.orLayout.setVerticalSpacing(4) self.orLayout.setLabelAlignment(QtCore.Qt.AlignLeft) self.orLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) # Origin Group Box self.originGroupBox = QGroupBox('Origin') self.originGroupBox.setLayout(self.orLayout) def createOptionsBox(self): # Width self.widthBox = QDoubleSpinBox(self) self.widthBox.setRange(.1, 99999) self.widthBox.valueChanged.connect(self.mw.editWidth) # Height self.heightBox = QDoubleSpinBox(self) self.heightBox.setRange(.1, 99999) self.heightBox.valueChanged.connect(self.mw.editHeight) # ColorBy self.colorbyBox = QComboBox(self) self.colorbyBox.addItem("material") self.colorbyBox.addItem("cell") self.colorbyBox.currentTextChanged[str].connect(self.mw.editColorBy) # Alpha self.plotAlphaBox = QDoubleSpinBox(self) self.plotAlphaBox.setValue(self.model.activeView.plotAlpha) self.plotAlphaBox.setSingleStep(0.05) self.plotAlphaBox.setDecimals(2) self.plotAlphaBox.setRange(0.0, 1.0) self.plotAlphaBox.valueChanged.connect(self.mw.editPlotAlpha) # Basis self.basisBox = QComboBox(self) self.basisBox.addItem("xy") self.basisBox.addItem("xz") self.basisBox.addItem("yz") self.basisBox.currentTextChanged.connect(self.mw.editBasis) # Advanced Color Options self.colorOptionsButton = QPushButton('Color Options...') self.colorOptionsButton.setMinimumHeight(self.FM.height() * 1.6) self.colorOptionsButton.clicked.connect(self.mw.showColorDialog) # Options Form Layout self.opLayout = QFormLayout() self.opLayout.addRow('Width:', self.widthBox) self.opLayout.addRow('Height:', self.heightBox) self.opLayout.addRow('Basis:', self.basisBox) self.opLayout.addRow('Color By:', self.colorbyBox) self.opLayout.addRow('Plot alpha:', self.plotAlphaBox) self.opLayout.addRow(self.colorOptionsButton) self.opLayout.setLabelAlignment(QtCore.Qt.AlignLeft) self.opLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) # Options Group Box self.optionsGroupBox = QGroupBox('Options') self.optionsGroupBox.setLayout(self.opLayout) def createResolutionBox(self): # Horizontal Resolution self.hResBox = QSpinBox(self) self.hResBox.setRange(1, 99999) self.hResBox.setSingleStep(25) self.hResBox.setSuffix(' px') self.hResBox.valueChanged.connect(self.mw.editHRes) # Vertical Resolution self.vResLabel = QLabel('Pixel Height:') self.vResBox = QSpinBox(self) self.vResBox.setRange(1, 99999) self.vResBox.setSingleStep(25) self.vResBox.setSuffix(' px') self.vResBox.valueChanged.connect(self.mw.editVRes) # Ratio checkbox self.ratioCheck = QCheckBox("Fixed Aspect Ratio", self) self.ratioCheck.stateChanged.connect(self.mw.toggleAspectLock) # Resolution Form Layout self.resLayout = QFormLayout() self.resLayout.addRow(self.ratioCheck) self.resLayout.addRow('Pixel Width:', self.hResBox) self.resLayout.addRow(self.vResLabel, self.vResBox) self.resLayout.setLabelAlignment(QtCore.Qt.AlignLeft) self.resLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) # Resolution Group Box self.resGroupBox = QGroupBox("Resolution") self.resGroupBox.setLayout(self.resLayout) def updateDock(self): self.updateOrigin() self.updateWidth() self.updateHeight() self.updateColorBy() self.updatePlotAlpha() self.updateBasis() self.updateAspectLock() self.updateHRes() self.updateVRes() def updateOrigin(self): self.xOrBox.setValue(self.model.activeView.origin[0]) self.yOrBox.setValue(self.model.activeView.origin[1]) self.zOrBox.setValue(self.model.activeView.origin[2]) def updateWidth(self): self.widthBox.setValue(self.model.activeView.width) def updateHeight(self): self.heightBox.setValue(self.model.activeView.height) def updateColorBy(self): self.colorbyBox.setCurrentText(self.model.activeView.colorby) def updatePlotAlpha(self): self.plotAlphaBox.setValue(self.model.activeView.plotAlpha) def updateBasis(self): self.basisBox.setCurrentText(self.model.activeView.basis) def updateAspectLock(self): if self.model.activeView.aspectLock: self.ratioCheck.setChecked(True) self.vResBox.setDisabled(True) self.vResLabel.setDisabled(True) else: self.ratioCheck.setChecked(False) self.vResBox.setDisabled(False) self.vResLabel.setDisabled(False) def updateHRes(self): self.hResBox.setValue(self.model.activeView.h_res) def updateVRes(self): self.vResBox.setValue(self.model.activeView.v_res) def revertToCurrent(self): cv = self.model.currentView self.xOrBox.setValue(cv.origin[0]) self.yOrBox.setValue(cv.origin[1]) self.zOrBox.setValue(cv.origin[2]) self.widthBox.setValue(cv.width) self.heightBox.setValue(cv.height) def resizeEvent(self, event): self.mw.resizeEvent(event) def hideEvent(self, event): self.mw.resizeEvent(event) def showEvent(self, event): self.mw.resizeEvent(event) def moveEvent(self, event): self.mw.resizeEvent(event)
class TabDisplays(QTabWidget): def __init__(self, parent=None): super(TabDisplays, self).__init__(parent) # Initialize logging logging_conf_file = os.path.join(os.path.dirname(__file__), 'cfg/aecgviewer_aecg_logging.conf') logging.config.fileConfig(logging_conf_file) self.logger = logging.getLogger(__name__) self.studyindex_info = aecg.tools.indexer.StudyInfo() self.validator = QWidget() self.studyinfo = QWidget() self.statistics = QWidget() self.waveforms = QWidget() self.waveforms.setAccessibleName("Waveforms") self.scatterplot = QWidget() self.histogram = QWidget() self.trends = QWidget() self.xmlviewer = QWidget() self.xml_display = QTextEdit(self.xmlviewer) self.options = QWidget() self.aecg_display_area = QScrollArea() self.cbECGLayout = QComboBox() self.aecg_display = EcgDisplayWidget(self.aecg_display_area) self.aecg_display_area.setWidget(self.aecg_display) self.addTab(self.validator, "Study information") self.addTab(self.waveforms, "Waveforms") self.addTab(self.xmlviewer, "XML") self.addTab(self.options, "Options") self.setup_validator() self.setup_waveforms() self.setup_xmlviewer() self.setup_options() size = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) size.setHeightForWidth(False) self.setSizePolicy(size) # Initialized a threpool with 2 threads 1 for the GUI, 1 for long # tasks, so GUI remains responsive self.threadpool = QThreadPool() self.threadpool.setMaxThreadCount(2) self.validator_worker = None self.indexing_timer = QElapsedTimer() def setup_validator(self): self.directory_indexer = None # aecg.indexing.DirectoryIndexer() self.validator_layout_container = QWidget() self.validator_layout = QFormLayout() self.validator_form_layout = QFormLayout( self.validator_layout_container) self.validator_grid_layout = QGridLayout() self.study_info_file = QLineEdit() self.study_info_file.setToolTip("Study index file") self.study_info_description = QLineEdit() self.study_info_description.setToolTip("Description") self.app_type = QLineEdit() self.app_type.setToolTip("Application type (e.g., NDA, IND, BLA, IDE)") self.app_num = QLineEdit() self.app_num.setToolTip("Six-digit application number") self.app_num.setValidator(QIntValidator(self.app_num)) self.study_id = QLineEdit() self.study_id.setToolTip("Study identifier") self.study_sponsor = QLineEdit() self.study_sponsor.setToolTip("Sponsor of the study") self.study_annotation_aecg_cb = QComboBox() self.study_annotation_aecg_cb.addItems( ["Rhythm", "Derived beat", "Holter-rhythm", "Holter-derived"]) self.study_annotation_aecg_cb.setToolTip( "Waveforms used to perform the ECG measurements (i.e., " "annotations)\n" "\tRhythm: annotations in a rhythm strip or discrete ECG " "extraction (e.g., 10-s strips)\n" "\tDerived beat: annotations in a representative beat derived " "from a rhythm strip\n" "\tHolter-rhythm: annotations in a the analysis window of a " "continuous recording\n" "\tHolter-derived: annotations in a representative beat derived " "from analysis window of a continuous recording\n") self.study_annotation_lead_cb = QComboBox() self.ui_leads = ["GLOBAL"] + aecg.STD_LEADS[0:12] +\ [aecg.KNOWN_NON_STD_LEADS[1]] + aecg.STD_LEADS[12:15] + ["Other"] self.study_annotation_lead_cb.addItems(self.ui_leads) self.study_annotation_lead_cb.setToolTip( "Primary analysis lead annotated per protocol. There could be " "annotations in other leads also, but only the primary lead should" " be selected here.\n" "Select global if all leads were used at the " "same time (e.g., superimposed on screen).\n" "Select other if the primary lead used is not in the list.") self.study_numsubjects = QLineEdit() self.study_numsubjects.setToolTip( "Number of subjects with ECGs in the study") self.study_numsubjects.setValidator( QIntValidator(self.study_numsubjects)) self.study_aecgpersubject = QLineEdit() self.study_aecgpersubject.setToolTip( "Number of scheduled ECGs (or analysis windows) per subject as " "specified in the study protocol.\n" "Enter average number of ECGs " "per subject if the protocol does not specify a fixed number of " "ECGs per subject.") self.study_aecgpersubject.setValidator( QIntValidator(self.study_aecgpersubject)) self.study_numaecg = QLineEdit() self.study_numaecg.setToolTip( "Total number of aECG XML files in the study") self.study_numaecg.setValidator(QIntValidator(self.study_numaecg)) self.study_annotation_numbeats = QLineEdit() self.study_annotation_numbeats.setToolTip( "Minimum number of beats annotated in each ECG or analysis window" ".\nEnter 1 if annotations were done in the derived beat.") self.study_annotation_numbeats.setValidator( QIntValidator(self.study_annotation_numbeats)) self.aecg_numsubjects = QLineEdit() self.aecg_numsubjects.setToolTip( "Number of subjects found across the provided aECG XML files") self.aecg_numsubjects.setReadOnly(True) self.aecg_aecgpersubject = QLineEdit() self.aecg_aecgpersubject.setToolTip( "Average number of ECGs per subject found across the provided " "aECG XML files") self.aecg_aecgpersubject.setReadOnly(True) self.aecg_numaecg = QLineEdit() self.aecg_numaecg.setToolTip( "Number of aECG XML files found in the study aECG directory") self.aecg_numaecg.setReadOnly(True) self.subjects_less_aecgs = QLineEdit() self.subjects_less_aecgs.setToolTip( "Percentage of subjects with less aECGs than specified per " "protocol") self.subjects_less_aecgs.setReadOnly(True) self.subjects_more_aecgs = QLineEdit() self.subjects_more_aecgs.setToolTip( "Percentage of subjects with more aECGs than specified per " "protocol") self.subjects_more_aecgs.setReadOnly(True) self.aecgs_no_annotations = QLineEdit() self.aecgs_no_annotations.setToolTip( "Percentage of aECGs with no annotations") self.aecgs_no_annotations.setReadOnly(True) self.aecgs_less_qt_in_primary_lead = QLineEdit() self.aecgs_less_qt_in_primary_lead.setToolTip( "Percentage of aECGs with less QT intervals in the primary lead " "than specified per protocol") self.aecgs_less_qt_in_primary_lead.setReadOnly(True) self.aecgs_less_qts = QLineEdit() self.aecgs_less_qts.setToolTip( "Percentage of aECGs with less QT intervals than specified per " "protocol") self.aecgs_less_qts.setReadOnly(True) self.aecgs_annotations_multiple_leads = QLineEdit() self.aecgs_annotations_multiple_leads.setToolTip( "Percentage of aECGs with QT annotations in multiple leads") self.aecgs_annotations_multiple_leads.setReadOnly(True) self.aecgs_annotations_no_primary_lead = QLineEdit() self.aecgs_annotations_no_primary_lead.setToolTip( "Percentage of aECGs with QT annotations not in the primary lead") self.aecgs_annotations_no_primary_lead.setReadOnly(True) self.aecgs_with_errors = QLineEdit() self.aecgs_with_errors.setToolTip("Number of aECG files with errors") self.aecgs_with_errors.setReadOnly(True) self.aecgs_potentially_digitized = QLineEdit() self.aecgs_potentially_digitized.setToolTip( "Number of aECG files potentially digitized (i.e., with more than " "5% of samples missing)") self.aecgs_potentially_digitized.setReadOnly(True) self.study_dir = QLineEdit() self.study_dir.setToolTip("Directory containing the aECG files") self.study_dir_button = QPushButton("...") self.study_dir_button.clicked.connect(self.select_study_dir) self.study_dir_button.setToolTip("Open select directory dialog") self.validator_form_layout.addRow("Application Type", self.app_type) self.validator_form_layout.addRow("Application Number", self.app_num) self.validator_form_layout.addRow("Study name/ID", self.study_id) self.validator_form_layout.addRow("Sponsor", self.study_sponsor) self.validator_form_layout.addRow("Study description", self.study_info_description) self.validator_form_layout.addRow("Annotations in", self.study_annotation_aecg_cb) self.validator_form_layout.addRow("Annotations primary lead", self.study_annotation_lead_cb) self.validator_grid_layout.addWidget(QLabel(""), 0, 0) self.validator_grid_layout.addWidget( QLabel("Per study protocol or report"), 0, 1) self.validator_grid_layout.addWidget(QLabel("Found in aECG files"), 0, 2) self.validator_grid_layout.addWidget(QLabel("Number of subjects"), 1, 0) self.validator_grid_layout.addWidget(self.study_numsubjects, 1, 1) self.validator_grid_layout.addWidget(self.aecg_numsubjects, 1, 2) self.validator_grid_layout.addWidget( QLabel("Number of aECG per subject"), 2, 0) self.validator_grid_layout.addWidget(self.study_aecgpersubject, 2, 1) self.validator_grid_layout.addWidget(self.aecg_aecgpersubject, 2, 2) self.validator_grid_layout.addWidget(QLabel("Total number of aECG"), 3, 0) self.validator_grid_layout.addWidget(self.study_numaecg, 3, 1) self.validator_grid_layout.addWidget(self.aecg_numaecg, 3, 2) self.validator_grid_layout.addWidget( QLabel("Number of beats per aECG"), 4, 0) self.validator_grid_layout.addWidget(self.study_annotation_numbeats, 4, 1) self.validator_grid_layout.addWidget( QLabel("Subjects with fewer ECGs"), 5, 1) self.validator_grid_layout.addWidget(self.subjects_less_aecgs, 5, 2) self.validator_grid_layout.addWidget(QLabel("Subjects with more ECGs"), 6, 1) self.validator_grid_layout.addWidget(self.subjects_more_aecgs, 6, 2) self.validator_grid_layout.addWidget( QLabel("aECGs without annotations"), 7, 1) self.validator_grid_layout.addWidget(self.aecgs_no_annotations, 7, 2) self.validator_grid_layout.addWidget( QLabel("aECGs without expected number of QTs in primary lead"), 8, 1) self.validator_grid_layout.addWidget( self.aecgs_less_qt_in_primary_lead, 8, 2) self.validator_grid_layout.addWidget( QLabel("aECGs without expected number of QTs"), 9, 1) self.validator_grid_layout.addWidget(self.aecgs_less_qts, 9, 2) self.validator_grid_layout.addWidget( QLabel("aECGs annotated in multiple leads"), 10, 1) self.validator_grid_layout.addWidget( self.aecgs_annotations_multiple_leads, 10, 2) self.validator_grid_layout.addWidget( QLabel("aECGs with annotations not in primary lead"), 11, 1) self.validator_grid_layout.addWidget( self.aecgs_annotations_no_primary_lead, 11, 2) self.validator_grid_layout.addWidget(QLabel("aECGs with errors"), 12, 1) self.validator_grid_layout.addWidget(self.aecgs_with_errors, 12, 2) self.validator_grid_layout.addWidget( QLabel("Potentially digitized aECGs"), 13, 1) self.validator_grid_layout.addWidget(self.aecgs_potentially_digitized, 13, 2) self.validator_form_layout.addRow(self.validator_grid_layout) tmp = QHBoxLayout() tmp.addWidget(self.study_dir) tmp.addWidget(self.study_dir_button) self.validator_form_layout.addRow("Study aECGs directory", tmp) self.validator_form_layout.addRow("Study index file", self.study_info_file) self.validator_layout.addWidget(self.validator_layout_container) self.validator_effective_dirs = QLabel("") self.validator_effective_dirs.setWordWrap(True) self.validator_layout.addWidget(self.validator_effective_dirs) self.val_button = QPushButton("Generate/update study index") self.val_button.clicked.connect(self.importstudy_dialog) self.validator_layout.addWidget(self.val_button) self.cancel_val_button = QPushButton("Cancel study index generation") self.cancel_val_button.clicked.connect(self.cancel_validator) self.cancel_val_button.setEnabled(False) self.validator_layout.addWidget(self.cancel_val_button) self.validator_pl = QLabel("") self.validator_layout.addWidget(self.validator_pl) self.validator_pb = QProgressBar() self.validator_layout.addWidget(self.validator_pb) self.validator.setLayout(self.validator_layout) self.stop_indexing = False self.lastindexing_starttime = None self.update_validator_effective_dirs() def effective_aecgs_dir(self, navwidget, silent=False): aecgs_effective_dir = self.study_dir.text() if navwidget.project_loaded != '': # Path specified in the GUI potential_aecgs_dirs = [self.study_dir.text()] # StudyDir path from current working directory potential_aecgs_dirs += [self.studyindex_info.StudyDir] # StudyDir path from directory where the index is located potential_aecgs_dirs += [ os.path.join(os.path.dirname(navwidget.project_loaded), self.studyindex_info.StudyDir) ] # StudyDir replaced with the directory where the index is located potential_aecgs_dirs += [os.path.dirname(navwidget.project_loaded)] dir_found = False # Get xml and zip filenames from first element in the index aecg_xml_file = navwidget.data_index["AECGXML"][0] zipfile = "" if aecg_xml_file != "": zipfile = navwidget.data_index["ZIPFILE"][0] for p in potential_aecgs_dirs: testfn = os.path.join(p, aecg_xml_file) if zipfile != "": testfn = os.path.join(p, zipfile) if os.path.isfile(testfn): dir_found = True aecgs_effective_dir = p break if not silent: if not dir_found: QMessageBox.warning( self, f"Study aECGs directory not found", f"The following paths were checked:" f"{[','.join(p) for p in potential_aecgs_dirs]} and " f"none of them is valid") elif p != self.study_dir.text(): QMessageBox.warning( self, f"Study aECGs directory not found", f"The path specified in the study aECGs directory is " f"not valid and {p} is being used instead. Check and " f"update the path in Study aECGs directory textbox if " f"the suggested path is not the adequate path") return aecgs_effective_dir def update_validator_effective_dirs(self): msg = f"Working directory: {os.getcwd()}" if self.parent() is not None: if isinstance(self.parent(), QSplitter): navwidget = self.parent().parent() else: # Tabs widget has not been allocated the QSplitter yet navwidget = self.parent() project_loaded = navwidget.project_loaded if project_loaded != '': msg = f"{msg}\nLoaded project index: "\ f"{navwidget.project_loaded}" effective_aecgs_path = self.effective_aecgs_dir(navwidget) msg = f"{msg}\nEffective study aECGs directory: "\ f"{effective_aecgs_path}" else: msg = f"{msg}\nLoaded project index: None" else: msg = f"{msg}\nLoaded project index: None" self.validator_effective_dirs.setText(msg) def load_study_info(self, fileName): self.study_info_file.setText(fileName) try: study_info = pd.read_excel(fileName, sheet_name="Info") self.studyindex_info = aecg.tools.indexer.StudyInfo() self.studyindex_info.__dict__.update( study_info.set_index("Property").transpose().reset_index( drop=True).to_dict('index')[0]) sponsor = "" description = "" if self.studyindex_info.Sponsor is not None and\ isinstance(self.studyindex_info.Sponsor, str): sponsor = self.studyindex_info.Sponsor if self.studyindex_info.Description is not None and\ isinstance(self.studyindex_info.Description, str): description = self.studyindex_info.Description self.study_sponsor.setText(sponsor) self.study_info_description.setText(description) self.app_type.setText(self.studyindex_info.AppType) self.app_num.setText(f"{int(self.studyindex_info.AppNum):06d}") self.study_id.setText(self.studyindex_info.StudyID) self.study_numsubjects.setText(str(self.studyindex_info.NumSubj)) self.study_aecgpersubject.setText( str(self.studyindex_info.NECGSubj)) self.study_numaecg.setText(str(self.studyindex_info.TotalECGs)) anns_in = self.studyindex_info.AnMethod.upper() idx = 0 if anns_in == "RHYTHM": idx = 0 elif anns_in == "DERIVED": idx = 1 elif anns_in == "HOLTER_RHYTHM": idx = 2 elif anns_in == "HOLTER_MEDIAN_BEAT": idx = 3 else: idx = int(anns_in) - 1 self.study_annotation_aecg_cb.setCurrentIndex(idx) the_lead = self.studyindex_info.AnLead idx = self.study_annotation_lead_cb.findText(str(the_lead)) if idx == -1: idx = self.study_annotation_lead_cb.findText("MDC_ECG_LEAD_" + str(the_lead)) if idx == -1: idx = int(the_lead) self.study_annotation_lead_cb.setCurrentIndex(idx) self.study_annotation_numbeats.setText( str(self.studyindex_info.AnNbeats)) if self.studyindex_info.StudyDir == "": self.studyindex_info.StudyDir = os.path.dirname(fileName) self.study_dir.setText(self.studyindex_info.StudyDir) self.update_validator_effective_dirs() self.setCurrentWidget(self.validator) except Exception as ex: QMessageBox.critical( self, "Import study error", "Error reading the study information file: '" + fileName + "'") def setup_waveforms(self): wflayout = QVBoxLayout() # ECG plot layout selection box self.cbECGLayout.addItems( ['12-lead stacked', '3x4 + lead II rhythm', 'Superimposed']) self.cbECGLayout.currentIndexChanged.connect( self.ecgplotlayout_changed) # Zoom buttons blayout = QHBoxLayout() pb_zoomin = QPushButton() pb_zoomin.setText("Zoom +") pb_zoomin.clicked.connect(self.zoom_in) pb_zoomreset = QPushButton() pb_zoomreset.setText("Zoom 1:1") pb_zoomreset.clicked.connect(self.zoom_reset) pb_zoomout = QPushButton() pb_zoomout.setText("Zoom -") pb_zoomout.clicked.connect(self.zoom_out) blayout.addWidget(self.cbECGLayout) blayout.addWidget(pb_zoomout) blayout.addWidget(pb_zoomreset) blayout.addWidget(pb_zoomin) wflayout.addLayout(blayout) # Add QScrollArea to main layout of waveforms tab self.aecg_display_area.setWidgetResizable(False) wflayout.addWidget(self.aecg_display_area) self.waveforms.setLayout(wflayout) size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) size.setHeightForWidth(False) self.aecg_display_area.setSizePolicy(size) self.waveforms.setSizePolicy(size) def setup_xmlviewer(self): wf_layout = QHBoxLayout() wf_layout.addWidget(self.xml_display) self.xmlviewer.setLayout(wf_layout) def setup_options(self): self.options_layout = QFormLayout() self.aecg_schema_filename = QLineEdit(aecg.get_aecg_schema_location()) self.options_layout.addRow("aECG XML schema path", self.aecg_schema_filename) self.save_index_every_n_aecgs = QSpinBox() self.save_index_every_n_aecgs.setMinimum(0) self.save_index_every_n_aecgs.setMaximum(50000) self.save_index_every_n_aecgs.setValue(0) self.save_index_every_n_aecgs.setSingleStep(100) self.save_index_every_n_aecgs.setSuffix(" aECGs") self.save_index_every_n_aecgs.setToolTip( "Set o 0 to save the study index file only after its generation " "is completed.\nOtherwise, the file is saved everytime the " " specified number of ECGs have been appended to the index.") self.options_layout.addRow("Save index every ", self.save_index_every_n_aecgs) self.save_all_intervals_cb = QCheckBox("") self.save_all_intervals_cb.setChecked(False) self.options_layout.addRow("Save individual beat intervals", self.save_all_intervals_cb) self.parallel_processing_cb = QCheckBox("") self.parallel_processing_cb.setChecked(True) self.options_layout.addRow("Parallel processing of files", self.parallel_processing_cb) self.options.setLayout(self.options_layout) def zoom_in(self): self.aecg_display.apply_zoom(self.aecg_display.zoom_factor + 0.1) def zoom_out(self): self.aecg_display.apply_zoom(self.aecg_display.zoom_factor - 0.1) def zoom_reset(self): self.aecg_display.apply_zoom(1.0) def ecgplotlayout_changed(self, i): self.aecg_display.update_aecg_plot( ecg_layout=aecg.utils.ECG_plot_layout(i + 1)) def update_search_progress(self, i, n): self.validator_pl.setText( f"Searching aECGs in directory ({n} XML files found)") def update_progress(self, i, n): j = i m = n if i <= 1: j = 1 if self.validator_pb.value() > 0: j = self.validator_pb.value() + 1 m = self.validator_pb.maximum() running_time = self.indexing_timer.elapsed() * 1e-3 # in seconds time_per_item = running_time / j # reamining = seconds per item so far * total pending items to process remaining_time = time_per_item * (m - j) eta = datetime.datetime.now() +\ datetime.timedelta(seconds=round(remaining_time, 0)) self.validator_pl.setText( f"Validating aECG {j}/{m} | " f"Execution time: " f"{str(datetime.timedelta(0,seconds=round(running_time)))} | " f"{round(1/time_per_item,2)} aECGs per second | " f"ETA: {eta.isoformat(timespec='seconds')}") self.validator_pb.setValue(j) if self.save_index_every_n_aecgs.value() > 0 and\ len(self.directory_indexer.studyindex) % \ self.save_index_every_n_aecgs.value() == 0: self.save_validator_results( pd.concat(self.directory_indexer.studyindex, ignore_index=True)) def save_validator_results(self, res): if res.shape[0] > 0: self.studyindex_info = aecg.tools.indexer.StudyInfo() self.studyindex_info.StudyDir = self.study_dir.text() self.studyindex_info.IndexFile = self.study_info_file.text() self.studyindex_info.Sponsor = self.study_sponsor.text() self.studyindex_info.Description =\ self.study_info_description.text() self.studyindex_info.Date = self.lastindexing_starttime.isoformat() self.studyindex_info.End_date = datetime.datetime.now().isoformat() self.studyindex_info.Version = aecg.__version__ self.studyindex_info.AppType = self.app_type.text() self.studyindex_info.AppNum = f"{int(self.app_num.text()):06d}" self.studyindex_info.StudyID = self.study_id.text() self.studyindex_info.NumSubj = int(self.study_numsubjects.text()) self.studyindex_info.NECGSubj = int( self.study_aecgpersubject.text()) self.studyindex_info.TotalECGs = int(self.study_numaecg.text()) anmethod = aecg.tools.indexer.AnnotationMethod( self.study_annotation_aecg_cb.currentIndex()) self.studyindex_info.AnMethod = anmethod.name self.studyindex_info.AnLead =\ self.study_annotation_lead_cb.currentText() self.studyindex_info.AnNbeats = int( self.study_annotation_numbeats.text()) # Calculate stats study_stats = aecg.tools.indexer.StudyStats( self.studyindex_info, res) # Save to file aecg.tools.indexer.save_study_index(self.studyindex_info, res, study_stats) validator_data_ready = Signal() def save_validator_results_and_load_index(self, res): self.save_validator_results(res) self.validator_data_ready.emit() def indexer_validator_results(self, res): self.studyindex_df = pd.concat([self.studyindex_df, res], ignore_index=True) def subindex_thread_complete(self): return def index_directory_thread_complete(self): tmp = self.validator_pl.text().replace("ETA:", "Completed: ").replace( "Validating", "Validated") self.validator_pl.setText(tmp) self.val_button.setEnabled(True) self.cancel_val_button.setEnabled(False) self.validator_layout_container.setEnabled(True) def index_directory(self, progress_callback): self.lastindexing_starttime = datetime.datetime.now() self.indexing_timer.start() studyindex_df = [] n_cores = os.cpu_count() aecg_schema = None if self.aecg_schema_filename.text() != "": aecg_schema = self.aecg_schema_filename.text() if self.parallel_processing_cb.isChecked(): studyindex_df = self.directory_indexer.index_directory( self.save_all_intervals_cb.isChecked(), aecg_schema, n_cores, progress_callback) else: studyindex_df = self.directory_indexer.index_directory( self.save_all_intervals_cb.isChecked(), aecg_schema, 1, progress_callback) return studyindex_df def importstudy_dialog(self): dirName = os.path.normpath(self.study_dir.text()) if dirName != "": if os.path.exists(dirName): self.directory_indexer = aecg.indexing.DirectoryIndexer() self.directory_indexer.set_aecg_dir( dirName, self.update_search_progress) self.validator_pb.setMaximum(self.directory_indexer.num_files) self.validator_pb.reset() self.stop_indexing = False self.validator_layout_container.setEnabled(False) self.val_button.setEnabled(False) self.cancel_val_button.setEnabled(True) self.validator_worker = Worker(self.index_directory) self.validator_worker.signals.result.connect( self.save_validator_results_and_load_index) self.validator_worker.signals.finished.connect( self.index_directory_thread_complete) self.validator_worker.signals.progress.connect( self.update_progress) # Execute self.threadpool.start(self.validator_worker) else: QMessageBox.critical( self, "Directory not found", f"Specified study directory not found:\n{dirName}") else: QMessageBox.critical(self, "Import study error", "Study directory cannot be empty") def cancel_validator(self): self.cancel_val_button.setEnabled(False) self.stop_indexing = True self.directory_indexer.cancel_indexing = True self.threadpool.waitForDone(3000) self.val_button.setEnabled(True) def select_study_dir(self): cd = self.study_dir.text() if cd == "": cd = "." dir = QFileDialog.getExistingDirectory( self, "Open Directory", cd, QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) if dir != "": self.study_dir.setText(dir)