class CSVExport(QGroupBox): def __init__(self, parent=None): self._parent = parent super().__init__(parent=parent) self.setTitle("CSV Export") layout = QHBoxLayout() self.export = QPushButton("Export Grid Information") self.export.clicked.connect(self.save_CSV) self.detailed = QCheckBox("Detailed") layout.addWidget(self.export) layout.addWidget(self.detailed) self.setLayout(layout) self.setEnabled(False) def setEnabled(self, enabled): self.export.setEnabled(enabled) self.detailed.setEnabled(enabled) def save_CSV(self): self._parent.calculate_stress() filename, _ = QFileDialog.getSaveFileName( self, "Export Grid Information", self._parent.model.get_default_csv_filename(), "CSV (*.csv);;All Files (*)") if not filename: return self._parent.controller.write_stress_to_csv(filename, self.detailed.isChecked())
def setup_and_check(self, data, title=''): """ Setup DataFrameEditor: return False if data is not supported, True otherwise """ self.layout = QGridLayout() self.setLayout(self.layout) self.setWindowIcon(ima.icon('arredit')) if title: title = to_text_string(title) + " - %s" % data.__class__.__name__ else: title = _("%s editor") % data.__class__.__name__ if isinstance(data, Series): self.is_series = True data = data.to_frame() self.setWindowTitle(title) self.resize(600, 500) self.dataModel = DataFrameModel(data, parent=self) self.dataTable = DataFrameView(self, self.dataModel) self.layout.addWidget(self.dataTable) self.setLayout(self.layout) self.setMinimumSize(400, 300) # Make the dialog act as a window self.setWindowFlags(Qt.Window) btn_layout = QHBoxLayout() btn = QPushButton(_("Format")) # disable format button for int type btn_layout.addWidget(btn) btn.clicked.connect(self.change_format) btn = QPushButton(_('Resize')) btn_layout.addWidget(btn) btn.clicked.connect(self.resize_to_contents) bgcolor = QCheckBox(_('Background color')) bgcolor.setChecked(self.dataModel.bgcolor_enabled) bgcolor.setEnabled(self.dataModel.bgcolor_enabled) bgcolor.stateChanged.connect(self.change_bgcolor_enable) btn_layout.addWidget(bgcolor) self.bgcolor_global = QCheckBox(_('Column min/max')) self.bgcolor_global.setChecked(self.dataModel.colum_avg_enabled) self.bgcolor_global.setEnabled(not self.is_series and self.dataModel.bgcolor_enabled) self.bgcolor_global.stateChanged.connect(self.dataModel.colum_avg) btn_layout.addWidget(self.bgcolor_global) btn_layout.addStretch() bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) btn_layout.addWidget(bbox) self.layout.addLayout(btn_layout, 2, 0) return True
def add_new_row(self, _metadata, row=0): self.parent.ui.table.insertRow(row) _layout = QHBoxLayout() _widget = QCheckBox() _widget.setEnabled(True) _layout.addWidget(_widget) _layout.addStretch() _new_widget = QWidget() _new_widget.setLayout(_layout) _widget.stateChanged.connect(lambda state=0, row=row: self.parent. table_select_state_changed(state, row)) self.parent.ui.table.setCellWidget(row, 0, _new_widget) _item = QTableWidgetItem(_metadata['name']) self.parent.ui.table.setItem(row, 1, _item) _item = QTableWidgetItem(_metadata['runs']) self.parent.ui.table.setItem(row, 2, _item) if not _metadata['sample_formula']: _item = QTableWidgetItem(_metadata['sample_formula']) self.parent.ui.table.setItem(row, 3, _item) if not _metadata['mass_density']: _item = QTableWidgetItem(_metadata['mass_density']) self.parent.ui.table.setItem(row, 4, _item) if not _metadata['radius']: _item = QTableWidgetItem(_metadata['radius']) self.parent.ui.table.setItem(row, 5, _item) if not _metadata['packing_fraction']: _item = QTableWidgetItem(_metadata['packing_fraction']) self.parent.ui.table.setItem(row, 6, _item) _widget = QComboBox() _widget.addItem("cylindrical") _widget.addItem("spherical") if _metadata['sample_shape'] == 'spherical': _widget.setCurrentIndex(1) self.parent.ui.table.setCellWidget(row, 7, _widget) _layout = QHBoxLayout() _widget = QCheckBox() if _metadata['do_abs_correction'] == 'go': _widget.setCheckState(Qt.Checked) _widget.setStyleSheet("border: 2px; solid-black") _widget.setEnabled(True) _layout.addStretch() _layout.addWidget(_widget) _layout.addStretch() _new_widget = QWidget() _new_widget.setLayout(_layout) self.parent.ui.table.setCellWidget(row, 8, _new_widget)
def add_new_row(self, _metadata, row=0): self.main_window.postprocessing_ui.table.insertRow(row) _layout = QHBoxLayout() _widget = QCheckBox() _widget.setEnabled(True) _layout.addWidget(_widget) _layout.addStretch() _new_widget = QWidget() _new_widget.setLayout(_layout) _widget.stateChanged.connect(lambda state=0, row=row: self.parent.table_select_state_changed(state, row)) self.main_window_postprocessing_ui.table.setCellWidget(row, 0, _new_widget) _item = QTableWidgetItem(_metadata['name']) self.main_window_postprocessing_ui.table.setItem(row, 1, _item) _item = QTableWidgetItem(_metadata['runs']) self.main_window_postprocessing_ui.table.setItem(row, 2, _item) if not _metadata['sample_formula']: _item = QTableWidgetItem(_metadata['sample_formula']) self.main_window_postprocessing_ui.table.setItem(row, 3, _item) if not _metadata['mass_density']: _item = QTableWidgetItem(_metadata['mass_density']) self.main_window_postprocessing_ui.table.setItem(row, 4, _item) if not _metadata['radius']: _item = QTableWidgetItem(_metadata['radius']) self.main_window_postprocessing_ui.table.setItem(row, 5, _item) if not _metadata['packing_fraction']: _item = QTableWidgetItem(_metadata['packing_fraction']) self.main_window_postprocessing_ui.table.setItem(row, 6, _item) _widget = QComboBox() _widget.addItem("cylindrical") _widget.addItem("spherical") if _metadata['sample_shape'] == 'spherical': _widget.setCurrentIndex(1) self.main_window_postprocessing_ui.table.setCellWidget(row, 7, _widget) _layout = QHBoxLayout() _widget = QCheckBox() if _metadata['do_abs_correction'] == 'go': _widget.setCheckState(Qt.Checked) _widget.setStyleSheet("border: 2px; solid-black") _widget.setEnabled(True) _layout.addStretch() _layout.addWidget(_widget) _layout.addStretch() _new_widget = QWidget() _new_widget.setLayout(_layout) self.main_window_postprocessing_ui.table.setCellWidget(row, 8, _new_widget)
class MovieArgsPrompt(QtWidgets.QWidget): def __init__(self, parent=None): super(MovieArgsPrompt, self).__init__(parent) self.create_controls() def create_controls(self): self.num_fps = QtWidgets.QDoubleSpinBox() self.num_fps.setValue(15.0) self.num_fps.setMinimum(0.001) self.edt_fname = QLineEdit() self.btn_browse = QtWidgets.QPushButton("...") self.num_dpi = QtWidgets.QSpinBox() self.num_dpi.setValue(72) self.num_dpi.setMinimum(1) self.num_dpi.setMaximum(10000) self.chk_axes = QCheckBox("Axes") self.chk_colorbar = QCheckBox("Colorbar") # codec = mpl.rcParams['animation.codec'] codec = 'h264' self.edt_codec = QLineEdit(codec) self.edt_extra = QLineEdit("-preset veryslow -crf 0") # TODO: Use QCompleter or QComboBox for codecs # TODO: Use QCompleter for 'extra' history # TODO: Bitrate and/or quality slider self.chk_verbose = QCheckBox("Verbose") try: sys.stdout.fileno() except: self.chk_verbose.setEnabled(False) self.chk_verbose.setToolTip("Verbose output does not work with " + "internal console.") frm = QtWidgets.QFormLayout() frm.addRow(tr("FPS:"), self.num_fps) frm.addRow(tr("DPI:"), self.num_dpi) frm.addRow(tr("Codec:"), self.edt_codec) frm.addRow(tr("Extra args:"), self.edt_extra) frm.addRow(self.chk_axes, self.chk_colorbar) hbox = QtWidgets.QHBoxLayout() hbox.addWidget(self.edt_fname) hbox.addWidget(self.btn_browse) frm.addRow(tr("File:"), hbox) frm.addRow("", self.chk_verbose) self.setLayout(frm)
def __init__(self, parent, data, readonly=False, xlabels=None, ylabels=None): QWidget.__init__(self, parent) self.data = data print(type(self.data)) self.old_data_shape = None if len(self.data.shape) == 1: self.old_data_shape = self.data.shape self.data.shape = (self.data.shape[0], 1) elif len(self.data.shape) == 0: self.old_data_shape = self.data.shape self.data.shape = (1, 1) format = SUPPORTED_FORMATS.get(data.dtype.name, '%s') self.model = ArrayModel(self.data, format=format, xlabels=xlabels, ylabels=ylabels, readonly=readonly, parent=self) self.view = ArrayView(self, self.model, data.dtype, data.shape) btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) btn = QPushButton("Format") # disable format button for int type btn.setEnabled(is_float(data.dtype)) btn_layout.addWidget(btn) btn.clicked.connect(self.change_format) btn = QPushButton("Resize") btn_layout.addWidget(btn) btn.clicked.connect(self.view.resize_to_contents) bgcolor = QCheckBox('Background color') bgcolor.setChecked(self.model.bgcolor_enabled) bgcolor.setEnabled(self.model.bgcolor_enabled) bgcolor.stateChanged.connect(self.model.bgcolor) btn_layout.addWidget(bgcolor) layout = QVBoxLayout() layout.addWidget(self.view) layout.addLayout(btn_layout) self.setLayout(layout)
def create_control_panel(self): # Create control panel # +/- range cntrl_layout = QHBoxLayout() cntrl_layout.addWidget(QLabel("+/-")) self.range_edit = QLineEdit("{:.2f}".format(YRANGE)) self.range_edit.editingFinished.connect( self.on_range_edit_editingFinished) cntrl_layout.addWidget(self.range_edit) # buttons for audio monitoring cntrl_layout.addStretch(1) cntrl_layout.addWidget(QLabel("Monitor: ")) monitor_group = QButtonGroup(parent=self) none_button = QRadioButton("None") none_button.setChecked(True) monitor_group.addButton(none_button) monitor_group.setId(none_button, 0) cntrl_layout.addWidget(none_button) for chan_ix in range(len(self.group_info)): new_button = QRadioButton(self.group_info[chan_ix]['label']) monitor_group.addButton(new_button) monitor_group.setId(new_button, chan_ix + 1) cntrl_layout.addWidget(new_button) monitor_group.buttonClicked[int].connect(self.on_monitor_group_clicked) # Checkbox for HP filter filter_checkbox = QCheckBox("HP") filter_checkbox.stateChanged.connect(self.on_hp_filter_changed) filter_checkbox.setChecked(True) cntrl_layout.addWidget(filter_checkbox) # Checkbox for Comb filter filter_checkbox = QCheckBox("LN") filter_checkbox.setEnabled(False) filter_checkbox.stateChanged.connect(self.on_ln_filter_changed) filter_checkbox.setChecked(False) cntrl_layout.addWidget(filter_checkbox) # Finish self.layout().addLayout(cntrl_layout)
def __init__(self, parent, data, readonly=False, xlabels=None, ylabels=None): QWidget.__init__(self, parent) self.data = data self.old_data_shape = None if len(self.data.shape) == 1: self.old_data_shape = self.data.shape self.data.shape = (self.data.shape[0], 1) elif len(self.data.shape) == 0: self.old_data_shape = self.data.shape self.data.shape = (1, 1) format = SUPPORTED_FORMATS.get(data.dtype.name, '%s') self.model = ArrayModel(self.data, format=format, xlabels=xlabels, ylabels=ylabels, readonly=readonly, parent=self) self.view = ArrayView(self, self.model, data.dtype, data.shape) btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) btn = QPushButton(_( "Format")) # disable format button for int type btn.setEnabled(is_float(data.dtype)) btn_layout.addWidget(btn) btn.clicked.connect(self.change_format) btn = QPushButton(_( "Resize")) btn_layout.addWidget(btn) btn.clicked.connect(self.view.resize_to_contents) bgcolor = QCheckBox(_( 'Background color')) bgcolor.setChecked(self.model.bgcolor_enabled) bgcolor.setEnabled(self.model.bgcolor_enabled) bgcolor.stateChanged.connect(self.model.bgcolor) btn_layout.addWidget(bgcolor) layout = QVBoxLayout() layout.addWidget(self.view) layout.addLayout(btn_layout) self.setLayout(layout)
class AnimationWindow(PyDialog): """ +-------------------+ | Animation | +-------------------------+ | icase ______ | | scale ______ Default | | time ______ Default | | | | nframes ______ Default | | resolu. ______ Default | | Dir ______ Browse | | iFrame ______ | | | | Animations: | | o Scale, Phase, Time | | | | x delete images | | x repeat | # TODO: change to an integer | x make gif | | | | Step, RunAll | | Close | +-------------------------+ TODO: add key-frame support """ def __init__(self, data, win_parent=None): PyDialog.__init__(self, data, win_parent) self.set_font_size(data['font_size']) self.istep = 0 self._animate_type = 'time' self._updated_animation = False self._active_deformation = 0. self._icase_fringe = data['icase_fringe'] self._icase_disp = data['icase_disp'] self._icase_vector = data['icase_vector'] self._default_name = data['name'] self._default_time = data['time'] self._default_fps = data['frames/sec'] self._default_resolution = data['resolution'] self._scale = data['scale'] self._default_scale = data['default_scale'] self._default_is_scale = data['is_scale'] self._arrow_scale = data['arrow_scale'] self._default_arrow_scale = data['default_arrow_scale'] self._phase = data['phase'] self._default_phase = data['default_phase'] self._default_dirname = data['dirname'] self._default_gif_name = os.path.join(self._default_dirname, data['name'] + '.gif') self.animation_types = [ 'Animate Scale', ] #'Animate Phase', #'Animate Time', #'Animate Frequency Sweep' #] self.setWindowTitle('Animate Model') self.create_widgets() self.create_layout() self.set_connections() self.is_gui = False if hasattr(self.win_parent, '_updated_legend'): self.win_parent.is_animate_open = True self.is_gui = True self.on_update_min_max_defaults() def create_widgets(self): """creates the menu objects""" self.box_scale = QGroupBox('Animate Scale') self.box_time = QGroupBox('Animate Time') icase_max = 1000 # TODO: update 1000 self.checkbox_fringe = QCheckBox('Animate') self.checkbox_fringe.setToolTip( 'Animate the fringe in addition to the deflection') #self.checkbox_disp = QCheckBox('Animate') self.checkbox_fringe.setEnabled(False) self.icase_fringe_label = QLabel("iCase (Fringe):") self.icase_fringe_edit = QSpinBox(self) self.icase_fringe_edit.setRange(0, icase_max) self.icase_fringe_edit.setSingleStep(1) if self._icase_fringe is not None: self.icase_fringe_edit.setValue(self._icase_fringe) self.icase_fringe_edit.setToolTip( 'Case Number for the Scale/Phase Animation Type.\n' 'Defaults to the result you had shown when you clicked "Create Animation".\n' 'iCase can be seen by clicking "Apply" on a result.') self.icase_disp_label = QLabel("iCase (Disp):") self.icase_disp_edit = QSpinBox(self) self.icase_disp_edit.setRange(1, icase_max) self.icase_disp_edit.setSingleStep(1) if self._icase_disp is not None: self.icase_disp_edit.setValue(self._icase_disp) self.checkbox_vector = QCheckBox('Animate') self.checkbox_vector.setToolTip( 'Animate the vector in addition to the deflection') self.checkbox_vector.hide() #self.checkbox_disp = QCheckBox('Animate') self.icase_vector_label = QLabel("iCase (Vector):") self.icase_vector_edit = QSpinBox(self) self.icase_vector_edit.setRange(1, icase_max) self.icase_vector_edit.setSingleStep(1) if self._icase_vector is not None: self.icase_vector_edit.setValue(self._icase_vector) self.scale_label = QLabel("True Scale:") self.scale_edit = QLineEdit(str(self._scale)) self.scale_button = QPushButton("Default") self.scale_edit.setToolTip('Scale factor of the "deflection"') self.scale_button.setToolTip('Sets the scale factor of the gif to %s' % self._scale) self.arrow_scale_label = QLabel("Arrow Scale:") self.arrow_scale_edit = QLineEdit(str(self._scale)) self.arrow_scale_button = QPushButton("Default") self.arrow_scale_edit.setToolTip('Scale factor of the "arrows"') self.arrow_scale_button.setToolTip( 'Sets the arrow scale factor of the gif to %s' % self._arrow_scale) self.arrow_scale_label.setVisible(False) self.arrow_scale_edit.setVisible(False) self.arrow_scale_button.setVisible(False) self.time_label = QLabel("Total Time (sec):") self.time_edit = QDoubleSpinBox(self) self.time_edit.setValue(self._default_time) self.time_edit.setRange(0.1, 5. * 60.) self.time_edit.setDecimals(2) self.time_edit.setSingleStep(0.1) self.time_button = QPushButton("Default") self.time_edit.setToolTip("Total time of the gif") self.time_button.setToolTip('Sets the total time of the gif to %.2f' % self._default_time) self.fps_label = QLabel("Frames/Second:") self.fps_edit = QSpinBox(self) self.fps_edit.setRange(1, 60) self.fps_edit.setSingleStep(1) self.fps_edit.setValue(self._default_fps) self.fps_button = QPushButton("Default") self.fps_edit.setToolTip( "A higher FPS is smoother, but may not play well for large gifs") self.fps_button.setToolTip('Sets the FPS to %s' % self._default_fps) self.resolution_label = QLabel("Resolution Scale:") self.resolution_edit = QSpinBox(self) self.resolution_edit.setRange(1, 5) self.resolution_edit.setSingleStep(1) self.resolution_edit.setValue(self._default_resolution) self.resolution_button = QPushButton("Default") self.resolution_edit.setToolTip( 'Scales the window resolution by an integer factor') self.resolution_button.setToolTip('Sets the resolution to %s' % self._default_resolution) #----------------- # Time plot self.fringe_label = QLabel("Fringe") self.icase_fringe_start_edit = QSpinBox(self) self.icase_fringe_start_edit.setRange(0, icase_max) self.icase_fringe_start_edit.setSingleStep(1) self.icase_fringe_start_edit.setValue(self._icase_fringe) self.icase_fringe_start_button = QPushButton("Default") self.icase_fringe_end_edit = QSpinBox(self) self.icase_fringe_end_edit.setRange(0, icase_max) self.icase_fringe_end_edit.setSingleStep(1) self.icase_fringe_end_edit.setValue(self._icase_fringe) self.icase_fringe_end_button = QPushButton("Default") self.icase_fringe_delta_edit = QSpinBox(self) self.icase_fringe_delta_edit.setRange(1, icase_max) self.icase_fringe_delta_edit.setSingleStep(1) self.icase_fringe_delta_edit.setValue(1) self.icase_fringe_delta_button = QPushButton("Default") self.displacement_label = QLabel("Displacement") self.icase_start = QLabel("iCase Start:") self.icase_disp_start_edit = QSpinBox(self) self.icase_disp_start_edit.setRange(0, icase_max) self.icase_disp_start_edit.setSingleStep(1) self.icase_disp_start_edit.setValue(self._icase_fringe) self.icase_disp_start_button = QPushButton("Default") self.icase_end_label = QLabel("iCase End:") self.icase_disp_end_edit = QSpinBox(self) self.icase_disp_end_edit.setRange(0, icase_max) self.icase_disp_end_edit.setSingleStep(1) self.icase_disp_end_edit.setValue(self._icase_fringe) self.icase_disp_end_button = QPushButton("Default") self.icase_delta_label = QLabel("iCase Delta:") self.icase_disp_delta_edit = QSpinBox(self) self.icase_disp_delta_edit.setRange(1, icase_max) self.icase_disp_delta_edit.setSingleStep(1) self.icase_disp_delta_edit.setValue(1) self.icase_disp_delta_button = QPushButton("Default") self.min_value_enable = QCheckBox() self.min_value_label = QLabel("Min Value:") self.min_value_edit = QLineEdit('') #self.min_value_edit.setRange(1, 1000) #self.min_value_edit.setSingleStep(1) #self.min_value_edit.setValue(1) self.min_value_button = QPushButton("Default") self.max_value_enable = QCheckBox() self.max_value_label = QLabel("Max Value:") self.max_value_edit = QLineEdit('') #self.min_value_edit.setRange(1, 1000) # TODO: update 1000 #self.min_value_edit.setSingleStep(1) #self.min_value_edit.setValue(1) self.max_value_button = QPushButton("Default") # TODO: enable this (uncomment) ------------------------------------------ #self.min_value_enable.hide() #self.min_value.hide() #self.min_value_edit.hide() #self.min_value_button.hide() #self.max_value_enable.hide() #self.max_value.hide() #self.max_value_edit.hide() #self.max_value_button.hide() # TODO: enable this (uncomment) ------------------------------------------ self.icase_disp_start_edit.setToolTip( 'The first frame of the animation') self.icase_disp_end_edit.setToolTip( 'The last frame of the animation\n' 'Assumes icase_start + nframes * icase_delta = icase_end') self.icase_disp_delta_edit.setToolTip( 'The frame step size (to skip non-consecutive results).\n' 'Frame skipping can be used to:\n' " - skip across results that you don't want to plot\n" ' - adjust the FPS') self.min_value_edit.setToolTip( 'Min value of the legend (not supported)') self.max_value_edit.setToolTip( 'Max value of the legend (not supported)') #'time' : 0., #'default_time' : 0, #'icase_start' : 10, #'icase_delta' : 3, #'min_value' : 0., #'max_value' : 1000., self.browse_folder_label = QLabel('Output Directory:') self.browse_folder_edit = QLineEdit(str(self._default_dirname)) self.browse_folder_button = QPushButton('Browse') self.browse_folder_edit.setToolTip( 'Location to save the png/gif files') self.gif_label = QLabel("Gif Filename:") self.gif_edit = QLineEdit(str(self._default_name + '.gif')) self.gif_button = QPushButton('Default') self.gif_edit.setToolTip('Name of the gif') self.gif_button.setToolTip('Sets the name of the gif to %s.gif' % self._default_name) # scale / phase if 1: # pragma: no cover self.animate_scale_radio = QRadioButton("Animate Scale") self.animate_phase_radio = QRadioButton("Animate Phase") self.animate_time_radio = QRadioButton("Animate Time") self.animate_freq_sweeep_radio = QRadioButton( "Animate Frequency Sweep") self.animate_scale_radio.setToolTip( 'Animates the scale factor based on the "Animation Type"') self.animate_time_radio.setToolTip( 'Animates the time/load/mode step') self.animate_scale_radio.setChecked(self._default_is_scale) self.animate_phase_radio.setChecked(not self._default_is_scale) self.animate_time_radio.setChecked(False) msg = 'Scale : Animates the scale factor based on the "Animation Profile"\n' if self._default_phase is None: self.animate_phase_radio.setDisabled(True) self.animate_phase_radio.setToolTip( 'Animates the phase angle ' '(only for complex results)') msg += 'Phase : Animates the phase angle (only for complex results)\n' else: self.animate_phase_radio.setToolTip("Animates the phase angle") msg += 'Phase : Animates the phase angle\n' msg += ( 'Time : Animates the time/load/mode step\n' 'Freq Sweep : Animates a complex result across a range of frequencies ' '(not supported)\n') self.animate_freq_sweeep_radio.setDisabled(True) self.animate_freq_sweeep_radio.setToolTip( 'Animates a complex result across a range of frequencies (not supported)' ) else: msg = 'Scale : Animates the scale factor based on the "Animation Profile"\n' if self._default_phase is None: #self.animate_phase_radio.setDisabled(True) #self.animate_phase_radio.setToolTip('Animates the phase angle ' #'(only for complex results)') msg += 'Phase : Animates the phase angle (only for complex results)\n' else: #self.animate_phase_radio.setToolTip("Animates the phase angle") msg += 'Phase : Animates the phase angle\n' msg += ( 'Time : Animates the time/load/mode step\n' 'Freq Sweep : Animates a complex result across a range of frequencies ' '(not supported)\n') self.animation_type = QLabel("Animation Type:") animation_type = OrderedDict() #scale_msg = 'Scale\n' #phase_msg = 'Phase\n' #time_msg = 'Time\n' #animation_types = [ #('Animate Scale', scale_msg), #('Animate Phase', phase_msg), #('Animate Time', time_msg), ##'Animate Frequency Sweep' #] if self._phase is not None: self.animation_types.append('Animate Phase') self.animation_types.append('Animate Time') self.animation_profile_label = QLabel("Animation Profile:") self.animation_profile_edit = QComboBox() for animation_profile in ANIMATION_PROFILES: self.animation_profile_edit.addItem(animation_profile) self.animation_profile_edit.setToolTip('The profile for a scaled GIF') self.animation_type_edit = QComboBox() # TODO: add a tooltip for each item for animation_type in self.animation_types: self.animation_type_edit.addItem(animation_type) #self.animation_type_edit.setToolTip('The profile for a scaled GIF') self.animation_type_edit.setToolTip(msg.rstrip()) self.csv_profile_label = QLabel("CSV profile:") self.csv_profile_edit = QLineEdit() self.csv_profile_browse_button = QPushButton('Browse') self.csv_profile_edit.setToolTip( 'The path to the CSV file of (Scale1, Scale2, Scale3, ...)') #widget = QWidget(self) #horizontal_vertical_group = QButtonGroup(widget) #horizontal_vertical_group.addButton(self.animate_scale_radio) #horizontal_vertical_group.addButton(self.animate_phase_radio) #horizontal_vertical_group.addButton(self.animate_time_radio) #horizontal_vertical_group.addButton(self.animate_freq_sweeep_radio) # animate in gui self.animate_in_gui_checkbox = QCheckBox("Animate In GUI?") self.animate_in_gui_checkbox.setChecked(True) # make images self.make_images_checkbox = QCheckBox("Make images?") self.make_images_checkbox.setChecked(True) # make images self.overwrite_images_checkbox = QCheckBox("Overwrite images?") self.overwrite_images_checkbox.setChecked(True) # delete images when finished self.delete_images_checkbox = QCheckBox("Delete images when finished?") self.delete_images_checkbox.setChecked(True) # endless loop self.repeat_checkbox = QCheckBox("Repeat?") self.repeat_checkbox.setChecked(True) self.repeat_checkbox.setToolTip( "Repeating creates an infinitely looping gif") # endless loop self.make_gif_checkbox = QCheckBox("Make Gif?") if IS_IMAGEIO: self.make_gif_checkbox.setChecked(True) else: self.make_gif_checkbox.setChecked(False) self.make_gif_checkbox.setEnabled(False) self.make_gif_checkbox.setToolTip( 'imageio is not available; install it') # bottom buttons self.step_button = QPushButton("Step") self.wipe_button = QPushButton("Wipe Deformed Shape") self.stop_button = QPushButton("Stop") self.run_button = QPushButton("Run") self.step_button.setToolTip( 'Steps through the animation (for testing)') self.wipe_button.setToolTip( 'Removes the existing "deflecton" from the animation') self.stop_button.setToolTip('Stops the animation') self.run_button.setToolTip('Creates the animation') self.step_button.hide() self.wipe_button.hide() self.wipe_button.setEnabled(False) #self.wipe_button.hide() self.stop_button.setEnabled(False) self.cancel_button = QPushButton("Close") #self.set_grid_time(enabled=False) #self.set_grid_scale(enabled=self._default_is_scale) if self._default_phase: self.on_animate_phase(force=True) set_combo_box_text(self.animation_type_edit, 'Animate Phase') else: self.on_animate_scale(force=True) def set_connections(self): """creates button actions""" self.checkbox_vector.clicked.connect(self.on_checkbox_vector) self.scale_button.clicked.connect(self.on_default_scale) self.arrow_scale_button.clicked.connect(self.on_default_arrow_scale) self.time_button.clicked.connect(self.on_default_time) self.fps_button.clicked.connect(self.on_default_fps) self.resolution_button.clicked.connect(self.on_default_resolution) self.browse_folder_button.clicked.connect(self.on_browse_folder) self.csv_profile_browse_button.clicked.connect(self.on_browse_csv) self.gif_button.clicked.connect(self.on_default_name) self.step_button.clicked.connect(self.on_step) self.wipe_button.clicked.connect(self.on_wipe) self.stop_button.clicked.connect(self.on_stop) self.run_button.clicked.connect(self.on_run) self.min_value_enable.clicked.connect(self.on_min_value_enable) self.max_value_enable.clicked.connect(self.on_max_value_enable) self.min_value_button.clicked.connect(self.on_min_value_default) self.max_value_button.clicked.connect(self.on_max_value_default) self.icase_disp_start_button.clicked.connect( self.on_update_min_max_defaults) #self.animate_scale_radio.clicked.connect(self.on_animate_scale) #self.animate_phase_radio.clicked.connect(self.on_animate_phase) #self.animate_time_radio.clicked.connect(self.on_animate_time) self.animation_type_edit.currentIndexChanged.connect(self.on_animate) #self.animate_freq_sweeep_radio self.cancel_button.clicked.connect(self.on_cancel) self.animate_in_gui_checkbox.clicked.connect(self.on_animate_in_gui) self.animate_in_gui_checkbox.setChecked(True) self.on_animate_in_gui() def on_checkbox_vector(self): is_enabled = self.checkbox_vector.isEnabled() is_checked = self.checkbox_vector.isChecked() enable_edit = is_enabled and is_checked self.icase_vector_label.setEnabled(is_checked) self.icase_vector_edit.setEnabled(enable_edit) def on_animate_in_gui(self): animate_in_gui = self.animate_in_gui_checkbox.isChecked() enable = not animate_in_gui if HIDE_WHEN_INACTIVE: self.make_images_checkbox.setVisible(enable) self.delete_images_checkbox.setVisible(enable) self.make_gif_checkbox.setVisible(enable) self.repeat_checkbox.setVisible(enable) self.resolution_button.setVisible(enable) self.resolution_label.setVisible(enable) self.resolution_edit.setVisible(enable) self.gif_label.setVisible(enable) self.gif_edit.setVisible(enable) self.gif_button.setVisible(enable) self.browse_folder_label.setVisible(enable) self.browse_folder_button.setVisible(enable) self.browse_folder_edit.setVisible(enable) self.step_button.setEnabled(enable) self.make_images_checkbox.setEnabled(enable) self.delete_images_checkbox.setEnabled(enable) self.make_gif_checkbox.setEnabled(enable) self.repeat_checkbox.setEnabled(enable) self.resolution_button.setEnabled(enable) self.resolution_edit.setEnabled(enable) self.gif_edit.setEnabled(enable) self.gif_button.setEnabled(enable) self.browse_folder_button.setEnabled(enable) self.browse_folder_edit.setEnabled(enable) self.step_button.setEnabled(enable) #wipe_button def on_animate(self, value): """ animate pulldown Parameters ---------- value : int index in animation_types """ #animation_types = ['Animate Scale', 'Animate Phase', 'Animate Time', #'Animate Frequency Sweep'] animation_type = self.animation_types[value] if animation_type == 'Animate Scale': self.on_animate_scale() elif animation_type == 'Animate Phase': self.on_animate_phase() elif animation_type == 'Animate Time': self.on_animate_time() else: raise NotImplementedError('value = ', value) def on_animate_time(self, force=False): """enables the secondary input""" #print('on_animate_time') if self._animate_type == 'scale' or force: self.set_grid_scale(False, 'time') self.set_grid_time(True, 'time') self._animate_type = 'time' def on_animate_scale(self, force=False): """enables the secondary input""" #print('on_animate_scale') self.set_grid_scale(True, 'scale') if self._animate_type == 'time' or force: self.set_grid_time(False, 'scale') self._animate_type = 'scale' def on_animate_phase(self, force=False): """enables the secondary input""" #print('on_animate_phase') if self._animate_type == 'scale' or force: self.set_grid_scale(False, 'phase') if self._animate_type == 'time' or force: self.set_grid_time(False, 'phase') self._animate_type = 'phase' def set_grid_scale(self, enabled=True, word=''): """enables/disables the secondary input""" #print('%s-set_grid_scale; enabled = %r' % (word, enabled)) if HIDE_WHEN_INACTIVE: self.box_scale.setVisible(enabled) self.animation_profile_label.setVisible(enabled) self.animation_profile_edit.setVisible(enabled) #self.min_value_enable.setVisible(enabled) #self.max_value_enable.setVisible(enabled) self.animation_profile_label.setEnabled(enabled) self.animation_profile_edit.setEnabled(enabled) # TODO: doesn't work... #self.csv_profile.setEnabled(enabled) #self.csv_profile_edit.setEnabled(enabled) #self.csv_profile_button.setEnabled(enabled) self.min_value_enable.setEnabled(enabled) self.max_value_enable.setEnabled(enabled) self.on_min_value_enable() self.on_max_value_enable() def set_grid_time(self, enabled=True, word=''): """enables/disables the secondary input""" #print('%s-set_grid_time; enabled = %r' % (word, enabled)) if HIDE_WHEN_INACTIVE: self.box_time.setVisible(enabled) self.checkbox_fringe.setVisible(not enabled) self.icase_fringe_label.setVisible(not enabled) self.icase_fringe_edit.setVisible(not enabled) self.icase_disp_label.setVisible(not enabled) self.icase_disp_edit.setVisible(not enabled) self.icase_vector_label.setVisible(not enabled) self.icase_vector_edit.setVisible(not enabled) #self.icase_fringe_delta_edit.setVisible(enabled) self.icase_disp_delta_edit.setVisible(enabled) self.icase_disp_delta_edit.setVisible(enabled) self.fps_label.setVisible(enabled) self.fps_edit.setVisible(enabled) self.fps_button.setVisible(enabled) self.displacement_label.setEnabled(enabled) self.fringe_label.setEnabled(enabled) self.icase_start.setEnabled(enabled) self.icase_disp_start_edit.setEnabled(enabled) self.icase_disp_start_button.setEnabled(enabled) self.icase_fringe_start_edit.setEnabled(enabled) self.icase_fringe_start_button.setEnabled(enabled) self.icase_end_label.setEnabled(enabled) self.icase_disp_end_edit.setEnabled(enabled) self.icase_disp_end_button.setEnabled(enabled) self.icase_fringe_end_edit.setEnabled(enabled) self.icase_fringe_end_button.setEnabled(enabled) self.icase_delta_label.setEnabled(enabled) self.icase_disp_delta_edit.setEnabled(enabled) self.icase_disp_delta_button.setEnabled(enabled) self.icase_fringe_delta_edit.setEnabled(enabled) self.icase_fringe_delta_button.setEnabled(enabled) #----------------------------------------------------------------------- is_min_enabled = self.min_value_enable.isChecked() self.min_value_label.setEnabled(is_min_enabled) self.min_value_edit.setEnabled(is_min_enabled) self.min_value_button.setEnabled(is_min_enabled) is_max_enabled = self.max_value_enable.isChecked() self.max_value_label.setEnabled(is_max_enabled) self.max_value_edit.setEnabled(is_max_enabled) self.max_value_button.setEnabled(is_max_enabled) self.min_value_enable.setEnabled(enabled) self.on_min_value_enable() #self.min_value.setEnabled(enabled) #self.min_value_edit.setEnabled(enabled) #self.min_value_button.setEnabled(enabled) self.max_value_enable.setEnabled(enabled) self.on_max_value_enable() #self.max_value.setEnabled(enabled) #self.max_value_edit.setEnabled(enabled) #self.max_value_button.setEnabled(enabled) self.icase_fringe_label.setEnabled(not enabled) self.icase_fringe_edit.setEnabled(not enabled) self.checkbox_fringe.setEnabled(not enabled) self.icase_disp_label.setEnabled(not enabled) self.icase_disp_edit.setEnabled(not enabled) self.icase_vector_label.setEnabled(not enabled) self.icase_vector_edit.setEnabled(not enabled) self.checkbox_vector.setEnabled(not enabled) self.on_checkbox_vector() self.fps_label.setEnabled(not enabled) self.fps_edit.setEnabled(not enabled) self.fps_button.setEnabled(not enabled) def on_min_value_enable(self): """ The min edit value box is enabled when we switch to time and the box is checked """ is_min_enabled = self.min_value_enable.isChecked( ) and self.min_value_enable.isEnabled() self.min_value_label.setEnabled(is_min_enabled) self.min_value_edit.setEnabled(is_min_enabled) self.min_value_button.setEnabled(is_min_enabled) def on_max_value_enable(self): """ The max edit value box is enabled when we switch to time and the box is checked """ is_max_enabled = self.max_value_enable.isChecked( ) and self.max_value_enable.isEnabled() self.max_value_label.setEnabled(is_max_enabled) self.max_value_edit.setEnabled(is_max_enabled) self.max_value_button.setEnabled(is_max_enabled) def on_update_min_max_defaults(self): """ When the icase is changed, the min/max value default message is changed """ icase = self.icase_disp_start_edit.value() min_value, max_value = self.get_min_max(icase) self.min_value_button.setToolTip('Sets the min value to %g' % min_value) self.max_value_button.setToolTip('Sets the max value to %g' % max_value) def on_min_value_default(self): """When min default icase is pressued, update the value""" icase = self.icase_disp_start_edit.value() min_value = self.get_min_max(icase)[0] self.min_value_edit.setText(str(min_value)) self.min_value_edit.setStyleSheet("QLineEdit{background: white;}") def on_max_value_default(self): """When max default icase is pressued, update the value""" icase = self.icase_disp_start_edit.value() max_value = self.get_min_max(icase)[1] self.max_value_edit.setText(str(max_value)) self.max_value_edit.setStyleSheet("QLineEdit{background: white;}") def on_browse_folder(self): """opens a folder dialog""" dirname = getexistingdirectory(parent=self, caption='Select a Directory', basedir='', options=QFileDialog.ShowDirsOnly) if not dirname: return self.browse_folder_edit.setText(dirname) def on_browse_csv(self): """opens a file dialog""" default_filename = '' file_types = 'Delimited Text (*.txt; *.dat; *.csv)' dirname = open_file_dialog(self, 'Select a CSV File', default_filename, file_types) if not dirname: return self.csv_profile_browse_button.setText(dirname) def on_default_name(self): """sets the default gif name""" self.gif_edit.setText(self._default_name + '.gif') def on_default_scale(self): """sets the default displacement scale factor""" self.scale_edit.setText(str(self._default_scale)) self.scale_edit.setStyleSheet("QLineEdit{background: white;}") def on_default_arrow_scale(self): """sets the default arrow scale factor""" self.arrow_scale_edit.setText(str(self._default_arrow_scale)) self.arrow_scale_edit.setStyleSheet("QLineEdit{background: white;}") def on_default_time(self): """sets the default gif time""" self.time_edit.setValue(self._default_time) def on_default_fps(self): """sets the default FPS""" self.fps_edit.setValue(self._default_fps) def on_default_resolution(self): """sets the default image resolution scale factor""" self.resolution_edit.setValue(self._default_resolution) def create_layout(self): """displays the menu objects""" grid = QGridLayout() irow = 0 grid.addWidget(self.icase_fringe_label, irow, 0) grid.addWidget(self.icase_fringe_edit, irow, 1) grid.addWidget(self.checkbox_fringe, irow, 2) irow += 1 grid.addWidget(self.icase_disp_label, irow, 0) grid.addWidget(self.icase_disp_edit, irow, 1) #grid.addWidget(self.checkbox_disp, irow, 2) irow += 1 grid.addWidget(self.icase_vector_label, irow, 0) grid.addWidget(self.icase_vector_edit, irow, 1) grid.addWidget(self.checkbox_vector, irow, 2) irow += 1 grid.addWidget(self.scale_label, irow, 0) grid.addWidget(self.scale_edit, irow, 1) grid.addWidget(self.scale_button, irow, 2) irow += 1 grid.addWidget(self.arrow_scale_label, irow, 0) grid.addWidget(self.arrow_scale_edit, irow, 1) grid.addWidget(self.arrow_scale_button, irow, 2) irow += 1 grid.addWidget(self.time_label, irow, 0) grid.addWidget(self.time_edit, irow, 1) grid.addWidget(self.time_button, irow, 2) irow += 1 # spacer spacer = QLabel('') grid.addWidget(self.fps_label, irow, 0) grid.addWidget(self.fps_edit, irow, 1) grid.addWidget(self.fps_button, irow, 2) irow += 1 grid.addWidget(self.animation_type, irow, 0) grid.addWidget(self.animation_type_edit, irow, 1) irow += 1 grid.addWidget(spacer, irow, 0) irow += 1 #---------- #Time grid_time = QGridLayout() jrow = 0 self.fringe_label.setAlignment(Qt.AlignCenter) self.displacement_label.setAlignment(Qt.AlignCenter) if not IS_TIME_FRINGE: self.fringe_label.hide() self.icase_fringe_delta_edit.hide() self.icase_fringe_start_edit.hide() self.icase_fringe_end_edit.hide() self.icase_fringe_delta_button.hide() grid_time.addWidget(self.displacement_label, jrow, 1) grid_time.addWidget(self.fringe_label, jrow, 2) jrow += 1 grid_time.addWidget(self.icase_start, jrow, 0) grid_time.addWidget(self.icase_disp_start_edit, jrow, 1) grid_time.addWidget(self.icase_fringe_start_edit, jrow, 2) #grid_time.addWidget(self.icase_disp_start_button, jrow, 2) jrow += 1 grid_time.addWidget(self.icase_end_label, jrow, 0) grid_time.addWidget(self.icase_disp_end_edit, jrow, 1) grid_time.addWidget(self.icase_fringe_end_edit, jrow, 2) #grid_time.addWidget(self.icase_end_button, jrow, 2) jrow += 1 grid_time.addWidget(self.icase_delta_label, jrow, 0) grid_time.addWidget(self.icase_disp_delta_edit, jrow, 1) grid_time.addWidget(self.icase_fringe_delta_edit, jrow, 2) #grid_time.addWidget(self.icase_delta_button, jrow, 2) jrow += 1 hbox_min = QHBoxLayout() hbox_min.addWidget(self.min_value_enable) hbox_min.addWidget(self.min_value_label) grid_time.addLayout(hbox_min, jrow, 0) grid_time.addWidget(self.min_value_edit, jrow, 1) grid_time.addWidget(self.min_value_button, jrow, 2) jrow += 1 hbox_max = QHBoxLayout() hbox_max.addWidget(self.max_value_enable) hbox_max.addWidget(self.max_value_label) grid_time.addLayout(hbox_max, jrow, 0) grid_time.addWidget(self.max_value_edit, jrow, 1) grid_time.addWidget(self.max_value_button, jrow, 2) jrow += 1 grid_time.addWidget(spacer, jrow, 0) jrow += 1 #-------------- grid_scale = QGridLayout() grid_scale.addWidget(self.animation_profile_label, 0, 0) grid_scale.addWidget(self.animation_profile_edit, 0, 1) #grid_scale.addWidget(self.csv_profile, 1, 0) #grid_scale.addWidget(self.csv_profile_edit, 1, 1) #grid_scale.addWidget(self.csv_profile_browse_button, 1, 2) self.csv_profile = QLabel("CSV profile:") self.csv_profile_edit = QLineEdit() self.csv_profile_button = QPushButton('Browse') #box_time = QVBoxLayout() # TODO: It's super annoying that the animate time box doesn't # line up with the previous box self.box_scale.setLayout(grid_scale) self.box_time.setLayout(grid_time) #---------- grid2 = QGridLayout() irow = 0 #grid2.addWidget(self.animate_scale_radio, 8, 0) #grid2.addWidget(self.animate_phase_radio, 8, 1) #grid2.addWidget(self.animate_time_radio, 8, 2) #grid2.addWidget(self.animate_freq_sweeep_radio, 8, 3) grid2.addWidget(self.animate_in_gui_checkbox, irow, 0) irow += 1 grid2.addWidget(self.resolution_label, irow, 0) grid2.addWidget(self.resolution_edit, irow, 1) grid2.addWidget(self.resolution_button, irow, 2) irow += 1 grid2.addWidget(self.browse_folder_label, irow, 0) grid2.addWidget(self.browse_folder_edit, irow, 1) grid2.addWidget(self.browse_folder_button, irow, 2) irow += 1 grid2.addWidget(self.gif_label, irow, 0) grid2.addWidget(self.gif_edit, irow, 1) grid2.addWidget(self.gif_button, irow, 2) irow += 1 grid2.addWidget(self.make_images_checkbox, irow, 0) #grid2.addWidget(self.overwrite_images_checkbox, irow, 0) grid2.addWidget(self.delete_images_checkbox, irow, 1) grid2.addWidget(self.make_gif_checkbox, irow, 2) irow += 1 grid2.addWidget(self.repeat_checkbox, irow, 0) irow += 1 grid2.addWidget(spacer, irow, 0) grid_hbox = QHBoxLayout() grid_hbox.addWidget(spacer) grid_hbox.addLayout(grid2) grid_hbox.addWidget(spacer) # bottom buttons step_run_box = QHBoxLayout() step_run_box.addWidget(self.step_button) step_run_box.addWidget(self.wipe_button) step_run_box.addWidget(self.stop_button) step_run_box.addWidget(self.run_button) ok_cancel_box = QHBoxLayout() ok_cancel_box.addWidget(self.cancel_button) vbox = QVBoxLayout() vbox.addLayout(grid) vbox.addWidget(self.box_scale) vbox.addWidget(self.box_time) #vbox.addLayout(checkboxes) vbox.addLayout(grid_hbox) vbox.addStretch() vbox.addLayout(step_run_box) vbox.addLayout(ok_cancel_box) self.setLayout(vbox) def on_step(self): """click the Step button""" passed, validate_out = self.on_validate() if passed: try: self._make_gif(validate_out, istep=self.istep) self.istep += 1 except IndexError: self._make_gif(validate_out, istep=0) self.istep += 1 self.wipe_button.setEnabled(True) def on_wipe(self): """click the Wipe button""" passed, validate_out = self.on_validate(wipe=True) if passed: self.istep = 0 self._make_gif(validate_out, istep=self.istep) self.wipe_button.setEnabled(False) self.stop_button.setEnabled(False) def on_stop(self): """click the Stop button""" #passed, validate_out = self.on_validate() #if passed: #self._make_gif(validate_out, stop_animation=True) if self.is_gui: self.win_parent.win_parent.stop_animation() self.wipe_button.setEnabled(True) self.stop_button.setEnabled(False) def on_run(self): """click the Run button""" self.istep = 0 self.wipe_button.setEnabled(False) self.stop_button.setEnabled(True) passed, validate_out = self.on_validate() if passed: self._make_gif(validate_out, istep=None) return passed def _make_gif(self, validate_out, istep=None, stop_animation=False): """interface for making the gif""" (icase_fringe, icase_disp, icase_vector, scale, time, fps, animate_in_gui, magnify, output_dir, gifbase, min_value, max_value) = validate_out fps = int(fps) gif_filename = None if not stop_animation and not animate_in_gui and gifbase is not None: if gifbase.lower().endswith('.gif'): gifbase = gifbase[:-4] gif_filename = os.path.join(output_dir, gifbase + '.gif') animate_fringe = self.checkbox_fringe.isChecked() animate_vector = self.checkbox_vector.isChecked() animate_scale = self.animate_scale_radio.isChecked() animate_phase = self.animate_phase_radio.isChecked() animate_time = self.animate_time_radio.isChecked() if not self.checkbox_vector.isEnabled(): icase_vector = None animate_scale = False animate_phase = False animate_time = False if self._animate_type == 'scale': animate_scale = True elif self._animate_type == 'phase': animate_phase = True elif self._animate_type == 'time': animate_time = True else: raise NotImplementedError(self._animate_type) make_images = self.make_images_checkbox.isChecked() delete_images = self.delete_images_checkbox.isChecked() make_gif = self.make_gif_checkbox.isChecked() animation_profile = str(self.animation_profile_edit.currentText()) icase_disp_start = self.icase_disp_start_edit.value() icase_disp_end = self.icase_disp_end_edit.value() icase_disp_delta = self.icase_disp_delta_edit.value() bool_repeat = self.repeat_checkbox.isChecked( ) # TODO: change this to an integer if bool_repeat: nrepeat = 0 else: nrepeat = 1 #self.out_data['is_shown'] = self.show_radio.isChecked() #icase = self._icase if self.is_gui: self.win_parent.win_parent.make_gif( gif_filename, scale, istep=istep, animate_scale=animate_scale, animate_phase=animate_phase, animate_time=animate_time, icase_fringe=icase_fringe, icase_disp=icase_disp, icase_vector=icase_vector, animate_fringe=animate_fringe, animate_vector=animate_vector, icase_start=icase_disp_start, icase_end=icase_disp_end, icase_delta=icase_disp_delta, time=time, animation_profile=animation_profile, nrepeat=nrepeat, fps=fps, magnify=magnify, make_images=make_images, delete_images=delete_images, make_gif=make_gif, stop_animation=stop_animation, animate_in_gui=animate_in_gui, min_value=min_value, max_value=max_value, ) self.out_data['clicked_ok'] = True self.out_data['close'] = True def get_min_max(self, icase): if self.is_gui: (obj, (i, name)) = self.win_parent.win_parent.result_cases[icase] min_value, max_value = obj.get_min_max(i, name) else: return 0., 1.0 return min_value, max_value def on_validate(self, wipe=False): """checks to see if the input is valid""" # requires no special validation icase_fringe, flag0 = check_int(self.icase_fringe_edit) icase_disp, unused_flaga = check_int(self.icase_disp_edit) icase_vector, unused_flagb = check_int(self.icase_vector_edit) #icase_disp = self._icase_disp #icase_vector = self._icase_vector scale, flag1 = check_float(self.scale_edit) time, flag2 = check_float(self.time_edit) fps, flag3 = check_float(self.fps_edit) min_value = max_value = None flag4 = flag5 = True if self.min_value_edit.isEnabled(): min_value, flag4 = check_float(self.min_value_edit) if self.max_value_edit.isEnabled(): max_value, flag5 = check_float(self.max_value_edit) if wipe: animate_in_gui = False scale = 0. flag1 = True else: animate_in_gui = self.animate_in_gui_checkbox.isChecked() if animate_in_gui or wipe: passed = all([flag0, flag1, flag2, flag3, flag4, flag5]) magnify, output_dir, gifbase = None, None, None else: magnify, flag6 = check_int(self.resolution_edit) output_dir, flag7 = self.check_path(self.browse_folder_edit) gifbase, flag8 = self.check_name(self.gif_edit) passed = all([ flag0, flag1, flag2, flag3, flag4, flag5, flag6, flag7, flag8 ]) return passed, (icase_fringe, icase_disp, icase_vector, scale, time, fps, animate_in_gui, magnify, output_dir, gifbase, min_value, max_value) @staticmethod def check_name(cell): """verifies that the data is string-able""" cell_value = cell.text() try: text = str(cell_value).strip() except UnicodeEncodeError: cell.setStyleSheet("QLineEdit{background: red;}") return None, False if len(text): cell.setStyleSheet("QLineEdit{background: white;}") return text, True else: cell.setStyleSheet("QLineEdit{background: red;}") return None, False def check_path(self, cell): """verifies that the path exists""" text, passed = self.check_name(cell) if not passed: return None, False if os.path.exists(text): cell.setStyleSheet("QLineEdit{background: white;}") return text, True else: cell.setStyleSheet("QLineEdit{background: red;}") return None, False #def on_ok(self): #"""click the OK button""" #passed = self.on_apply() #if passed: #self.win_parent._animation_window_shown = False #self.close() ##self.destroy() def on_cancel(self): """click the Cancel button""" self.on_stop() self.out_data['close'] = True self.close()
class LSPServerEditor(QDialog): DEFAULT_HOST = '127.0.0.1' DEFAULT_PORT = 2084 DEFAULT_CMD = '' DEFAULT_ARGS = '' DEFAULT_CONFIGURATION = '{}' DEFAULT_EXTERNAL = False DEFAULT_STDIO = False HOST_REGEX = re.compile(r'^\w+([.]\w+)*$') NON_EMPTY_REGEX = re.compile(r'^\S+$') JSON_VALID = _('Valid JSON') JSON_INVALID = _('Invalid JSON') MIN_SIZE = QSize(850, 600) INVALID_CSS = "QLineEdit {border: 1px solid red;}" VALID_CSS = "QLineEdit {border: 1px solid green;}" def __init__(self, parent, language=None, cmd='', host='127.0.0.1', port=2084, args='', external=False, stdio=False, configurations={}, **kwargs): super(LSPServerEditor, self).__init__(parent) description = _( "To create a new server configuration, you need to select a " "programming language, set the command to start its associated " "server and enter any arguments that should be passed to it on " "startup. Additionally, you can set the server's hostname and " "port if connecting to an external server, " "or to a local one using TCP instead of stdio pipes." "<br><br>" "<i>Note</i>: You can use the placeholders <tt>{host}</tt> and " "<tt>{port}</tt> in the server arguments field to automatically " "fill in the respective values.<br>" ) self.parent = parent self.external = external # Widgets self.server_settings_description = QLabel(description) self.lang_cb = QComboBox(self) self.external_cb = QCheckBox(_('External server'), self) self.host_label = QLabel(_('Host:')) self.host_input = QLineEdit(self) self.port_label = QLabel(_('Port:')) self.port_spinner = QSpinBox(self) self.cmd_label = QLabel(_('Command:')) self.cmd_input = QLineEdit(self) self.args_label = QLabel(_('Arguments:')) self.args_input = QLineEdit(self) self.json_label = QLabel(self.JSON_VALID, self) self.conf_label = QLabel(_('<b>Server Configuration:</b>')) self.conf_input = CodeEditor(None) self.bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = self.bbox.button(QDialogButtonBox.Ok) self.button_cancel = self.bbox.button(QDialogButtonBox.Cancel) # Widget setup self.setMinimumSize(self.MIN_SIZE) self.setWindowTitle(_('LSP server editor')) self.server_settings_description.setWordWrap(True) self.lang_cb.setToolTip( _('Programming language provided by the LSP server')) self.lang_cb.addItem(_('Select a language')) self.lang_cb.addItems(LSP_LANGUAGES) self.button_ok.setEnabled(False) if language is not None: idx = LSP_LANGUAGES.index(language) self.lang_cb.setCurrentIndex(idx + 1) self.button_ok.setEnabled(True) self.host_input.setPlaceholderText('127.0.0.1') self.host_input.setText(host) self.host_input.textChanged.connect(lambda x: self.validate()) self.port_spinner.setToolTip(_('TCP port number of the server')) self.port_spinner.setMinimum(1) self.port_spinner.setMaximum(60000) self.port_spinner.setValue(port) self.cmd_input.setText(cmd) self.cmd_input.setPlaceholderText('/absolute/path/to/command') self.args_input.setToolTip( _('Additional arguments required to start the server')) self.args_input.setText(args) self.args_input.setPlaceholderText(r'--host {host} --port {port}') self.conf_input.setup_editor( language='json', color_scheme=CONF.get('appearance', 'selected'), wrap=False, edge_line=True, highlight_current_line=True, highlight_current_cell=True, occurrence_highlighting=True, auto_unindent=True, font=get_font(), filename='config.json', folding=False ) self.conf_input.set_language('json', 'config.json') self.conf_input.setToolTip(_('Additional LSP server configuration ' 'set at runtime. JSON required')) try: conf_text = json.dumps(configurations, indent=4, sort_keys=True) except Exception: conf_text = '{}' self.conf_input.set_text(conf_text) self.external_cb.setToolTip( _('Check if the server runs on a remote location')) self.external_cb.setChecked(external) self.stdio_cb = QCheckBox(_('Use stdio pipes for communication'), self) self.stdio_cb.setToolTip(_('Check if the server communicates ' 'using stdin/out pipes')) self.stdio_cb.setChecked(stdio) # Layout setup hlayout = QHBoxLayout() general_vlayout = QVBoxLayout() general_vlayout.addWidget(self.server_settings_description) vlayout = QVBoxLayout() lang_group = QGroupBox(_('Language')) lang_layout = QVBoxLayout() lang_layout.addWidget(self.lang_cb) lang_group.setLayout(lang_layout) vlayout.addWidget(lang_group) server_group = QGroupBox(_('Language server')) server_layout = QGridLayout() server_layout.addWidget(self.cmd_label, 0, 0) server_layout.addWidget(self.cmd_input, 0, 1) server_layout.addWidget(self.args_label, 1, 0) server_layout.addWidget(self.args_input, 1, 1) server_group.setLayout(server_layout) vlayout.addWidget(server_group) address_group = QGroupBox(_('Server address')) host_layout = QVBoxLayout() host_layout.addWidget(self.host_label) host_layout.addWidget(self.host_input) port_layout = QVBoxLayout() port_layout.addWidget(self.port_label) port_layout.addWidget(self.port_spinner) conn_info_layout = QHBoxLayout() conn_info_layout.addLayout(host_layout) conn_info_layout.addLayout(port_layout) address_group.setLayout(conn_info_layout) vlayout.addWidget(address_group) advanced_group = QGroupBox(_('Advanced')) advanced_layout = QVBoxLayout() advanced_layout.addWidget(self.external_cb) advanced_layout.addWidget(self.stdio_cb) advanced_group.setLayout(advanced_layout) vlayout.addWidget(advanced_group) conf_layout = QVBoxLayout() conf_layout.addWidget(self.conf_label) conf_layout.addWidget(self.conf_input) conf_layout.addWidget(self.json_label) vlayout.addStretch() hlayout.addLayout(vlayout, 2) hlayout.addLayout(conf_layout, 3) general_vlayout.addLayout(hlayout) general_vlayout.addWidget(self.bbox) self.setLayout(general_vlayout) self.form_status(False) # Signals if not external: self.cmd_input.textChanged.connect(lambda x: self.validate()) self.external_cb.stateChanged.connect(self.set_local_options) self.stdio_cb.stateChanged.connect(self.set_stdio_options) self.lang_cb.currentIndexChanged.connect(self.lang_selection_changed) self.conf_input.textChanged.connect(self.validate) self.bbox.accepted.connect(self.accept) self.bbox.rejected.connect(self.reject) # Final setup if language is not None: self.form_status(True) self.validate() if stdio: self.set_stdio_options(True) if external: self.set_local_options(True) @Slot() def validate(self): host_text = self.host_input.text() cmd_text = self.cmd_input.text() if not self.HOST_REGEX.match(host_text): self.button_ok.setEnabled(False) self.host_input.setStyleSheet(self.INVALID_CSS) if bool(host_text): self.host_input.setToolTip(_('Hostname must be valid')) else: self.host_input.setToolTip( _('Hostname or IP address of the host on which the server ' 'is running. Must be non empty.')) else: self.host_input.setStyleSheet(self.VALID_CSS) self.host_input.setToolTip(_('Hostname is valid')) self.button_ok.setEnabled(True) if not self.external: if not self.NON_EMPTY_REGEX.match(cmd_text): self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet(self.INVALID_CSS) self.cmd_input.setToolTip( _('Command used to start the LSP server locally. Must be ' 'non empty')) return if find_program(cmd_text) is None: self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet(self.INVALID_CSS) self.cmd_input.setToolTip(_('Program was not found ' 'on your system')) else: self.cmd_input.setStyleSheet(self.VALID_CSS) self.cmd_input.setToolTip(_('Program was found on your ' 'system')) self.button_ok.setEnabled(True) try: json.loads(self.conf_input.toPlainText()) try: self.json_label.setText(self.JSON_VALID) except Exception: pass except ValueError: try: self.json_label.setText(self.JSON_INVALID) self.button_ok.setEnabled(False) except Exception: pass def form_status(self, status): self.host_input.setEnabled(status) self.port_spinner.setEnabled(status) self.external_cb.setEnabled(status) self.stdio_cb.setEnabled(status) self.cmd_input.setEnabled(status) self.args_input.setEnabled(status) self.conf_input.setEnabled(status) self.json_label.setVisible(status) @Slot() def lang_selection_changed(self): idx = self.lang_cb.currentIndex() if idx == 0: self.set_defaults() self.form_status(False) self.button_ok.setEnabled(False) else: server = self.parent.get_server_by_lang(LSP_LANGUAGES[idx - 1]) self.form_status(True) if server is not None: self.host_input.setText(server.host) self.port_spinner.setValue(server.port) self.external_cb.setChecked(server.external) self.stdio_cb.setChecked(server.stdio) self.cmd_input.setText(server.cmd) self.args_input.setText(server.args) self.conf_input.set_text(json.dumps(server.configurations)) self.json_label.setText(self.JSON_VALID) self.button_ok.setEnabled(True) else: self.set_defaults() def set_defaults(self): self.cmd_input.setStyleSheet('') self.host_input.setStyleSheet('') self.host_input.setText(self.DEFAULT_HOST) self.port_spinner.setValue(self.DEFAULT_PORT) self.external_cb.setChecked(self.DEFAULT_EXTERNAL) self.stdio_cb.setChecked(self.DEFAULT_STDIO) self.cmd_input.setText(self.DEFAULT_CMD) self.args_input.setText(self.DEFAULT_ARGS) self.conf_input.set_text(self.DEFAULT_CONFIGURATION) self.json_label.setText(self.JSON_VALID) @Slot(bool) @Slot(int) def set_local_options(self, enabled): self.external = enabled self.cmd_input.setEnabled(True) self.args_input.setEnabled(True) if enabled: self.cmd_input.setEnabled(False) self.cmd_input.setStyleSheet('') self.args_input.setEnabled(False) self.stdio_cb.stateChanged.disconnect() self.stdio_cb.setChecked(False) self.stdio_cb.setEnabled(False) else: self.cmd_input.setEnabled(True) self.args_input.setEnabled(True) self.stdio_cb.setEnabled(True) self.stdio_cb.setChecked(False) self.stdio_cb.stateChanged.connect(self.set_stdio_options) try: self.validate() except Exception: pass @Slot(bool) @Slot(int) def set_stdio_options(self, enabled): self.stdio = enabled if enabled: self.cmd_input.setEnabled(True) self.args_input.setEnabled(True) self.external_cb.stateChanged.disconnect() self.external_cb.setChecked(False) self.external_cb.setEnabled(False) self.host_input.setStyleSheet('') self.host_input.setEnabled(False) self.port_spinner.setEnabled(False) else: self.cmd_input.setEnabled(True) self.args_input.setEnabled(True) self.external_cb.setChecked(False) self.external_cb.setEnabled(True) self.external_cb.stateChanged.connect(self.set_local_options) self.host_input.setEnabled(True) self.port_spinner.setEnabled(True) try: self.validate() except Exception: pass def get_options(self): language_idx = self.lang_cb.currentIndex() language = LSP_LANGUAGES[language_idx - 1] host = self.host_input.text() port = int(self.port_spinner.value()) external = self.external_cb.isChecked() stdio = self.stdio_cb.isChecked() args = self.args_input.text() cmd = self.cmd_input.text() configurations = json.loads(self.conf_input.toPlainText()) server = LSPServer(language=language.lower(), cmd=cmd, args=args, host=host, port=port, external=external, stdio=stdio, configurations=configurations) return server
class SiriusScrnView(QWidget): """ Class to read Sirius screen cameras image data. To allow saving a grid correctly, control calibrationgrid_flag, which indicates if the screen is in calibration grid position. You can control it by using the method/Slot updateCalibrationGridFlag. """ save_files = Signal() def __init__(self, parent=None, prefix=_VACA_PREFIX, device=None): """Initialize object.""" QWidget.__init__(self, parent=parent) self.prefix = prefix self.device = SiriusPVName(device) self.scrn_prefix = self.device.substitute(prefix=prefix) self._receivedData = False self.setObjectName(self.scrn_prefix.sec + 'App') self.screen_type_conn = SiriusConnectionSignal( self.scrn_prefix.substitute(propty='ScrnType-Sts')) self.screen_type_conn.new_value_signal.connect( self.updateCalibrationGridFlag) self._calibrationgrid_flag = self.screen_type_conn.getvalue() self.save_files.connect(self._saveGridLocalFiles) self.ch_ImgROIHeight = SiriusConnectionSignal( self.scrn_prefix.substitute(propty='ImgROIHeight-RB')) self.ch_ImgROIWidth = SiriusConnectionSignal( self.scrn_prefix.substitute(propty='ImgROIWidth-RB')) self.ch_ImgROIOffsetX = SiriusConnectionSignal( self.scrn_prefix.substitute(propty='ImgROIOffsetX-RB')) self.ch_ImgROIOffsetY = SiriusConnectionSignal( self.scrn_prefix.substitute(propty='ImgROIOffsetY-RB')) self._setupUi() self.setFocus(True) self.setFocusPolicy(Qt.StrongFocus) self._loadCalibrationGrid(default=True) @property def calibrationgrid_flag(self): """Indicate if the screen device is in calibration grid position.""" return self._calibrationgrid_flag @Slot(int) def updateCalibrationGridFlag(self, new_state): """Update calibrationgrid_flag property.""" self._calibrationgrid_flag = new_state if new_state == 1: self.pushbutton_savegrid.setEnabled(True) else: self.pushbutton_savegrid.setEnabled(False) def _setupUi(self): self.cameraview_widget = QWidget() self.cameraview_widget.setLayout(self._cameraviewLayout()) self.settings_widget = QWidget(self) self.settings_widget.setLayout(self._settingsLayout()) self.settings_widget.setStyleSheet(""" .QLabel{ min-width: 5em;} QLabel{ qproperty-alignment: AlignCenter;}""") self.calibrationgrid_widget = QWidget(self) self.calibrationgrid_widget.setLayout(self._calibrationgridLayout()) self.calibrationgrid_widget.setSizePolicy(QSzPlcy.Expanding, QSzPlcy.Expanding) self.calibrationgrid_widget.layout().setAlignment(Qt.AlignHCenter) self.tab = QTabWidget(self) self.tab.setStyleSheet(""" QTabWidget::pane { border-left: 2px solid gray; border-bottom: 2px solid gray; border-right: 2px solid gray; }""") self.tab.addTab(self.settings_widget, 'Camera Settings') self.tab.addTab(self.calibrationgrid_widget, 'Calibration') self.statistics_groupBox = QGroupBox('Statistics', self) self.statistics_groupBox.setLayout(self._statisticsLayout()) self.statistics_groupBox.setSizePolicy(QSzPlcy.Expanding, QSzPlcy.Expanding) self.statistics_groupBox.setStyleSheet(""" .QLabel{ min-width:0.28em; max-width:0.28em; min-height:1.29em; max-height:1.29em;} QLabel{ qproperty-alignment: AlignCenter;} PyDMWidget{ min-width:4.84em; max-width:4.84em; min-height:1.29em; max-height:1.29em;}""") self.trigger_groupBox = QGroupBox('Trigger', self) if 'TB' in self.device or 'BO' in self.device: trg_prefix = 'AS-Fam:TI-Scrn-TBBO' elif 'TS' in self.device: trg_prefix = 'TS-Fam:TI-Scrn' hbl = QHBoxLayout(self.trigger_groupBox) hbl.addWidget( HLTriggerSimple(parent=self.trigger_groupBox, device=trg_prefix, prefix=self.prefix)) self.trigger_groupBox.setLayout(hbl) self.trigger_groupBox.setStyleSheet(""" PyDMWidget{ min-width:4.84em; max-width:4.84em; min-height:1.29em; max-height:1.29em;}""") vlay = QVBoxLayout() vlay.addWidget(self.statistics_groupBox) vlay.addWidget(self.trigger_groupBox) vlay.setSpacing(10) lay = QGridLayout(self) lay.setHorizontalSpacing(10) lay.setVerticalSpacing(10) lay.addWidget(self.cameraview_widget, 0, 0, 1, 2) lay.addWidget(self.tab, 1, 0) lay.addLayout(vlay, 1, 1) lay.setRowStretch(0, 15) lay.setRowStretch(1, 7) def _cameraviewLayout(self): label = QLabel(self.device, self) label.setStyleSheet("""font-weight: bold;max-height:1.29em;""") label.setAlignment(Qt.AlignCenter) self.image_view = _SiriusImageView( parent=self, image_channel=self.scrn_prefix.substitute(propty='ImgData-Mon'), width_channel=self.scrn_prefix.substitute(propty='ImgROIWidth-RB'), offsetx_channel=self.scrn_prefix.substitute( propty='ImgROIOffsetX-RB'), offsety_channel=self.scrn_prefix.substitute( propty='ImgROIOffsetY-RB'), maxwidth_channel=self.scrn_prefix.substitute( propty='ImgMaxWidth-Cte'), maxheight_channel=self.scrn_prefix.substitute( propty='ImgMaxHeight-Cte')) self.image_view.setObjectName('ScrnView') self.image_view.normalizeData = True self.image_view.readingOrder = self.image_view.Clike self.image_view.maxRedrawRate = 15 self.image_view.setStyleSheet(""" #ScrnView{min-width:30em; min-height:24em;}""") self.image_view.failToSaveGrid.connect(self._showFailToSaveGridMsg) self.image_view.receivedData.connect(self._setReceivedDataFlag) lay = QGridLayout() lay.setContentsMargins(0, 0, 0, 0) lay.addWidget(label, 0, 1) lay.addItem(QSpacerItem(40, 2, QSzPlcy.Preferred, QSzPlcy.Fixed), 1, 1) lay.addWidget(self.image_view, 2, 1) return lay def _calibrationgridLayout(self): self.checkBox_showgrid = QCheckBox('Show', self) self.checkBox_showgrid.setEnabled(False) self.checkBox_showgrid.setStyleSheet(""" min-width:4em; max-width:4em; min-height:1.29em; max-height:1.29em;""") self.checkBox_showgrid.toggled.connect( self.image_view.showCalibrationGrid) self.pushbutton_savegrid = QPushButton('Save', self) self.pushbutton_savegrid.setEnabled(False) self.pushbutton_savegrid.setStyleSheet(""" min-width:4em; max-width:4em; min-height:1.29em; max-height:1.29em;""") self.pushbutton_savegrid.clicked.connect(self._saveCalibrationGrid) self.pushbutton_loadgrid = QPushButton('Load', self) self.pushbutton_loadgrid.setStyleSheet(""" min-width:4em; max-width:4em; min-height:1.29em; max-height:1.29em;""") self.pushbutton_loadgrid.clicked.connect(self._loadCalibrationGrid) hbox_grid = QHBoxLayout() hbox_grid.addWidget(self.checkBox_showgrid) hbox_grid.addWidget(self.pushbutton_savegrid) hbox_grid.addWidget(self.pushbutton_loadgrid) lb = QLabel('Show levels <') lb.setStyleSheet("min-width:7em;max-width:7em;") self.spinbox_gridfilterfactor = QSpinBoxPlus() self.spinbox_gridfilterfactor.setMaximum(100) self.spinbox_gridfilterfactor.setMinimum(0) self.spinbox_gridfilterfactor.setValue( self.image_view.calibration_grid_filterfactor) self.spinbox_gridfilterfactor.editingFinished.connect( self._setCalibrationGridFilterFactor) self.spinbox_gridfilterfactor.setStyleSheet(""" min-width:4em; max-width:4em;""") hbox_filter = QHBoxLayout() hbox_filter.setSpacing(0) hbox_filter.addWidget(lb) hbox_filter.addWidget(self.spinbox_gridfilterfactor) hbox_filter.addWidget(QLabel(' %')) lb = QLabel('Remove border: ') lb.setStyleSheet("min-width:7em;max-width:7em;") self.spinbox_removeborder = QSpinBoxPlus() self.spinbox_removeborder.setMaximum(512) self.spinbox_removeborder.setMinimum(0) self.spinbox_removeborder.setValue( self.image_view.calibration_grid_removeborder) self.spinbox_removeborder.editingFinished.connect( self._setCalibrationGridBorder2Remove) self.spinbox_removeborder.setStyleSheet(""" min-width:4em; max-width:4em;""") hbox_remove = QHBoxLayout() hbox_remove.setSpacing(0) hbox_remove.addWidget(lb) hbox_remove.addWidget(self.spinbox_removeborder) hbox_remove.addWidget(QLabel(' px')) hbox_EnblLED = _create_propty_layout(parent=self, prefix=self.scrn_prefix, propty='EnblLED', propty_type='enbldisabl', width=4.68) lay = QFormLayout() lay.addItem(QSpacerItem(1, 10, QSzPlcy.Ignored, QSzPlcy.Preferred)) lay.addRow(' Grid: ', hbox_grid) lay.addItem(QSpacerItem(1, 10, QSzPlcy.Ignored, QSzPlcy.Preferred)) lay.addRow(' ', hbox_filter) lay.addRow(' ', hbox_remove) lay.addItem(QSpacerItem(1, 20, QSzPlcy.Ignored, QSzPlcy.Preferred)) lay.addRow(' LED: ', hbox_EnblLED) lay.addItem(QSpacerItem(1, 10, QSzPlcy.Ignored, QSzPlcy.Preferred)) lay.setLabelAlignment(Qt.AlignRight) lay.setFormAlignment(Qt.AlignCenter) return lay def _settingsLayout(self): label_CamEnbl = QLabel('Enable: ', self) hbox_CamEnbl = _create_propty_layout(parent=self, prefix=self.scrn_prefix, propty='CamEnbl', propty_type='enbldisabl') label_CamAcqPeriod = QLabel('Acquire\nPeriod [s]:', self) hbox_CamAcqPeriod = _create_propty_layout(parent=self, prefix=self.scrn_prefix, propty='CamAcqPeriod', propty_type='sprb', width=5.0) label_CamExposureTime = QLabel('Exposure\nTime [us]:', self) hbox_CamExposureTime = _create_propty_layout(parent=self, prefix=self.scrn_prefix, propty='CamExposureTime', propty_type='sprb', width=5.0) label_CamGain = QLabel('Gain[dB]:', self) hbox_CamGain = _create_propty_layout(parent=self, prefix=self.scrn_prefix, propty='CamGain', width=5.0, propty_type='sprb') label_AutoGain = QLabel('Auto Gain: ', self) self.pb_autogain = PyDMPushButton( label='', icon=qta.icon('mdi.auto-fix'), parent=self, pressValue=1, init_channel=self.scrn_prefix.substitute(propty='CamAutoGain-Cmd')) self.pb_autogain.setObjectName('autog') self.pb_autogain.setStyleSheet( "#autog{min-width:25px; max-width:25px; icon-size:20px;}") cam_prefix = SiriusPVName(self.scrn_prefix).substitute(dev='ScrnCam') label_Reset = QLabel('Reset: ', self) self.pb_dtl = PyDMPushButton( label='', icon=qta.icon('fa5s.sync'), parent=self, pressValue=1, init_channel=cam_prefix.substitute(propty='Rst-Cmd')) self.pb_dtl.setObjectName('reset') self.pb_dtl.setStyleSheet( "#reset{min-width:25px; max-width:25px; icon-size:20px;}") self.pb_details = QPushButton(qta.icon('fa5s.ellipsis-h'), '', self) self.pb_details.setToolTip('More settings') self.pb_details.setObjectName('detail') self.pb_details.setStyleSheet( "#detail{min-width:25px; max-width:25px; icon-size:20px;}") self.pb_details.setSizePolicy(QSzPlcy.Expanding, QSzPlcy.Preferred) util.connect_window(self.pb_details, _ScrnSettingsDetails, parent=self, prefix=self.prefix, device=self.device) hbox_aux = QHBoxLayout() hbox_aux.addWidget(self.pb_dtl, alignment=Qt.AlignLeft) hbox_aux.addWidget(self.pb_details, alignment=Qt.AlignRight) lay = QFormLayout() lay.setFormAlignment(Qt.AlignCenter) lay.addRow(label_CamEnbl, hbox_CamEnbl) lay.addRow(label_CamAcqPeriod, hbox_CamAcqPeriod) lay.addRow(label_CamExposureTime, hbox_CamExposureTime) lay.addRow(label_CamGain, hbox_CamGain) lay.addRow(label_AutoGain, self.pb_autogain) lay.addRow(label_Reset, hbox_aux) return lay def _statisticsLayout(self): # - Method label_Method = QLabel('CalcMethod:', self) label_Method.setStyleSheet("min-width:7em;") self.comboBox_Method = QComboBox(self) self.comboBox_Method.addItem('DimFei', 0) self.comboBox_Method.addItem('NDStats', 1) self.comboBox_Method.setCurrentIndex(0) self.comboBox_Method.setStyleSheet(""" QComboBox::item {height: 1em;} QComboBox{min-width:6em;}""") self.comboBox_Method.currentIndexChanged.connect( self._handleShowStatistics) # - Centroid label_Centroid = QLabel('Centroid [mm]: ', self) label_Centroid.setStyleSheet("min-width:7em;") label_i_Center = QLabel('(', self) self.PyDMLabel_CenterXDimFei = PyDMLabel( parent=self, init_channel=self.scrn_prefix.substitute( propty='CenterXDimFei-Mon')) self.PyDMLabel_CenterXDimFei.setStyleSheet( 'min-width:4em; max-width:4em;') self.PyDMLabel_CenterXNDStats = PyDMLabel( parent=self, init_channel=self.scrn_prefix.substitute( propty='CenterXNDStats-Mon')) self.PyDMLabel_CenterXNDStats.setStyleSheet( 'min-width:4em; max-width:4em;') self.PyDMLabel_CenterXNDStats.setVisible(False) label_m_Center = QLabel(',', self) self.PyDMLabel_CenterYDimFei = PyDMLabel( parent=self, init_channel=self.scrn_prefix.substitute( propty='CenterYDimFei-Mon')) self.PyDMLabel_CenterYDimFei.setStyleSheet( 'min-width:4em; max-width:4em;') self.PyDMLabel_CenterYNDStats = PyDMLabel( parent=self, init_channel=self.scrn_prefix.substitute( propty='CenterYNDStats-Mon')) self.PyDMLabel_CenterYNDStats.setStyleSheet( 'min-width:4em; max-width:4em;') self.PyDMLabel_CenterYNDStats.setVisible(False) label_f_Center = QLabel(')', self) # - Sigma label_Sigma = QLabel('Sigma [mm]: ', self) label_Sigma.setStyleSheet("min-width:7em;") label_i_Sigma = QLabel('(', self) self.PyDMLabel_SigmaXDimFei = PyDMLabel( parent=self, init_channel=self.scrn_prefix.substitute( propty='SigmaXDimFei-Mon')) self.PyDMLabel_SigmaXDimFei.setStyleSheet( 'min-width:4em; max-width:4em;') self.PyDMLabel_SigmaXNDStats = PyDMLabel( parent=self, init_channel=self.scrn_prefix.substitute( propty='SigmaXNDStats-Mon')) self.PyDMLabel_SigmaXNDStats.setStyleSheet( 'min-width:4em; max-width:4em;') self.PyDMLabel_SigmaXNDStats.setVisible(False) label_m_Sigma = QLabel(',', self) self.PyDMLabel_SigmaYDimFei = PyDMLabel( parent=self, init_channel=self.scrn_prefix.substitute( propty='SigmaYDimFei-Mon')) self.PyDMLabel_SigmaYDimFei.setStyleSheet( 'min-width:4em; max-width:4em;') self.PyDMLabel_SigmaYNDStats = PyDMLabel( parent=self, init_channel=self.scrn_prefix.substitute( propty='SigmaYNDStats-Mon')) self.PyDMLabel_SigmaYNDStats.setStyleSheet( 'min-width:4em; max-width:4em;') self.PyDMLabel_SigmaYNDStats.setVisible(False) label_f_Sigma = QLabel(')', self) # - Theta label_Theta = QLabel('Theta [rad]: ') label_Theta.setStyleSheet("min-width:7em;") label_i_Theta = QLabel('(', self) self.PyDMLabel_ThetaDimFei = PyDMLabel( parent=self, init_channel=self.scrn_prefix.substitute(propty='ThetaDimFei-Mon')) self.PyDMLabel_ThetaDimFei.setStyleSheet("max-width:12em;") self.PyDMLabel_ThetaNDStats = PyDMLabel( parent=self, init_channel=self.scrn_prefix.substitute( propty='ThetaNDStats-Mon')) self.PyDMLabel_ThetaNDStats.setStyleSheet("max-width:12em;") self.PyDMLabel_ThetaNDStats.setVisible(False) label_f_Theta = QLabel(')', self) lay = QGridLayout() lay.addWidget(label_Method, 1, 1, 1, 3) lay.addWidget(self.comboBox_Method, 1, 3, 1, 3) lay.addWidget(label_Centroid, 3, 1, alignment=Qt.AlignCenter) lay.addWidget(label_i_Center, 3, 2) lay.addWidget(self.PyDMLabel_CenterXDimFei, 3, 3) lay.addWidget(self.PyDMLabel_CenterXNDStats, 3, 3) lay.addWidget(label_m_Center, 3, 4) lay.addWidget(self.PyDMLabel_CenterYDimFei, 3, 5) lay.addWidget(self.PyDMLabel_CenterYNDStats, 3, 5) lay.addWidget(label_f_Center, 3, 6) lay.addWidget(label_Sigma, 5, 1, alignment=Qt.AlignCenter) lay.addWidget(label_i_Sigma, 5, 2) lay.addWidget(self.PyDMLabel_SigmaXDimFei, 5, 3) lay.addWidget(self.PyDMLabel_SigmaXNDStats, 5, 3) lay.addWidget(label_m_Sigma, 5, 4) lay.addWidget(self.PyDMLabel_SigmaYDimFei, 5, 5) lay.addWidget(self.PyDMLabel_SigmaYNDStats, 5, 5) lay.addWidget(label_f_Sigma, 5, 6) lay.addWidget(label_Theta, 7, 1, alignment=Qt.AlignCenter) lay.addWidget(label_i_Theta, 7, 2) lay.addWidget(self.PyDMLabel_ThetaDimFei, 7, 3, 1, 3) lay.addWidget(self.PyDMLabel_ThetaNDStats, 7, 3, 1, 3) lay.addWidget(label_f_Theta, 7, 6) return lay def _handleShowStatistics(self, visible): self.PyDMLabel_CenterXDimFei.setVisible(not visible) self.PyDMLabel_CenterXNDStats.setVisible(visible) self.PyDMLabel_CenterYDimFei.setVisible(not visible) self.PyDMLabel_CenterYNDStats.setVisible(visible) self.PyDMLabel_ThetaDimFei.setVisible(not visible) self.PyDMLabel_ThetaNDStats.setVisible(visible) self.PyDMLabel_SigmaXDimFei.setVisible(not visible) self.PyDMLabel_SigmaXNDStats.setVisible(visible) self.PyDMLabel_SigmaYDimFei.setVisible(not visible) self.PyDMLabel_SigmaYNDStats.setVisible(visible) def _saveCalibrationGrid(self): t = Thread(target=self._saveCalibrationGrid_thread, daemon=True) t.start() def _saveCalibrationGrid_thread(self): roi_h = float(self.ch_ImgROIHeight.value) roi_w = float(self.ch_ImgROIWidth.value) roi_offsetx = float(self.ch_ImgROIOffsetX.value) roi_offsety = float(self.ch_ImgROIOffsetY.value) cond = roi_h != float(self.image_view.image_maxheight) or \ roi_w != float(self.image_view.image_maxwidth) or \ roi_offsetx != 0 or roi_offsety != 0 if cond: # Disable camera acquisition and wait for disabling self.PyDMStateButton_CamEnbl.send_value_signal[int].emit(0) state = self.SiriusLedState_CamEnbl.state while state == 1: time.sleep(0.1) state = self.SiriusLedState_CamEnbl.state # Change ROI to get entire image self.ch_ImgROIHeight.send_value_signal[float].emit( float(self.image_view.image_maxheight)) self.ch_ImgROIWidth.send_value_signal[float].emit( float(self.image_view.image_maxwidth)) self.ch_ImgROIOffsetX.send_value_signal[float].emit(0) self.ch_ImgROIOffsetY.send_value_signal[float].emit(0) # Enable led and wait for status self.PyDMStateButton_EnblLED.send_value_signal[int].emit(1) while not self.SiriusLedState_EnblLED.state: time.sleep(0.1) # Enable camera acquisition and wait for receiveing first frame self._receivedData = False self.PyDMStateButton_CamEnbl.send_value_signal[int].emit(1) while not self._receivedData: time.sleep(0.1) # Save grid self.image_view.saveCalibrationGrid() if cond: # Disable camera acquisition and wait for disabling self.PyDMStateButton_CamEnbl.send_value_signal[int].emit(0) state = self.SiriusLedState_CamEnbl.state while state == 1: time.sleep(0.1) state = self.SiriusLedState_CamEnbl.state # Change ROI to original size self.ch_ImgROIHeight.send_value_signal[float].emit(roi_h) self.ch_ImgROIWidth.send_value_signal[float].emit(roi_w) self.ch_ImgROIOffsetX.send_value_signal[float].emit(roi_offsetx) self.ch_ImgROIOffsetY.send_value_signal[float].emit(roi_offsety) # Enable camera acquisition self.PyDMStateButton_CamEnbl.send_value_signal[int].emit(1) # Enable showing saved grid time.sleep(0.1) self.checkBox_showgrid.setEnabled(True) self.save_files.emit() def _saveGridLocalFiles(self): home = os.path.expanduser('~') folder_month = datetime.now().strftime('%Y-%m') folder_day = datetime.now().strftime('%Y-%m-%d') path = os.path.join(home, 'mounts', 'screens-iocs', folder_month, folder_day) if not os.path.exists(path): os.makedirs(path) fn, _ = QFileDialog.getSaveFileName( self, 'Save Grid As...', path + '/' + self.device + datetime.now().strftime('_%Y-%m-%d_%Hh%Mmin'), '*.npy') if not fn: return False path_default = os.path.join(home, 'mounts', 'screens-iocs', 'default') if not os.path.exists(path_default): os.makedirs(path_default) fn_default = path_default + '/' + self.device grid = self.image_view.calibrationGrid width = self.image_view.imageWidth data = np.append(width, grid) np.save(fn, data) np.save(fn_default, data) def _loadCalibrationGrid(self, default=False): home = os.path.expanduser('~') if not default: folder_month = datetime.now().strftime('%Y-%m') path = os.path.join(home, 'mounts', 'screens-iocs', folder_month) fn, _ = QFileDialog.getOpenFileName(self, 'Load Grid...', path, '*.npy') if not fn: return if self.device not in fn: ans = QMessageBox.question( self, 'Warning', 'The name of the selected file does not contain the name' + ' of this screen. Are you sure you\'re loading this grid?', QMessageBox.Yes, QMessageBox.Cancel) if ans == QMessageBox.Cancel: return else: path = os.path.join(home, 'mounts', 'screens-iocs', 'default') fn = path + '/' + self.device + '.npy' try: data = np.load(fn) self.image_view.calibrationGrid = data except Exception as e: if not default: QMessageBox.critical( self, 'Error', 'Could not load calibration grid from file ' + fn + '. ' + '\nError message: ' + str(e), QMessageBox.Ok) return # Enable showing saved grid self.checkBox_showgrid.setEnabled(True) def _setReceivedDataFlag(self): self._receivedData = True def _setCalibrationGridFilterFactor(self): self.image_view.set_calibration_grid_filterfactor( self.spinbox_gridfilterfactor.value()) def _setCalibrationGridBorder2Remove(self): self.image_view.set_calibration_grid_border2remove( self.spinbox_removeborder.value()) @Slot() def _showFailToSaveGridMsg(self): QMessageBox.warning(self, 'Warning', 'Could not save calibration grid!', QMessageBox.Ok)
class LSPServerEditor(QDialog): DEFAULT_HOST = '127.0.0.1' DEFAULT_PORT = 2084 DEFAULT_CMD = '' DEFAULT_ARGS = '' DEFAULT_CONFIGURATION = '{}' DEFAULT_EXTERNAL = False HOST_REGEX = re.compile(r'^\w+([.]\w+)*$') NON_EMPTY_REGEX = re.compile(r'^\S+$') JSON_VALID = _('JSON valid') JSON_INVALID = _('JSON invalid') def __init__(self, parent, language=None, cmd='', host='127.0.0.1', port=2084, args='', external=False, configurations={}, **kwargs): super(LSPServerEditor, self).__init__(parent) self.parent = parent self.external = external bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = bbox.button(QDialogButtonBox.Ok) self.button_cancel = bbox.button(QDialogButtonBox.Cancel) self.button_ok.setEnabled(False) description = _('To create a new configuration, ' 'you need to select a programming ' 'language, along with a executable ' 'name for the server to execute ' '(If the instance is local), ' 'and the host and port. Finally, ' 'you need to provide the ' 'arguments that the server accepts. ' 'The placeholders <tt>%(host)s</tt> and ' '<tt>%(port)s</tt> refer to the host ' 'and the port, respectively.') server_settings_description = QLabel(description) server_settings_description.setWordWrap(True) lang_label = QLabel(_('Language:')) self.lang_cb = QComboBox(self) self.lang_cb.setToolTip(_('Programming language provided ' 'by the LSP server')) self.lang_cb.addItem(_('Select a language')) self.lang_cb.addItems(LSP_LANGUAGES) if language is not None: idx = LSP_LANGUAGES.index(language) self.lang_cb.setCurrentIndex(idx + 1) self.button_ok.setEnabled(True) host_label = QLabel(_('Host:')) self.host_input = QLineEdit(self) self.host_input.setToolTip(_('Name of the host that will provide ' 'access to the server')) self.host_input.setText(host) self.host_input.textChanged.connect(lambda x: self.validate()) port_label = QLabel(_('Port:')) self.port_spinner = QSpinBox(self) self.port_spinner.setToolTip(_('TCP port number of the server')) self.port_spinner.setMinimum(1) self.port_spinner.setMaximum(60000) self.port_spinner.setValue(port) cmd_label = QLabel(_('Command to execute:')) self.cmd_input = QLineEdit(self) self.cmd_input.setToolTip(_('Command used to start the ' 'LSP server locally')) self.cmd_input.setText(cmd) if not external: self.cmd_input.textChanged.connect(lambda x: self.validate()) args_label = QLabel(_('Server arguments:')) self.args_input = QLineEdit(self) self.args_input.setToolTip(_('Additional arguments required to ' 'start the server')) self.args_input.setText(args) conf_label = QLabel(_('LSP Server Configurations:')) self.conf_input = CodeEditor(None) self.conf_input.textChanged.connect(self.validate) color_scheme = CONF.get('appearance', 'selected') self.conf_input.setup_editor( language='JSON', color_scheme=color_scheme, wrap=False, edge_line=True, highlight_current_line=True, highlight_current_cell=True, occurrence_highlighting=True, auto_unindent=True, font=get_font(), filename='config.json') self.conf_input.setToolTip(_('Additional LSP server configurations ' 'set at runtime. JSON required')) conf_text = '{}' try: conf_text = json.dumps(configurations, indent=4, sort_keys=True) except Exception: pass self.conf_input.set_text(conf_text) self.json_label = QLabel(self.JSON_VALID, self) self.external_cb = QCheckBox(_('External server'), self) self.external_cb.setToolTip(_('Check if the server runs ' 'on a remote location')) self.external_cb.setChecked(external) self.external_cb.stateChanged.connect(self.set_local_options) hlayout = QHBoxLayout() general_vlayout = QVBoxLayout() general_vlayout.addWidget(server_settings_description) vlayout = QVBoxLayout() lang_layout = QVBoxLayout() lang_layout.addWidget(lang_label) lang_layout.addWidget(self.lang_cb) # layout2 = QHBoxLayout() # layout2.addLayout(lang_layout) lang_layout.addWidget(self.external_cb) vlayout.addLayout(lang_layout) host_layout = QVBoxLayout() host_layout.addWidget(host_label) host_layout.addWidget(self.host_input) port_layout = QVBoxLayout() port_layout.addWidget(port_label) port_layout.addWidget(self.port_spinner) conn_info_layout = QHBoxLayout() conn_info_layout.addLayout(host_layout) conn_info_layout.addLayout(port_layout) vlayout.addLayout(conn_info_layout) cmd_layout = QVBoxLayout() cmd_layout.addWidget(cmd_label) cmd_layout.addWidget(self.cmd_input) vlayout.addLayout(cmd_layout) args_layout = QVBoxLayout() args_layout.addWidget(args_label) args_layout.addWidget(self.args_input) vlayout.addLayout(args_layout) conf_layout = QVBoxLayout() conf_layout.addWidget(conf_label) conf_layout.addWidget(self.conf_input) conf_layout.addWidget(self.json_label) hlayout.addLayout(vlayout) hlayout.addLayout(conf_layout) general_vlayout.addLayout(hlayout) general_vlayout.addWidget(bbox) self.setLayout(general_vlayout) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) self.lang_cb.currentIndexChanged.connect( self.lang_selection_changed) self.form_status(False) if language is not None: self.form_status(True) self.validate() @Slot() def validate(self): host_text = self.host_input.text() cmd_text = self.cmd_input.text() if not self.HOST_REGEX.match(host_text): self.button_ok.setEnabled(False) self.host_input.setStyleSheet("QLineEdit{border: 1px solid red;}") self.host_input.setToolTip('Hostname must be valid') return else: self.host_input.setStyleSheet( "QLineEdit{border: 1px solid green;}") self.host_input.setToolTip('Hostname is valid') self.button_ok.setEnabled(True) if not self.external: if not self.NON_EMPTY_REGEX.match(cmd_text): self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid red;}") self.cmd_input.setToolTip('Command must be non empty') return if find_program(cmd_text) is None: self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid red;}") self.cmd_input.setToolTip('Program was not found ' 'on your system') return else: self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid green;}") self.cmd_input.setToolTip('Program was found on your system') self.button_ok.setEnabled(True) try: json.loads(self.conf_input.toPlainText()) try: self.json_label.setText(self.JSON_VALID) except: pass except (ValueError, json.decoder.JSONDecodeError): try: self.json_label.setText(self.JSON_INVALID) self.button_ok.setEnabled(False) except: pass def form_status(self, status): self.host_input.setEnabled(status) self.port_spinner.setEnabled(status) self.external_cb.setEnabled(status) self.cmd_input.setEnabled(status) self.args_input.setEnabled(status) self.conf_input.setEnabled(status) self.json_label.setVisible(status) @Slot() def lang_selection_changed(self): idx = self.lang_cb.currentIndex() if idx == 0: self.set_defaults() self.form_status(False) self.button_ok.setEnabled(False) else: server = self.parent.get_server_by_lang(LSP_LANGUAGES[idx - 1]) self.form_status(True) if server is not None: self.host_input.setText(server.host) self.port_spinner.setValue(server.port) self.external_cb.setChecked(server.external) self.cmd_input.setText(server.cmd) self.args_input.setText(server.args) self.conf_input.set_text(json.dumps(server.configurations)) self.json_label.setText(self.JSON_VALID) self.button_ok.setEnabled(True) else: self.set_defaults() def set_defaults(self): self.cmd_input.setStyleSheet('') self.host_input.setStyleSheet('') self.host_input.setText(self.DEFAULT_HOST) self.port_spinner.setValue(self.DEFAULT_PORT) self.external_cb.setChecked(self.DEFAULT_EXTERNAL) self.cmd_input.setText(self.DEFAULT_CMD) self.args_input.setText(self.DEFAULT_ARGS) self.conf_input.set_text(self.DEFAULT_CONFIGURATION) self.json_label.setText(self.JSON_VALID) @Slot(bool) @Slot(int) def set_local_options(self, enabled): self.external = enabled self.cmd_input.setEnabled(True) self.args_input.setEnabled(True) if enabled: self.cmd_input.setEnabled(False) self.cmd_input.setStyleSheet('') self.args_input.setEnabled(False) try: self.validate() except: pass def get_options(self): language_idx = self.lang_cb.currentIndex() language = LSP_LANGUAGES[language_idx - 1] host = self.host_input.text() port = int(self.port_spinner.value()) external = self.external_cb.isChecked() args = self.args_input.text() cmd = self.cmd_input.text() configurations = json.loads(self.conf_input.toPlainText()) server = LSPServer(language=language.lower(), cmd=cmd, args=args, host=host, port=port, external=external, configurations=configurations) return server
def __init__(self, parent, data: _Quantity, readonly=False, xlabels=None, ylabels=None, qdata=None): QWidget.__init__(self, parent) self.parent = parent self.qdata = data self.data = data self.old_data_shape = None if len(self.data.m.shape) == 1: self.old_data_shape = self.data.m.shape self.data.m.shape = (self.data.m.shape[0], 1) elif len(self.data.m.shape) == 0: self.old_data_shape = self.data.m.shape self.data.m.shape = (1, 1) format = SUPPORTED_FORMATS.get(data.m.dtype.name, '%s') self.model = QuantityArrayModel(self.data, format=format, xlabels=xlabels, ylabels=ylabels, readonly=readonly, parent=self) self.view = QuantityArrayView(self, self.model, data.m.dtype, data.m.shape) btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) btn = QPushButton("Format") # disable format button for int type btn.setEnabled(is_float(data.m.dtype)) btn_layout.addWidget(btn) btn.clicked.connect(self.change_format) btn = QPushButton("Resize") btn_layout.addWidget(btn) btn.clicked.connect(self.view.resize_to_contents) bgcolor = QCheckBox('Background color') bgcolor.setChecked(self.model.bgcolor_enabled) bgcolor.setEnabled(self.model.bgcolor_enabled) bgcolor.stateChanged.connect(self.model.bgcolor) btn_layout.addWidget(bgcolor) self.unitCombo = QComboBox() self.add_units_to_combo() i = self.unitCombo.findData(self.data.u) self.unitCombo.setCurrentIndex(i) self.unitCombo.currentIndexChanged.connect(self.handle_unit_change) btn_layout.addWidget(self.unitCombo) layout = QVBoxLayout() layout.addWidget(self.view) layout.addLayout(btn_layout) self.setLayout(layout)
def populate_gui(self): _contain_parsed = self.contain_parsed for _row, _entry in enumerate(_contain_parsed): if _entry == ['']: continue self.parent.ui.table.insertRow(_row) # select _layout = QHBoxLayout() _widget = QCheckBox() _widget.setEnabled(True) _layout.addWidget(_widget) _layout.addStretch() _new_widget = QWidget() _new_widget.setLayout(_layout) _widget.stateChanged.connect( lambda state=0, row=_row: self.parent. table_select_state_changed(state, row)) self.parent.ui.table.setCellWidget(_row, 0, _new_widget) # name _item = QTableWidgetItem(_entry[1]) self.parent.ui.table.setItem(_row, 1, _item) # runs _item = QTableWidgetItem(_entry[2]) self.parent.ui.table.setItem(_row, 2, _item) # Sample formula if _entry[3]: _item = QTableWidgetItem(_entry[3]) else: _item = QTableWidgetItem("") self.parent.ui.table.setItem(_row, 3, _item) # mass density if _entry[4]: _item = QTableWidgetItem(_entry[4]) else: _item = QTableWidgetItem("") self.parent.ui.table.setItem(_row, 4, _item) # radius if _entry[5]: _item = QTableWidgetItem(_entry[5]) else: _item = QTableWidgetItem("") self.parent.ui.table.setItem(_row, 5, _item) # packing fraction if _entry[6]: _item = QTableWidgetItem(_entry[6]) else: _item = QTableWidgetItem("") self.parent.ui.table.setItem(_row, 6, _item) # sample shape _widget = QComboBox() _widget.addItem("cylindrical") _widget.addItem("spherical") if _entry[7] == "spherical": _widget.setCurrentIndex(1) self.parent.ui.table.setCellWidget(_row, 7, _widget) # do abs corr _layout = QHBoxLayout() _widget = QCheckBox() if _entry[8] == "True": _widget.setCheckState(Qt.Checked) _widget.setStyleSheet("border: 2px; solid-black") _widget.setEnabled(True) _layout.addStretch() _layout.addWidget(_widget) _layout.addStretch() _new_widget = QWidget() _new_widget.setLayout(_layout) self.parent.ui.table.setCellWidget(_row, 8, _new_widget) for _row, _entry in enumerate(_contain_parsed): if _entry == ['']: continue # select _widget = self.parent.ui.table.cellWidget(_row, 0).children()[1] if _entry[0] == "True": _widget.setChecked(True)
class AlarmTreeEditorDisplay(Display): def __init__(self, parent=None): super(AlarmTreeEditorDisplay, self).__init__(parent=parent) self.app = QApplication.instance() # set up the ui self.setup_ui() # allow add and remove row self.add_button.clicked.connect(self.insertChild) self.remove_button.clicked.connect(self.removeItem) self.remove_button.setEnabled(True) # connect save changes self.button_box.accepted.connect(self.save_property_changes) # upon tree view selection, change the item view self.tree_view.selectionModel().selectionChanged.connect( self.handle_selection) self.tree_view.tree_model.dataChanged.connect(self.item_change) self.file_dialog = QFileDialog() self.open_config_action = QAction("Open", self) self.open_config_action.triggered.connect(self.open_file) self.toolbar.addAction(self.open_config_action) self.save_config_action = QAction("Save", self) self.save_config_action.triggered.connect(self.save_configuration) self.toolbar.addAction(self.save_config_action) # update configuration name self.tree_label.editingFinished.connect(self._update_config_name) # default open size self.resize(800, 600) self.config_tool = PhoebusConfigTool() def setup_ui(self): self.main_layout = QGridLayout() self.setLayout(self.main_layout) # add toolbar self.toolbar = QToolBar() self.main_layout.setMenuBar(self.toolbar) # create the tree view layout and add/remove buttons self.tree_view_layout = QVBoxLayout() self.tree_view = PyDMAlarmTree(self, config_name="UNITITLED", edit_mode=True) self.tree_view.setEditTriggers(QAbstractItemView.DoubleClicked) self.tree_view.setSelectionMode(QAbstractItemView.SingleSelection) self.tree_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.tree_view.setHeaderHidden(True) # Drag/drop self.tree_view.setDragDropMode(QAbstractItemView.InternalMove) self.tree_view.setDragEnabled(True) self.tree_view.setAcceptDrops(True) # view sizing self.tree_view.setColumnWidth(0, 160) self.tree_view.setColumnWidth(1, 160) self.tree_view.setColumnWidth(2, 160) # lable for tree view configuration_indicator = QLabel("Configuration:") self.tree_label = QLineEdit("Untitled") self.tree_label_layout = QHBoxLayout() self.tree_label_layout.addWidget(configuration_indicator) self.tree_label_layout.addWidget(self.tree_label) self.tree_view_layout.addLayout(self.tree_label_layout) self.tree_view_layout.addWidget(self.tree_view) # add/ remove buttons self.add_remove_layout = QHBoxLayout() spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.add_remove_layout.addItem(spacer) self.add_button = QPushButton("New", self) self.add_remove_layout.addWidget(self.add_button) self.remove_button = QPushButton("Remove", self) self.add_remove_layout.addWidget(self.remove_button) self.tree_view_layout.addLayout(self.add_remove_layout) # add the tree view to the window self.main_layout.addLayout(self.tree_view_layout, 0, 0) self.property_layout = QVBoxLayout() self.property_layout.addWidget(QLabel("Alarm Properties")) # crate property view self.property_data_layout = QStackedLayout() self.property_layout.addLayout(self.property_data_layout) self.property_widget_config = QWidget() self.property_widget_config.setWindowTitle("config") # create group widget self.property_widget_group = QWidget() self.property_widget_group.setWindowTitle("group") self.property_view_layout_group = QGridLayout() # add label self.label_edit_group = QLineEdit() self.label_label_group = QLabel("NAME") # add guidance self.guidance_edit_group = QLineEdit() self.guidance_label_group = QLabel("GUIDANCE") self.property_view_layout_group.addWidget(self.label_label_group, 1, 0) self.property_view_layout_group.addWidget(self.label_edit_group, 1, 1) self.property_view_layout_group.addWidget(self.guidance_label_group, 2, 0) self.property_view_layout_group.addWidget(self.guidance_edit_group, 2, 1) spacer = QSpacerItem(40, 200, QSizePolicy.Expanding, QSizePolicy.Minimum) self.property_view_layout_group.addItem(spacer, 3, 0) self.property_view_layout_group.addItem(spacer, 4, 0) self.property_view_layout_group.addItem(spacer, 5, 0) self.property_view_layout_group.addItem(spacer, 6, 0) self.property_view_layout_group.addItem(spacer, 7, 0) self.property_view_layout_group.addItem(spacer, 8, 0) # create pv widget self.property_widget_pv = QWidget() self.property_widget_pv.setWindowTitle("pv") self.property_view_layout_pv = QGridLayout() # add label self.label_edit_pv = QLineEdit() self.label_label_pv = QLabel("NAME") # add guidance self.guidance_edit_pv = QLineEdit() self.guidance_label_pv = QLabel("GUIDANCE") self.property_view_layout_pv.addWidget(self.label_label_pv, 1, 0) self.property_view_layout_pv.addWidget(self.label_edit_pv, 1, 1, 1, 3) self.property_view_layout_pv.addWidget(self.guidance_label_pv, 2, 0) self.property_view_layout_pv.addWidget(self.guidance_edit_pv, 2, 1, 1, 3) # add description self.description_edit = QLineEdit() self.description_label = QLabel("DESCRIPTION") self.property_view_layout_pv.addWidget(self.description_label, 3, 0) self.property_view_layout_pv.addWidget(self.description_edit, 3, 1, 1, 3) # add delay self.delay_edit = QLineEdit() self.delay_label = QLabel("DELAY") self.property_view_layout_pv.addWidget(self.delay_label, 4, 0) self.property_view_layout_pv.addWidget(self.delay_edit, 4, 1, 1, 3) self.delay_edit.setValidator(QIntValidator()) # add count self.count_edit = QLineEdit() self.count_label = QLabel("COUNT") self.property_view_layout_pv.addWidget(self.count_label, 5, 0) self.property_view_layout_pv.addWidget(self.count_edit, 5, 1, 1, 3) self.count_edit.setValidator(QIntValidator()) # add filter/force pv self.filter_edit = QLineEdit() self.filter_label = QLabel("ENABLING FILTER") self.property_view_layout_pv.addWidget(self.filter_label, 6, 0) self.property_view_layout_pv.addWidget(self.filter_edit, 6, 1, 1, 3) # enabled, latching, annunciating self.enabled_check = QCheckBox("ENABLED") self.annunciating_check = QCheckBox("ANNUNCIATING") self.latching_check = QCheckBox("LATCHING") self.property_view_layout_pv.addWidget(self.enabled_check, 7, 0) self.property_view_layout_pv.addWidget(self.annunciating_check, 7, 1) self.property_view_layout_pv.addWidget(self.latching_check, 7, 2) self.property_view_layout_pv.addItem(spacer, 8, 0) # create save button self.button_box = QDialogButtonBox(self) self.button_box.setOrientation(Qt.Horizontal) self.button_box.addButton("Save Properties", QDialogButtonBox.AcceptRole) self.property_layout.addWidget(self.button_box) # self.property_layout.addLayout(self.property_view_layout) self.property_widget_pv.setLayout(self.property_view_layout_pv) self.property_widget_group.setLayout(self.property_view_layout_group) self.property_data_layout.addWidget(self.property_widget_config) self.property_data_layout.addWidget(self.property_widget_pv) self.property_data_layout.addWidget(self.property_widget_group) self.main_layout.addLayout(self.property_layout, 0, 1) self.setWindowTitle("Alarm Tree Editor") self.tree_view.expandAll() def minimumSizeHint(self): # This is the default recommended size # for this screen return QSize(400, 200) def insertChild(self): index = self.tree_view.selectionModel().currentIndex() model = self.tree_view.model() if model.columnCount(index) == 0: if not model.insertColumn(0, index): return if not model.insertRow(0, index): return for column in range(model.columnCount(index)): child = model.index(0, column, index) model.set_data(child, label="NEW_ITEM", role=Qt.EditRole) def removeItem(self): index = self.tree_view.selectionModel().currentIndex() self.tree_view.model().removeRow(index.row(), index.parent()) @Slot() def save_property_changes(self): index = self.tree_view.selectionModel().currentIndex() item = self.tree_view.model().getItem(index) if item.is_group: guidance = self.guidance_edit_group.text() label = self.label_edit_group.text() else: guidance = self.guidance_edit_pv.text() label = self.label_edit_pv.text() self.tree_view.model().set_data( index, label=label, description=self.description_edit.text(), delay=self.delay_edit.text(), count=self.count_edit.text(), enabled=self.enabled_check.isChecked(), annunciating=self.annunciating_check.isChecked(), latching=self.latching_check.isChecked(), alarm_filter=self.filter_edit.text(), guidance=guidance, role=Qt.EditRole, ) @Slot() def handle_selection(self): self.remove_button.setEnabled( self.tree_view.selectionModel().hasSelection()) index = self.tree_view.selectionModel().currentIndex() item = self.tree_view.model().getItem(index) if item.is_group: self.guidance_edit_group.setText(item.guidance) self.label_edit_group.setText(item.label) else: self.guidance_edit_pv.setText(item.guidance) self.label_edit_pv.setText(item.label) if item.is_group: # black for configuration screen if not item.parent: self.property_data_layout.setCurrentWidget( self.property_widget_config) # otherwise show group screen and set all disables else: self.property_data_layout.setCurrentWidget( self.property_widget_group) self.description_edit.setEnabled(False) self.description_edit.setVisible(False) self.description_label.setVisible(False) self.count_edit.setEnabled(False) self.count_edit.setVisible(False) self.count_label.setVisible(False) self.delay_edit.setEnabled(False) self.delay_edit.setVisible(False) self.delay_label.setVisible(False) self.latching_check.setEnabled(False) self.latching_check.setVisible(False) self.annunciating_check.setEnabled(False) self.annunciating_check.setVisible(False) self.filter_edit.setEnabled(False) self.filter_edit.setVisible(False) self.filter_label.setVisible(False) # set pv enabled else: self.property_data_layout.setCurrentWidget(self.property_widget_pv) self.description_edit.setEnabled(True) self.description_edit.setVisible(True) self.description_label.setVisible(True) self.count_edit.setEnabled(True) self.count_edit.setVisible(True) self.count_label.setVisible(True) self.delay_edit.setEnabled(True) self.delay_edit.setVisible(True) self.delay_label.setVisible(True) self.latching_check.setEnabled(True) self.latching_check.setVisible(True) self.annunciating_check.setEnabled(True) self.annunciating_check.setVisible(True) self.filter_edit.setEnabled(True) self.filter_edit.setVisible(True) self.filter_label.setVisible(True) if item.enabled: self.enabled_check.setChecked(True) else: self.enabled_check.setChecked(False) if item.latching: self.latching_check.setChecked(True) else: self.latching_check.setChecked(False) if item.annunciating: self.annunciating_check.setChecked(True) else: self.annunciating_check.setChecked(False) @Slot() def item_change(self): index = self.tree_view.selectionModel().currentIndex() item = self.tree_view.model().getItem(index) if item.is_group: self.guidance_edit_group.setText(item.guidance) self.label_edit_group.setText(item.label) else: self.guidance_edit_pv.setText(item.guidance) self.label_edit_pv.setText(item.label) if item.is_group: if not item.parent(): self.property_data_layout.setCurrentWidget( self.property_widget_config) else: self.property_data_layout.setCurrentWidget( self.property_widget_group) self.description_edit.setEnabled(False) self.description_edit.setVisible(False) self.description_label.setVisible(False) self.count_edit.setEnabled(False) self.count_edit.setVisible(False) self.count_label.setVisible(False) self.delay_edit.setEnabled(False) self.delay_edit.setVisible(False) self.delay_label.setVisible(False) self.latching_check.setEnabled(False) self.latching_check.setVisible(False) self.annunciating_check.setEnabled(False) self.annunciating_check.setVisible(False) self.filter_edit.setEnabled(False) self.filter_edit.setVisible(False) self.filter_label.setVisible(False) else: self.delay_edit.setText(item.delay) self.count_edit.setText(item.count) if item.enabled: self.enabled_check.setChecked(True) else: self.enabled_check.setChecked(False) self.property_data_layout.setCurrentWidget(self.property_widget_pv) self.description_edit.setEnabled(True) self.description_edit.setVisible(True) self.description_label.setVisible(True) self.count_edit.setEnabled(True) self.count_edit.setVisible(True) self.count_label.setVisible(True) self.delay_edit.setEnabled(True) self.delay_edit.setVisible(True) self.delay_label.setVisible(True) self.latching_check.setEnabled(True) self.latching_check.setVisible(True) self.annunciating_check.setEnabled(True) self.annunciating_check.setVisible(True) self.filter_edit.setEnabled(True) self.filter_edit.setVisible(True) self.filter_label.setVisible(True) if item.latching: self.latching_check.setChecked(True) else: self.latching_check.setChecked(False) if item.annunciating: self.annunciating_check.setChecked(True) else: self.annunciating_check.setChecked(False) def ui_filepath(self): # No UI file is being used return None @Slot(bool) def open_file(self, checked): modifiers = QApplication.keyboardModifiers() try: curr_file = self.current_file() folder = os.path.dirname(curr_file) except Exception: folder = os.getcwd() filename = QFileDialog.getOpenFileName( self, "Open File...", folder, "XML (*.xml);; ALH Config (*.alhConfig)") filename = filename[0] if isinstance(filename, (list, tuple)) else filename if filename: filename = str(filename) # if alh file selected, open conversion prompt if filename[-9:] == "alhConfig": self.legacy_window = LegacyWindow(filename) self.legacy_window.exec_() if self.legacy_window.converted_filename: self.import_configuration( self.legacy_window.converted_filename) else: self.import_configuration(filename) def import_configuration(self, filename): nodes = self.config_tool.parse_config(filename) self.tree_view.model().import_hierarchy(nodes) self.tree_label.setText(self.tree_view.model()._nodes[0].label) @Slot() def save_configuration(self): modifiers = QApplication.keyboardModifiers() try: curr_file = self.current_file() folder = os.path.dirname(curr_file) except Exception: folder = os.getcwd() filename = QFileDialog.getSaveFileName(self, "Save File...", folder, "Configration files (*.xml)") filename = filename[0] if isinstance(filename, (list, tuple)) else filename self.config_tool.save_configuration(self.tree_view.model()._root_item, filename) def _update_config_name(self): name = self.tree_label.text() self.tree_view.model()._nodes[0].label = name def _import_legacy_file(self): convert_alh_to_phoebus()
def insert_row(self, row=-1, title='', sample_runs='', sample_mass_density='N/A', sample_chemical_formula='N/A', packing_fraction='N/A', align_and_focus_args={}, sample_placzek_arguments={}, normalization_placzek_arguments={}): self.table_ui.insertRow(row) self.set_row_height(row, COLUMN_DEFAULT_HEIGHT) _list_ui_to_unlock = [self.table_ui] _dimension_widgets = {'label': None, 'value': 'N/A', 'units': None} _full_dimension_widgets = { 'radius': copy.deepcopy(_dimension_widgets), 'radius2': copy.deepcopy(_dimension_widgets), 'height': copy.deepcopy(_dimension_widgets) } _text_button = {'text': None, 'button': None} _mass_density_options = {'value': "N/A", "selected": False} _mass_density_infos = { 'number_density': copy.deepcopy(_mass_density_options), 'mass_density': copy.deepcopy(_mass_density_options), 'mass': copy.deepcopy(_mass_density_options), 'molecular_mass': np.NaN, 'total_number_of_atoms': np.NaN, } _material_infos = {'mantid_format': None, 'addie_format': None} _mass_density_infos['mass_density']["selected"] = True _master_table_row_ui = { 'active': None, 'title': None, 'sample': { 'runs': None, 'background': { 'runs': None, 'background': None, }, 'material': copy.deepcopy(_text_button), 'material_infos': copy.deepcopy(_material_infos), 'mass_density': copy.deepcopy(_text_button), 'mass_density_infos': copy.deepcopy(_mass_density_infos), 'packing_fraction': None, 'geometry': copy.deepcopy(_full_dimension_widgets), 'shape': None, 'abs_correction': None, 'mult_scat_correction': None, 'inelastic_correction': None, 'placzek_button': None, 'placzek_infos': None, }, 'normalization': { 'runs': None, 'background': { 'runs': None, 'background': None, }, 'material': copy.deepcopy(_text_button), 'material_infos': copy.deepcopy(_material_infos), 'mass_density': copy.deepcopy(_text_button), 'mass_density_infos': copy.deepcopy(_mass_density_infos), 'packing_fraction': None, 'geometry': copy.deepcopy(_full_dimension_widgets), 'shape': None, 'abs_correction': None, 'mult_scat_correction': None, 'inelastic_correction': None, 'placzek_button': None, 'placzek_infos': None, }, 'align_and_focus_args_button': None, 'align_and_focus_args_infos': {}, 'align_and_focus_args_use_global': True, } random_key = self.generate_random_key() self.key = random_key # block main table events self.table_ui.blockSignals(True) # column 0 (active or not checkBox) _layout = QHBoxLayout() _widget = QCheckBox() _widget.setCheckState(QtCore.Qt.Checked) _widget.setEnabled(True) _master_table_row_ui['active'] = _widget _spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) _layout.addItem(_spacer) _layout.addWidget(_widget) _spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) _layout.addItem(_spacer) _layout.addStretch() _new_widget = QWidget() _new_widget.setLayout(_layout) _widget.stateChanged.connect( lambda state=0, key=random_key: self.main_window. master_table_select_state_changed(state, key)) column = 0 self.table_ui.setCellWidget(row, column, _new_widget) # sample column += 1 # column 1 - title _item = QTableWidgetItem(title) _master_table_row_ui['title'] = _item self.table_ui.setItem(row, column, _item) # column 2 - sample runs column += 1 _item = QTableWidgetItem(sample_runs) _master_table_row_ui['sample']['runs'] = _item self.table_ui.setItem(row, column, _item) # column 3 - background runs column += 1 _item = QTableWidgetItem("") _master_table_row_ui['sample']['background']['runs'] = _item self.table_ui.setItem(row, column, _item) # column 4 - background background column += 1 _item = QTableWidgetItem("") _master_table_row_ui['sample']['background']['background'] = _item self.table_ui.setItem(row, column, _item) # column 5 - material (chemical formula) column += 1 clean_sample_chemical_formula = format_chemical_formula_equation( sample_chemical_formula) _material_text = QLineEdit(clean_sample_chemical_formula) _material_text = QLabel(clean_sample_chemical_formula) _material_button = QPushButton("...") _material_button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _material_button.setFixedWidth(CONFIG_BUTTON_WIDTH) _material_button.pressed.connect( lambda key=random_key: self.main_window. master_table_sample_material_button_pressed(key)) _verti_layout = QVBoxLayout() _verti_layout.addWidget(_material_text) _verti_layout.addWidget(_material_button) _material_widget = QWidget() _material_widget.setLayout(_verti_layout) self.table_ui.setCellWidget(row, column, _material_widget) _master_table_row_ui['sample']['material']['text'] = _material_text _master_table_row_ui['sample']['material']['button'] = _material_button # column 6 - mass density column += 1 _mass_text = QLineEdit(sample_mass_density) _mass_text.returnPressed.connect( lambda key=random_key: self.main_window. master_table_sample_mass_density_line_edit_entered(key)) _mass_units = QLabel("g/cc") _top_widget = QWidget() _top_layout = QHBoxLayout() _top_layout.addWidget(_mass_text) _top_layout.addWidget(_mass_units) _top_widget.setLayout(_top_layout) _mass_button = QPushButton("...") _mass_button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _mass_button.setFixedWidth(CONFIG_BUTTON_WIDTH) _mass_button.pressed.connect( lambda key=random_key: self.main_window. master_table_sample_mass_density_button_pressed(key)) _verti_layout = QVBoxLayout() _verti_layout.addWidget(_top_widget) _verti_layout.addWidget(_mass_button) _mass_widget = QWidget() _mass_widget.setLayout(_verti_layout) self.table_ui.setCellWidget(row, column, _mass_widget) _master_table_row_ui['sample']['mass_density']['text'] = _mass_text _master_table_row_ui['sample']['mass_density']['button'] = _mass_button # column 7 - packing fraction column += 1 if packing_fraction == "N/A": packing_fraction = "{}".format(self.main_window.packing_fraction) _item = QTableWidgetItem(packing_fraction) _master_table_row_ui['sample']['packing_fraction'] = _item self.table_ui.setItem(row, column, _item) # column 8 - shape (cylinder or sphere) column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget = QComboBox() _shape_default_index = 0 _widget.currentIndexChanged.connect( lambda index=_shape_default_index, key=random_key: self.main_window .master_table_sample_shape_changed(index, key)) _list_ui_to_unlock.append(_widget) _widget.blockSignals(True) _widget.addItem("Cylinder") _widget.addItem("Sphere") _widget.addItem("Hollow Cylinder") _master_table_row_ui['sample']['shape'] = _widget _layout.addWidget(_widget) _w = QWidget() _w.setLayout(_layout) self.table_ui.setCellWidget(row, column, _w) # column 9 - dimensions column += 1 # layout 1 _grid_layout = QGridLayout() _label1 = QLabel("Radius:") _grid_layout.addWidget(_label1, 1, 0) _value1 = QLabel("N/A") _grid_layout.addWidget(_value1, 1, 1) _dim1 = QLabel("cm") _grid_layout.addWidget(_dim1, 1, 2) _label2 = QLabel("Radius:") _label2.setVisible(False) _grid_layout.addWidget(_label2, 2, 0) _value2 = QLabel("N/A") _value2.setVisible(False) _grid_layout.addWidget(_value2, 2, 1) _dim2 = QLabel("cm") _dim2.setVisible(False) _grid_layout.addWidget(_dim2, 2, 2) _label3 = QLabel("Height:") _grid_layout.addWidget(_label3, 3, 0) _value3 = QLabel("N/A") _grid_layout.addWidget(_value3, 3, 1) _dim3 = QLabel("cm") _grid_layout.addWidget(_dim3, 3, 2) _master_table_row_ui['sample']['geometry']['radius']['value'] = _value1 _master_table_row_ui['sample']['geometry']['radius2'][ 'value'] = _value2 _master_table_row_ui['sample']['geometry']['height']['value'] = _value3 _master_table_row_ui['sample']['geometry']['radius']['label'] = _label1 _master_table_row_ui['sample']['geometry']['radius2'][ 'label'] = _label2 _master_table_row_ui['sample']['geometry']['height']['label'] = _label3 _master_table_row_ui['sample']['geometry']['radius']['units'] = _dim1 _master_table_row_ui['sample']['geometry']['radius2']['units'] = _dim2 _master_table_row_ui['sample']['geometry']['height']['units'] = _dim3 _geometry_widget = QWidget() _geometry_widget.setLayout(_grid_layout) _set_dimensions_button = QPushButton("...") _set_dimensions_button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _set_dimensions_button.setFixedWidth(CONFIG_BUTTON_WIDTH) _verti_layout = QVBoxLayout() _verti_layout.addWidget(_geometry_widget) _verti_layout.addWidget(_set_dimensions_button) _verti_widget = QWidget() _verti_widget.setLayout(_verti_layout) _set_dimensions_button.pressed.connect( lambda key=random_key: self.main_window. master_table_sample_dimensions_setter_button_pressed(key)) self.table_ui.setCellWidget(row, column, _verti_widget) # column 10 - abs. correction column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget = QComboBox() _shape_default_value = 0 list_abs_correction = self.get_absorption_correction_list( shape=_shape_default_value) _widget.currentIndexChanged.connect( lambda value=list_abs_correction[0], key=random_key: self. main_window.master_table_sample_abs_correction_changed(value, key)) _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) for _item in list_abs_correction: _widget.addItem(_item) _master_table_row_ui['sample']['abs_correction'] = _widget _layout.addWidget(_widget) _w = QWidget() _w.setLayout(_layout) self.table_ui.setCellWidget(row, column, _w) # column 11 - multi. scattering correction column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget = QComboBox() list_multi_scat_correction = self.get_multi_scat_correction_list( shape=_shape_default_value) _widget.currentIndexChanged.connect( lambda value=list_multi_scat_correction[ 0], key=random_key: self.main_window. master_table_sample_multi_scattering_correction_changed( value, key)) _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) for _item in list_multi_scat_correction: _widget.addItem(_item) _master_table_row_ui['sample']['mult_scat_correction'] = _widget _layout.addWidget(_widget) _w = QWidget() _w.setLayout(_layout) self.table_ui.setCellWidget(row, column, _w) # column 12 - inelastic correction column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget1 = QComboBox() _widget1.setMinimumHeight(20) list_inelastic_correction = self.get_inelastic_scattering_list( shape=_shape_default_value) for _item in list_inelastic_correction: _widget1.addItem(_item) _master_table_row_ui['sample']['inelastic_correction'] = _widget1 _button = QPushButton("...") _button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _button.setFixedWidth(CONFIG_BUTTON_WIDTH) _button.pressed.connect( lambda key=random_key: self.main_window. master_table_sample_placzek_button_pressed(key)) _master_table_row_ui['sample']['placzek_button'] = _button _button.setVisible(False) _master_table_row_ui['sample']['placzek_button'] = _button _layout.addWidget(_widget1) _layout.addWidget(_button) _widget = QWidget() _widget.setLayout(_layout) _default_value = 'None' _widget1.currentIndexChanged.connect( lambda value=_default_value, key=random_key: self.main_window. master_table_sample_inelastic_correction_changed(value, key)) _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) self.table_ui.setCellWidget(row, column, _widget) # save default placzek settings _sample_formated_placzek_default = self.formated_placzek_default( sample_placzek_arguments) _master_table_row_ui['sample'][ 'placzek_infos'] = _sample_formated_placzek_default ## normalization # column 13 - sample runs column += 1 _item = QTableWidgetItem("") self.table_ui.setItem(row, column, _item) # column 14 - background runs column += 1 _item = QTableWidgetItem("") self.table_ui.setItem(row, column, _item) # column 15 - background background column += 1 _item = QTableWidgetItem("") self.table_ui.setItem(row, column, _item) # column 16 - material (chemical formula) column += 1 #_material_text = QLineEdit("") _material_text = QLabel("N/A") _material_button = QPushButton("...") _material_button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _material_button.setFixedWidth(CONFIG_BUTTON_WIDTH) _material_button.pressed.connect( lambda key=random_key: self.main_window. master_table_normalization_material_button_pressed(key)) _verti_layout = QVBoxLayout() _verti_layout.addWidget(_material_text) _verti_layout.addWidget(_material_button) _material_widget = QWidget() _material_widget.setLayout(_verti_layout) self.table_ui.setCellWidget(row, column, _material_widget) _master_table_row_ui['normalization']['material'][ 'text'] = _material_text _master_table_row_ui['normalization']['material'][ 'button'] = _material_button # column 17 - mass density column += 1 _mass_text = QLineEdit("N/A") _mass_text.returnPressed.connect( lambda key=random_key: self.main_window. master_table_normalization_mass_density_line_edit_entered(key)) _mass_units = QLabel("g/cc") _top_widget = QWidget() _top_layout = QHBoxLayout() _top_layout.addWidget(_mass_text) _top_layout.addWidget(_mass_units) _top_widget.setLayout(_top_layout) _mass_button = QPushButton("...") _mass_button.setFixedWidth(CONFIG_BUTTON_WIDTH) _mass_button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _mass_button.pressed.connect( lambda key=random_key: self.main_window. master_table_normalization_mass_density_button_pressed(key)) _verti_layout = QVBoxLayout() _verti_layout.addWidget(_top_widget) _verti_layout.addWidget(_mass_button) _mass_widget = QWidget() _mass_widget.setLayout(_verti_layout) self.table_ui.setCellWidget(row, column, _mass_widget) _master_table_row_ui['normalization']['mass_density'][ 'text'] = _mass_text _master_table_row_ui['normalization']['mass_density'][ 'button'] = _mass_button # column 18 - packing fraction column += 1 _item = QTableWidgetItem("") self.table_ui.setItem(row, column, _item) # column 19 - shape (cylinder or sphere) column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget = QComboBox() _widget.currentIndexChanged.connect( lambda value=_shape_default_value, key=random_key: self.main_window .master_table_normalization_shape_changed(value, key)) _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) _widget.addItem("Cylinder") _widget.addItem("Sphere") _widget.addItem("Hollow Cylinder") _master_table_row_ui['normalization']['shape'] = _widget _layout.addWidget(_widget) _w = QWidget() _w.setLayout(_layout) self.table_ui.setCellWidget(row, column, _w) # column 20 - dimensions column += 1 # layout 1 _grid_layout = QGridLayout() _label1 = QLabel("Radius:") _grid_layout.addWidget(_label1, 1, 0) _value1 = QLabel("N/A") _grid_layout.addWidget(_value1, 1, 1) _dim1 = QLabel("cm") _grid_layout.addWidget(_dim1, 1, 2) _label2 = QLabel("Radius:") _label2.setVisible(False) _grid_layout.addWidget(_label2, 2, 0) _value2 = QLabel("N/A") _value2.setVisible(False) _grid_layout.addWidget(_value2, 2, 1) _dim2 = QLabel("cm") _dim2.setVisible(False) _grid_layout.addWidget(_dim2, 2, 2) _label3 = QLabel("Height:") _grid_layout.addWidget(_label3, 3, 0) _value3 = QLabel("N/A") _grid_layout.addWidget(_value3, 3, 1) _dim3 = QLabel("cm") _grid_layout.addWidget(_dim3, 3, 2) _master_table_row_ui['normalization']['geometry']['radius'][ 'value'] = _value1 _master_table_row_ui['normalization']['geometry']['radius2'][ 'value'] = _value2 _master_table_row_ui['normalization']['geometry']['height'][ 'value'] = _value3 _master_table_row_ui['normalization']['geometry']['radius'][ 'label'] = _label1 _master_table_row_ui['normalization']['geometry']['radius2'][ 'label'] = _label2 _master_table_row_ui['normalization']['geometry']['height'][ 'label'] = _label3 _master_table_row_ui['normalization']['geometry']['radius'][ 'units'] = _dim1 _master_table_row_ui['normalization']['geometry']['radius2'][ 'units'] = _dim2 _master_table_row_ui['normalization']['geometry']['height'][ 'units'] = _dim3 _geometry_widget = QWidget() _geometry_widget.setLayout(_grid_layout) _set_dimensions_button = QPushButton("...") _set_dimensions_button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _set_dimensions_button.setFixedWidth(CONFIG_BUTTON_WIDTH) _verti_layout = QVBoxLayout() _verti_layout.addWidget(_geometry_widget) _verti_layout.addWidget(_set_dimensions_button) _verti_widget = QWidget() _verti_widget.setLayout(_verti_layout) _set_dimensions_button.pressed.connect( lambda key=random_key: self.main_window. master_table_normalization_dimensions_setter_button_pressed( key)) # noqa self.table_ui.setCellWidget(row, column, _verti_widget) # column 21 - abs. correction column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget = QComboBox() _widget.currentIndexChanged.connect( lambda value=list_abs_correction[0], key=random_key: self. main_window.master_table_normalization_abs_correction_changed( value, key)) # noqa _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) for _item in list_abs_correction: _widget.addItem(_item) _widget.setCurrentIndex(0) _master_table_row_ui['normalization']['abs_correction'] = _widget _layout.addWidget(_widget) _w = QWidget() _w.setLayout(_layout) self.table_ui.setCellWidget(row, column, _w) # column 24 - multi. scattering correction column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget = QComboBox() _widget.currentIndexChanged.connect( lambda value=list_multi_scat_correction[ 0], key=random_key: self.main_window. master_table_normalization_multi_scattering_correction_changed( value, key)) # noqa _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) for _item in list_multi_scat_correction: _widget.addItem(_item) _widget.setCurrentIndex(0) _master_table_row_ui['normalization']['mult_scat_correction'] = _widget _layout.addWidget(_widget) _w = QWidget() _w.setLayout(_layout) self.table_ui.setCellWidget(row, column, _w) # column 22 - inelastic correction column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget1 = QComboBox() _widget1.setMinimumHeight(20) list_inelastic_correction = self.get_inelastic_scattering_list( shape=_shape_default_value) for _item in list_inelastic_correction: _widget1.addItem(_item) _widget1.setCurrentIndex(0) _master_table_row_ui['normalization'][ 'inelastic_correction'] = _widget1 _button = QPushButton("...") _button.setFixedWidth(CONFIG_BUTTON_WIDTH) _button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _button.pressed.connect( lambda key=random_key: self.main_window. master_table_normalization_placzek_button_pressed(key)) _master_table_row_ui['normalization']['placzek_button'] = _button _button.setVisible(False) _layout.addWidget(_widget1) _layout.addWidget(_button) _widget = QWidget() _widget.setLayout(_layout) _default_value = 'None' _widget1.currentIndexChanged.connect( lambda value=_default_value, key=random_key: self.main_window. master_table_normalization_inelastic_correction_changed( value, key)) # noqa _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) self.table_ui.setCellWidget(row, column, _widget) # automatically populate placzek infos with default values _norm_formated_placzek_default = self.formated_placzek_default( normalization_placzek_arguments) _master_table_row_ui['normalization'][ 'placzek_infos'] = _norm_formated_placzek_default # column 23 - key/value pair column += 1 _layout = QHBoxLayout() _spacer_kv1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) _layout.addItem(_spacer_kv1) _button = QPushButton("...") _layout.addWidget(_button) _button.setFixedWidth(CONFIG_BUTTON_WIDTH) _button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _button.pressed.connect(lambda key=random_key: self.main_window. master_table_keyvalue_button_pressed(key)) _new_widget = QWidget() _new_widget.setLayout(_layout) self.table_ui.setCellWidget(row, column, _new_widget) _master_table_row_ui['align_and_focus_args_button'] = _button _spacer_kv2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) _layout.addItem(_spacer_kv2) _layout.addStretch() align_and_focus_args = self.add_global_key_value_to_local_key_value( align_and_focus_args) _master_table_row_ui[ 'align_and_focus_args_infos'] = align_and_focus_args # Recap. self.main_window.master_table_list_ui[ random_key] = _master_table_row_ui self.unlock_signals_ui(list_ui=_list_ui_to_unlock) self.main_window.check_status_of_right_click_buttons()
class DataFrameEditor(QDialog): """ Dialog for displaying and editing DataFrame and related objects. Signals ------- sig_option_changed(str, object): Raised if an option is changed. Arguments are name of option and its new value. """ sig_option_changed = Signal(str, object) def __init__(self, parent=None): QDialog.__init__(self, parent) # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.is_series = False self.layout = None def setup_and_check(self, data, title=''): """ Setup DataFrameEditor: return False if data is not supported, True otherwise """ self.layout = QGridLayout() self.setLayout(self.layout) self.setWindowIcon(ima.icon('arredit')) if title: title = to_text_string(title) + " - %s" % data.__class__.__name__ else: title = _("%s editor") % data.__class__.__name__ if isinstance(data, Series): self.is_series = True data = data.to_frame() self.setWindowTitle(title) self.resize(600, 500) self.dataModel = DataFrameModel(data, parent=self) self.dataTable = DataFrameView(self, self.dataModel) self.layout.addWidget(self.dataTable) self.setLayout(self.layout) self.setMinimumSize(400, 300) # Make the dialog act as a window self.setWindowFlags(Qt.Window) btn_layout = QHBoxLayout() btn = QPushButton(_("Format")) # disable format button for int type btn_layout.addWidget(btn) btn.clicked.connect(self.change_format) btn = QPushButton(_('Resize')) btn_layout.addWidget(btn) btn.clicked.connect(self.resize_to_contents) bgcolor = QCheckBox(_('Background color')) bgcolor.setChecked(self.dataModel.bgcolor_enabled) bgcolor.setEnabled(self.dataModel.bgcolor_enabled) bgcolor.stateChanged.connect(self.change_bgcolor_enable) btn_layout.addWidget(bgcolor) self.bgcolor_global = QCheckBox(_('Column min/max')) self.bgcolor_global.setChecked(self.dataModel.colum_avg_enabled) self.bgcolor_global.setEnabled(not self.is_series and self.dataModel.bgcolor_enabled) self.bgcolor_global.stateChanged.connect(self.dataModel.colum_avg) btn_layout.addWidget(self.bgcolor_global) btn_layout.addStretch() bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) btn_layout.addWidget(bbox) self.layout.addLayout(btn_layout, 2, 0) return True def change_bgcolor_enable(self, state): """ This is implementet so column min/max is only active when bgcolor is """ self.dataModel.bgcolor(state) self.bgcolor_global.setEnabled(not self.is_series and state > 0) def change_format(self): """ Ask user for display format for floats and use it. This function also checks whether the format is valid and emits `sig_option_changed`. """ format, valid = QInputDialog.getText(self, _('Format'), _("Float formatting"), QLineEdit.Normal, self.dataModel.get_format()) if valid: format = str(format) try: format % 1.1 except: msg = _("Format ({}) is incorrect").format(format) QMessageBox.critical(self, _("Error"), msg) return if not format.startswith('%'): msg = _("Format ({}) should start with '%'").format(format) QMessageBox.critical(self, _("Error"), msg) return self.dataModel.set_format(format) self.sig_option_changed.emit('dataframe_format', format) def get_value(self): """Return modified Dataframe -- this is *not* a copy""" # It is import to avoid accessing Qt C++ object as it has probably # already been destroyed, due to the Qt.WA_DeleteOnClose attribute df = self.dataModel.get_data() if self.is_series: return df.iloc[:, 0] else: return df def resize_to_contents(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.dataTable.resizeColumnsToContents() self.dataModel.fetch_more(columns=True) self.dataTable.resizeColumnsToContents() QApplication.restoreOverrideCursor()
class RunICADialog(QDialog): def __init__(self, parent, nchan, methods): super().__init__(parent) self.setWindowTitle("Run ICA") vbox = QVBoxLayout(self) grid = QGridLayout() grid.addWidget(QLabel("Method:"), 0, 0) self.method = QComboBox() self.method.addItems(methods) self.method.setCurrentIndex(0) self.method.currentIndexChanged.connect(self.toggle_options) grid.addWidget(self.method, 0, 1) self.extended_label = QLabel("Extended:") grid.addWidget(self.extended_label, 1, 0) self.extended = QCheckBox() self.extended.setChecked(True) grid.addWidget(self.extended, 1, 1) self.ortho_label = QLabel("Orthogonal:") grid.addWidget(self.ortho_label, 2, 0) self.ortho = QCheckBox() self.ortho.setChecked(False) grid.addWidget(self.ortho, 2, 1) if "Picard" not in methods: self.ortho_label.hide() self.ortho.hide() grid.addWidget(QLabel("Number of components:"), 3, 0) self.n_components = QSpinBox() self.n_components.setRange(0, nchan) self.n_components.setValue(nchan) self.n_components.setAlignment(Qt.AlignRight) grid.addWidget(self.n_components, 3, 1) grid.addWidget(QLabel("Exclude bad segments:"), 4, 0) self.exclude_bad_segments = QCheckBox() self.exclude_bad_segments.setChecked(True) grid.addWidget(self.exclude_bad_segments, 4, 1) vbox.addLayout(grid) buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) vbox.addWidget(buttonbox) buttonbox.accepted.connect(self.accept) buttonbox.rejected.connect(self.reject) vbox.setSizeConstraint(QVBoxLayout.SetFixedSize) self.toggle_options() @Slot() def toggle_options(self): """Toggle extended options.""" if self.method.currentText() == "Picard": # enable extended and ortho self.extended_label.setEnabled(True) self.extended.setEnabled(True) self.ortho_label.setEnabled(True) self.ortho.setEnabled(True) elif self.method.currentText() == "Infomax": # enable extended self.extended_label.setEnabled(True) self.extended.setEnabled(True) self.ortho_label.setEnabled(False) self.ortho.setChecked(False) self.ortho.setEnabled(False) else: self.extended_label.setEnabled(False) self.extended.setChecked(False) self.extended.setEnabled(False) self.ortho_label.setEnabled(False) self.ortho.setChecked(False) self.ortho.setEnabled(False)
class WndGeneralFittingSettings(SecondaryWindow): # Signal that is sent (to main window) to update global state of the program update_global_state = Signal() computations_complete = Signal(object) def __init__(self, *, gpc, gui_vars): super().__init__() # Global processing classes self.gpc = gpc # Global GUI variables (used for control of GUI state) self.gui_vars = gui_vars self._dialog_data = {} self._validator_int = IntValidatorStrict() self._validator_float = DoubleValidatorStrict() # Reference to the main window. The main window will hold # references to all non-modal windows that could be opened # from multiple places in the program. self.ref_main_window = self.gui_vars["ref_main_window"] self.update_global_state.connect( self.ref_main_window.update_widget_state) self.initialize() self._data_changed = False def initialize(self): self.setWindowTitle("General Settings for Fitting Algorithm") self.setMinimumHeight(330) self.setMinimumWidth(500) self.resize(650, 330) self.pb_apply = QPushButton("Apply") self.pb_apply.setEnabled(False) self.pb_apply.clicked.connect(self.pb_apply_clicked) self.pb_cancel = QPushButton("Cancel") self.pb_cancel.setEnabled(False) self.pb_cancel.clicked.connect(self.pb_cancel_clicked) vbox = QVBoxLayout() hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.pb_apply) hbox.addWidget(self.pb_cancel) vbox.addLayout(hbox) hbox = self._setup_table() vbox.addLayout(hbox) vbox.addStretch(1) self.setLayout(vbox) self._set_tooltips() self.update_form_data() def _setup_table(self): vbox_left = QVBoxLayout() # ===== Top-left section of the dialog box ===== self.le_max_iterations = LineEditExtended() self.le_tolerance_stopping = LineEditExtended() self.le_escape_ratio = LineEditExtended() grid = QGridLayout() grid.addWidget(QLabel("Iterations (max):"), 0, 0) grid.addWidget(self.le_max_iterations, 0, 1) grid.addWidget(QLabel("Tolerance (stopping):"), 1, 0) grid.addWidget(self.le_tolerance_stopping, 1, 1) grid.addWidget(QLabel("Escape peak ratio:"), 2, 0) grid.addWidget(self.le_escape_ratio, 2, 1) self.group_total_spectrum_fitting = QGroupBox( "Fitting of Total Spectrum (Model)") self.group_total_spectrum_fitting.setLayout(grid) vbox_left.addWidget(self.group_total_spectrum_fitting) # ===== Bottom-left section of the dialog box ===== # Incident energy and the selected range self.le_incident_energy = LineEditExtended() self.le_range_low = LineEditExtended() self.le_range_high = LineEditExtended() self.group_energy_range = QGroupBox( "Incident Energy and Selected Range") grid = QGridLayout() grid.addWidget(QLabel("Incident energy, keV"), 0, 0) grid.addWidget(self.le_incident_energy, 0, 1) grid.addWidget(QLabel("Range (low), keV"), 1, 0) grid.addWidget(self.le_range_low, 1, 1) grid.addWidget(QLabel("Range (high), keV"), 2, 0) grid.addWidget(self.le_range_high, 2, 1) self.group_energy_range.setLayout(grid) vbox_left.addWidget(self.group_energy_range) vbox_right = QVBoxLayout() # ===== Top-right section of the dialog box ===== self.cb_linear_baseline = QCheckBox("Subtract linear baseline") self.cb_snip_baseline = QCheckBox("Subtract baseline using SNIP") # This option is not supported. In the future it may be removed # if not needed or implemented. self.cb_add_const_to_data = QCheckBox("Add const. bias to data") self.cb_add_const_to_data.setEnabled(False) self.lb_add_const_to_data = QLabel("Constant bias:") self.lb_add_const_to_data.setEnabled(False) self.le_add_const_to_data = LineEditExtended() self.le_add_const_to_data.setEnabled(False) vbox = QVBoxLayout() vbox.addWidget(self.cb_linear_baseline) vbox.addWidget(self.cb_snip_baseline) vbox.addWidget(self.cb_add_const_to_data) hbox = QHBoxLayout() hbox.addWidget(self.lb_add_const_to_data) hbox.addWidget(self.le_add_const_to_data) vbox.addLayout(hbox) self.group_pixel_fitting = QGroupBox( "Fitting of Single Spectra (XRF Maps)") self.group_pixel_fitting.setLayout(vbox) vbox_right.addWidget(self.group_pixel_fitting) # ===== Bottom-right section of the dialog box ===== self.le_snip_window_size = LineEditExtended() vbox = QVBoxLayout() hbox = QHBoxLayout() hbox.addWidget(QLabel("SNIP window size(*):")) hbox.addWidget(self.le_snip_window_size) vbox.addLayout(hbox) vbox.addWidget( QLabel( "*Total spectrum fitting always includes \n SNIP baseline subtraction" )) self.group_all_fitting = QGroupBox("All Fitting") self.group_all_fitting.setLayout(vbox) vbox_right.addWidget(self.group_all_fitting) vbox_right.addStretch(1) hbox = QHBoxLayout() hbox.addLayout(vbox_left) hbox.addLayout(vbox_right) self.le_max_iterations.textChanged.connect( self.le_max_iterations_text_changed) self.le_max_iterations.editingFinished.connect( self.le_max_iterations_editing_finished) self.le_tolerance_stopping.textChanged.connect( self.le_tolerance_stopping_text_changed) self.le_tolerance_stopping.editingFinished.connect( self.le_tolerance_stopping_editing_finished) self.le_escape_ratio.textChanged.connect( self.le_escape_ratio_text_changed) self.le_escape_ratio.editingFinished.connect( self.le_escape_ratio_editing_finished) self.le_incident_energy.textChanged.connect( self.le_incident_energy_text_changed) self.le_incident_energy.editingFinished.connect( self.le_incident_energy_editing_finished) self.le_range_low.textChanged.connect(self.le_range_low_text_changed) self.le_range_low.editingFinished.connect( self.le_range_low_editing_finished) self.le_range_high.textChanged.connect(self.le_range_high_text_changed) self.le_range_high.editingFinished.connect( self.le_range_high_editing_finished) self.le_snip_window_size.textChanged.connect( self.le_snip_window_size_text_changed) self.le_snip_window_size.editingFinished.connect( self.le_snip_window_size_editing_finished) self.cb_linear_baseline.stateChanged.connect( self.cb_linear_baseline_state_changed) self.cb_snip_baseline.stateChanged.connect( self.cb_snip_baseline_state_changed) return hbox def update_widget_state(self, condition=None): # Update the state of the menu bar state = not self.gui_vars["gui_state"]["running_computations"] self.setEnabled(state) if condition == "tooltips": self._set_tooltips() def _set_tooltips(self): set_tooltip(self.pb_apply, "Save changes and <b>update plots</b>.") set_tooltip(self.pb_cancel, "<b>Discard</b> all changes.") set_tooltip( self.le_max_iterations, "<b>Maximum number of iterations</b> used for total spectrum fitting." ) set_tooltip(self.le_tolerance_stopping, "<b>Tolerance</b> setting for total spectrum fitting.") set_tooltip( self.le_escape_ratio, "Parameter for total spectrum fitting: <b>escape ration</b>") set_tooltip(self.le_incident_energy, "<b>Incident energy</b> in keV") set_tooltip(self.le_range_low, "<b>Lower boundary</b> of the selected range in keV.") set_tooltip(self.le_range_high, "<b>Upper boundary</b> of the selected range in keV.") set_tooltip( self.cb_linear_baseline, "Subtract baseline as represented as a constant. <b>XRF Map generation</b>. " "Baseline subtraction is performed as part of NNLS fitting.", ) set_tooltip( self.cb_snip_baseline, "Subtract baseline using SNIP method. <b>XRF Map generation</b>. " "This is a separate step of processing and can be used together with " "'linear' baseline subtraction if needed.", ) set_tooltip( self.le_snip_window_size, "Window size for <b>SNIP</b> algorithm. Used both for total spectrum fitting " "and XRF Map generation. SNIP baseline subtraction is always performed while " "fitting total spectrum, but its effect may be reduced or eliminated by setting " "the window size to some large value.", ) def pb_apply_clicked(self): """Save dialog data and update plots""" self.save_form_data() def pb_cancel_clicked(self): """Reload data (discard all changes)""" self.update_form_data() def le_max_iterations_text_changed(self, text): self._data_changed = True self._validate_all() def le_max_iterations_editing_finished(self): if self._validate_max_interations(): self._dialog_data["max_iterations"] = int( self._read_le_value(self.le_max_iterations)) else: self._show_max_iterations() def le_tolerance_stopping_text_changed(self, text): self._data_changed = True self._validate_all() def le_tolerance_stopping_editing_finished(self): if self._validate_tolerance_stopping(): self._dialog_data["tolerance"] = self._read_le_value( self.le_tolerance_stopping) else: self._show_tolerance_stopping() def le_escape_ratio_text_changed(self, text): self._data_changed = True self._validate_all() def le_escape_ratio_editing_finished(self): if self._validate_escape_ratio(): self._dialog_data["escape_peak_ratio"] = self._read_le_value( self.le_escape_ratio) else: self._show_escape_ratio() def le_incident_energy_text_changed(self, text): self._data_changed = True if self._validate_incident_energy(text): val = float(text) val_range_high = val + 0.8 self._show_range_high(val_range_high) self._validate_all() def le_incident_energy_editing_finished(self): if self._validate_incident_energy(): self._dialog_data["incident_energy"] = self._read_le_value( self.le_incident_energy) else: self._show_incident_energy() if self._validate_range(): self._dialog_data["range_low"] = self._read_le_value( self.le_range_low) self._dialog_data["range_high"] = self._read_le_value( self.le_range_high) else: self._show_range_low() self._show_range_high() self._validate_all() def le_range_low_text_changed(self, text): self._data_changed = True self._validate_all() def le_range_low_editing_finished(self): if self._validate_range(): self._dialog_data["range_low"] = self._read_le_value( self.le_range_low) self._dialog_data["range_high"] = self._read_le_value( self.le_range_high) else: self._show_range_low() self._show_range_high() self._validate_all() def le_range_high_text_changed(self, text): self._data_changed = True self._validate_all() def le_range_high_editing_finished(self): if self._validate_range(): self._dialog_data["range_low"] = self._read_le_value( self.le_range_low) self._dialog_data["range_high"] = self._read_le_value( self.le_range_high) else: self._show_range_low() self._show_range_high() def le_snip_window_size_text_changed(self, text): self._data_changed = True self._validate_all() def le_snip_window_size_editing_finished(self): if self._validate_snip_window_size(): self._dialog_data["snip_window_size"] = self._read_le_value( self.le_snip_window_size) else: self._show_snip_window_size() def cb_linear_baseline_state_changed(self, state): self._dialog_data["subtract_baseline_linear"] = state == Qt.Checked self._data_changed = True self._validate_all() def cb_snip_baseline_state_changed(self, state): self._dialog_data["subtract_baseline_snip"] = state == Qt.Checked self._data_changed = True self._validate_all() def update_form_data(self): self._dialog_data = self.gpc.get_general_fitting_params() self._show_all() self._data_changed = False self._validate_all() def save_form_data(self): if self._data_changed: def cb(dialog_data): try: self.gpc.set_general_fitting_params(dialog_data) success, msg = True, "" except Exception as ex: success, msg = False, str(ex) return {"success": success, "msg": msg} self._compute_in_background(cb, self.slot_save_form_data, dialog_data=self._dialog_data) @Slot(object) def slot_save_form_data(self, result): self._recover_after_compute(self.slot_save_form_data) if not result["success"]: msg = result["msg"] msgbox = QMessageBox(QMessageBox.Critical, "Failed to Apply Fit Parameters", msg, QMessageBox.Ok, parent=self) msgbox.exec() else: self._data_changed = False self._validate_all() self.gui_vars["gui_state"]["state_model_fit_exists"] = False self.update_global_state.emit() def _show_all(self): self._show_max_iterations() self._show_tolerance_stopping() self._show_escape_ratio() self._show_incident_energy() self._show_range_high() self._show_range_low() self._show_linear_baseline() self._show_snip_baseline() self._show_snip_window_size() def _show_max_iterations(self): val = self._dialog_data["max_iterations"] self.le_max_iterations.setText(f"{val}") def _show_tolerance_stopping(self): val = self._dialog_data["tolerance"] self.le_tolerance_stopping.setText(self._format_float(val)) def _show_escape_ratio(self): val = self._dialog_data["escape_peak_ratio"] self.le_escape_ratio.setText(self._format_float(val)) def _show_incident_energy(self): val = self._dialog_data["incident_energy"] self.le_incident_energy.setText(self._format_float(val)) def _show_range_high(self, val=None): val = self._dialog_data["range_high"] if val is None else val self.le_range_high.setText(self._format_float(val)) def _show_range_low(self): val = self._dialog_data["range_low"] self.le_range_low.setText(self._format_float(val)) def _show_linear_baseline(self): val = self._dialog_data["subtract_baseline_linear"] self.cb_linear_baseline.setChecked(Qt.Checked if val else Qt.Unchecked) def _show_snip_baseline(self): val = self._dialog_data["subtract_baseline_snip"] self.cb_snip_baseline.setChecked(Qt.Checked if val else Qt.Unchecked) def _show_snip_window_size(self): val = self._dialog_data["snip_window_size"] self.le_snip_window_size.setText(self._format_float(val)) def _validate_all(self): valid = (self._validate_max_interations() and self._validate_tolerance_stopping() and self._validate_escape_ratio() and self._validate_incident_energy() and self._validate_range() and self._validate_snip_window_size()) self.pb_apply.setEnabled(valid and self._data_changed) self.pb_cancel.setEnabled(valid and self._data_changed) def _validate_max_interations(self, text=None): if text is None: text = self.le_max_iterations.text() valid = self._validate_int(text, v_min=1) self.le_max_iterations.setValid(valid) return valid def _validate_tolerance_stopping(self, text=None): if text is None: text = self.le_tolerance_stopping.text() valid = self._validate_float(text, v_min=1e-30) self.le_tolerance_stopping.setValid(valid) return valid def _validate_escape_ratio(self, text=None): if text is None: text = self.le_escape_ratio.text() valid = self._validate_float(text, v_min=0) self.le_escape_ratio.setValid(valid) return valid def _validate_incident_energy(self, text=None): if text is None: text = self.le_incident_energy.text() valid = self._validate_float(text, v_min=0) self.le_incident_energy.setValid(valid) return valid def _validate_range(self, low_text=None, high_text=None): if low_text is None: low_text = self.le_range_low.text() if high_text is None: high_text = self.le_range_high.text() valid = False if self._validate_float(low_text, v_min=0) and self._validate_float( high_text, v_min=0): v_low = float(low_text) v_high = float(high_text) if v_low < v_high: valid = True self.le_range_high.setValid(valid) self.le_range_low.setValid(valid) return valid def _validate_snip_window_size(self, text=None): if text is None: text = self.le_snip_window_size.text() valid = self._validate_float(text, v_min=1e-30) self.le_snip_window_size.setValid(valid) return valid def _validate_int(self, text, *, v_min=None, v_max=None): valid = False if self._validator_int.validate(text, 0)[0] == IntValidatorStrict.Acceptable: valid = True v_int = int(text) if (v_min is not None) and (v_int < v_min): valid = False if (v_max is not None) and (v_int > v_max): valid = False return valid def _validate_float(self, text, *, v_min=None, v_max=None): valid = False if self._validator_float.validate( text, 0)[0] == DoubleValidatorStrict.Acceptable: valid = True v_float = float(text) if (v_min is not None) and (v_float < v_min): valid = False if (v_max is not None) and (v_float > v_max): valid = False return valid def _format_float(self, val): return f"{val:.10g}" def _read_le_value(self, line_edit): """It is assumed that the value is validated before the function is called""" return float(line_edit.text()) def _compute_in_background(self, func, slot, *args, **kwargs): """ Run function `func` in a background thread. Send the signal `self.computations_complete` once computation is finished. Parameters ---------- func: function Reference to a function that is supposed to be executed at the background. The function return value is passed as a signal parameter once computation is complete. slot: qtpy.QtCore.Slot or None Reference to a slot. If not None, then the signal `self.computation_complete` is connected to this slot. args, kwargs arguments of the function `func`. """ signal_complete = self.computations_complete def func_to_run(func, *args, **kwargs): class RunTask(QRunnable): def run(self): result_dict = func(*args, **kwargs) signal_complete.emit(result_dict) return RunTask() if slot is not None: self.computations_complete.connect(slot) self.gui_vars["gui_state"]["running_computations"] = True self.update_global_state.emit() QThreadPool.globalInstance().start(func_to_run(func, *args, **kwargs)) def _recover_after_compute(self, slot): """ The function should be called after the signal `self.computations_complete` is received. The slot should be the same as the one used when calling `self.compute_in_background`. """ if slot is not None: self.computations_complete.disconnect(slot) self.gui_vars["gui_state"]["running_computations"] = False self.update_global_state.emit()
class Options(QWidget): def __init__( self, settings: PartSettings, channel_control2: ChannelProperty, left_image: ResultImageView, synchronize: SynchronizeView, ): super().__init__() self._settings = settings self.left_panel = left_image self._ch_control2 = channel_control2 self.synchronize_val = False self.hide_left_panel_chk = QCheckBox("Hide left panel") self.hide_left_panel_chk.stateChanged.connect(self.hide_left_panel) self.synchronize_checkbox = QCheckBox("Synchronize view") self.synchronize_checkbox.stateChanged.connect( synchronize.set_synchronize) self.interactive_use = QCheckBox("Interactive use") self.execute_btn = QPushButton("Execute") self.execute_btn.clicked.connect(self.execute_algorithm) self.execute_btn.setStyleSheet("QPushButton{font-weight: bold;}") self.save_pipe_btn = QPushButton("Save pipeline") self.save_pipe_btn.clicked.connect(self.save_pipeline) self.save_pipe_btn.setToolTip( "Save current pipeline. Last element is last executed algorithm") self.choose_pipe = SearchComboBox() self.choose_pipe.addItem("<none>") # self.choose_pipe.addItems(list(self._settings.roi_pipelines.keys())) self.choose_pipe.textActivated.connect(self.choose_pipeline) self.choose_pipe.setToolTip("Execute chosen pipeline") self.save_profile_btn = QPushButton("Save profile") self.save_profile_btn.setToolTip("Save values from current view") self.save_profile_btn.clicked.connect(self.save_profile) self.choose_profile = SearchComboBox() self.choose_profile.addItem("<none>") # self.choose_profile.addItems(list(self._settings.roi_profiles.keys())) self.choose_profile.setToolTip( "Select profile to restore its settings. Execute if interactive is checked" ) # image state self.compare_btn = QPushButton("Compare") self.compare_btn.setDisabled(True) self.compare_btn.clicked.connect(self.compare_action) left_image.hide_signal.connect(self.compare_btn.setHidden) self.update_tooltips() self.choose_profile.textActivated.connect(self.change_profile) self.interactive_use.stateChanged.connect(self.execute_btn.setDisabled) self.interactive_use.stateChanged.connect(self.interactive_change) self.algorithm_choose_widget = AlgorithmChoose( settings, algorithm_description.analysis_algorithm_dict) self.algorithm_choose_widget.result.connect(self.execution_done) self.algorithm_choose_widget.finished.connect( self.calculation_finished) self.algorithm_choose_widget.value_changed.connect( self.interactive_algorithm_execute) self.algorithm_choose_widget.algorithm_changed.connect( self.interactive_algorithm_execute) self._settings.roi_profiles_changed.connect(self._update_profiles) self._settings.roi_pipelines_changed.connect(self._update_pipelines) self._update_pipelines() self._update_profiles() self.label = TextShow() layout = QVBoxLayout() layout2 = QHBoxLayout() layout2.setSpacing(1) layout2.setContentsMargins(0, 0, 0, 0) layout3 = QHBoxLayout() layout3.setContentsMargins(0, 0, 0, 0) layout.setContentsMargins(0, 0, 0, 0) layout5 = QHBoxLayout() layout5.setContentsMargins(0, 0, 0, 0) layout5.addWidget(self.save_pipe_btn) layout5.addWidget(self.choose_pipe) layout4 = QHBoxLayout() layout4.setContentsMargins(0, 0, 0, 0) layout4.addWidget(self.save_profile_btn) layout4.addWidget(self.choose_profile) layout3.addWidget(self.interactive_use) layout3.addWidget(self.execute_btn) layout.addLayout(layout5) layout.addLayout(layout4) layout.addLayout(layout3) layout.addWidget(self.algorithm_choose_widget, 1) # layout.addLayout(self.stack_layout) layout.addWidget(self.label) # layout.addStretch(1) layout2.addWidget(self.hide_left_panel_chk) layout2.addWidget(self.synchronize_checkbox) layout.addLayout(layout2) layout.addWidget(self._ch_control2) # layout.setSpacing(0) self.setLayout(layout) @ensure_main_thread def _update_profiles(self): self.update_combo_box(self.choose_profile, self._settings.roi_profiles) self.update_tooltips() @ensure_main_thread def _update_pipelines(self): self.update_combo_box(self.choose_pipe, self._settings.roi_pipelines) self.update_tooltips() def compare_action(self): if self.compare_btn.text() == "Compare": self._settings.set_segmentation_to_compare(self._settings.roi_info) self.compare_btn.setText("Remove") else: self._settings.set_segmentation_to_compare(ROIInfo(None)) self.compare_btn.setText("Compare") def calculation_finished(self): self.execute_btn.setDisabled(self.interactive_use.isChecked()) self.interactive_use.setEnabled(True) def save_pipeline(self): history = self._settings.get_history() if not history: QMessageBox.information(self, "No mask created", "There is no new mask created", QMessageBox.Ok) return mask_history = [] for el in history: mask = el.mask_property segmentation = ROIExtractionProfile( name="Unknown", algorithm=el.roi_extraction_parameters["algorithm_name"], values=el.roi_extraction_parameters["values"], ) new_el = SegmentationPipelineElement(mask_property=mask, segmentation=segmentation) mask_history.append(new_el) name = self._settings.last_executed_algorithm if not name: QMessageBox.information(self, "No segmentation", "No segmentation executed", QMessageBox.Ok) return values = self._settings.get(f"algorithms.{name}", {}) if len(values) == 0: QMessageBox.information(self, "Some problem", "Pleas run execution again", QMessageBox.Ok) return current_segmentation = ROIExtractionProfile(name="Unknown", algorithm=name, values=values) while True: text, ok = QInputDialog.getText(self, "Pipeline name", "Input pipeline name here") if not ok: return if text in self._settings.roi_pipelines and QMessageBox.No == QMessageBox.warning( self, "Already exists", "Profile with this name already exist. Overwrite?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No, ): continue profile = SegmentationPipeline(name=text, segmentation=current_segmentation, mask_history=mask_history) self._settings.roi_pipelines[text] = profile self._settings.dump() self.choose_pipe.addItem(text) break def choose_pipeline(self, text): if text == "<none>": return pipeline = self._settings.roi_pipelines[text] process_thread = CalculatePipelineThread(self._settings.image, self._settings.mask, pipeline) dial = WaitingDialog(process_thread) if dial.exec_() and process_thread.result: pipeline_result = process_thread.result self._settings.mask = pipeline_result.mask self._settings.roi = pipeline_result.roi_info.roi self._settings.set_history(pipeline_result.history) self.label.setText(pipeline_result.description) self.algorithm_choose_widget.change_algorithm( pipeline.segmentation.algorithm, pipeline.segmentation.values) self.choose_pipe.setCurrentIndex(0) def update_tooltips(self): for i in range(1, self.choose_profile.count()): if self.choose_profile.itemData(i, Qt.ToolTipRole) is not None: continue text = self.choose_profile.itemText(i) profile: ROIExtractionProfile = self._settings.roi_profiles[text] tool_tip_text = str(profile) self.choose_profile.setItemData(i, tool_tip_text, Qt.ToolTipRole) for i in range(1, self.choose_pipe.count()): if self.choose_pipe.itemData(i, Qt.ToolTipRole) is not None: continue text = self.choose_pipe.itemText(i) profile: SegmentationPipeline = self._settings.roi_pipelines[text] tool_tip_text = str(profile) self.choose_pipe.setItemData(i, tool_tip_text, Qt.ToolTipRole) @staticmethod def update_combo_box(combo_box: QComboBox, dkt: dict): current_names = set(dkt.keys()) prev_names = { combo_box.itemText(i) for i in range(1, combo_box.count()) } new_names = current_names - prev_names delete_names = prev_names - current_names if len(delete_names) > 0: i = 1 while i < combo_box.count(): if combo_box.itemText(i) in delete_names: combo_box.removeItem(i) else: i += 1 if len(new_names) > 0: combo_box.addItems(list(sorted(new_names))) def keyPressEvent(self, event: QKeyEvent): if event.key() in [Qt.Key_Enter, Qt.Key_Return ] and event.modifiers() == Qt.ControlModifier: self.execute_btn.click() def save_profile(self): widget: InteractiveAlgorithmSettingsWidget = self.algorithm_choose_widget.current_widget( ) while True: text, ok = QInputDialog.getText(self, "Profile Name", "Input profile name here") if not ok: return if text in self._settings.roi_profiles and QMessageBox.No == QMessageBox.warning( self, "Already exists", "Profile with this name already exist. Overwrite?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No, ): continue resp = ROIExtractionProfile(text, widget.name, widget.get_values()) self._settings.roi_profiles[text] = resp self._settings.dump() break def change_profile(self, val): self.choose_profile.setToolTip("") if val == "<none>": return interactive = self.interactive_use.isChecked() self.interactive_use.setChecked(False) profile = self._settings.roi_profiles[val] self.algorithm_choose_widget.change_algorithm(profile.algorithm, profile.values) self.choose_profile.blockSignals(True) self.choose_profile.setCurrentIndex(0) self.choose_profile.blockSignals(False) self.interactive_use.setChecked(interactive) @property def segmentation(self): return self._settings.roi @property def interactive(self): return self.interactive_use.isChecked() def hide_left_panel(self, val): self._settings.set_in_profile("hide_left_panel", val) if val: self.synchronize_val = self.synchronize_checkbox.isChecked() self.synchronize_checkbox.setChecked(False) else: self.synchronize_checkbox.setChecked(self.synchronize_val) self.synchronize_checkbox.setDisabled(val) self.left_panel.parent().setHidden(val) def interactive_change(self, val): if val: self.execute_algorithm() def algorithm_change(self, val): self._settings.set("current_algorithm", val) if self.interactive: self.execute_algorithm() def interactive_algorithm_execute(self): if self.interactive: self.execute_algorithm() def execute_algorithm(self): widget: InteractiveAlgorithmSettingsWidget = self.algorithm_choose_widget.current_widget( ) if self._settings.image.is_time and not widget.algorithm.support_time( ): QMessageBox.information( self, "Not supported", "This algorithm do not support time data. You can convert it in image adjust" ) return if self._settings.image.is_stack and not widget.algorithm.support_z(): QMessageBox.information( self, "Not supported", "This algorithm do not support stack data. You can convert it in image adjust" ) return self._settings.last_executed_algorithm = widget.name self.execute_btn.setDisabled(True) self.interactive_use.setDisabled(True) widget.execute() def execution_done(self, segmentation: ROIExtractionResult): if segmentation.info_text != "": QMessageBox.information(self, "Algorithm info", segmentation.info_text) self._settings.set_segmentation_result(segmentation) self.compare_btn.setEnabled( isinstance(segmentation.roi, np.ndarray) and np.any(segmentation.roi)) self.label.setText(self.sender().get_info_text()) def showEvent(self, _event): self.hide_left_panel_chk.setChecked( self._settings.get_from_profile("hide_left_panel", False))
class DataFrameEditor(QDialog): """ Dialog for displaying and editing DataFrame and related objects. Signals ------- sig_option_changed(str, object): Raised if an option is changed. Arguments are name of option and its new value. """ sig_option_changed = Signal(str, object) def __init__(self, parent=None): QDialog.__init__(self, parent) # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.is_series = False self.layout = None def setup_and_check(self, data, title=''): """ Setup DataFrameEditor: return False if data is not supported, True otherwise. Supported types for data are DataFrame, Series and Index. """ self.layout = QGridLayout() self.setLayout(self.layout) self.setWindowIcon(ima.icon('arredit')) if title: title = to_text_string(title) + " - %s" % data.__class__.__name__ else: title = _("%s editor") % data.__class__.__name__ if isinstance(data, Series): self.is_series = True data = data.to_frame() elif isinstance(data, Index): data = DataFrame(data) self.setWindowTitle(title) self.resize(600, 500) self.dataModel = DataFrameModel(data, parent=self) self.dataTable = DataFrameView(self, self.dataModel) self.layout.addWidget(self.dataTable) self.setLayout(self.layout) self.setMinimumSize(400, 300) # Make the dialog act as a window self.setWindowFlags(Qt.Window) btn_layout = QHBoxLayout() btn = QPushButton(_("Format")) # disable format button for int type btn_layout.addWidget(btn) btn.clicked.connect(self.change_format) btn = QPushButton(_('Resize')) btn_layout.addWidget(btn) btn.clicked.connect(self.resize_to_contents) bgcolor = QCheckBox(_('Background color')) bgcolor.setChecked(self.dataModel.bgcolor_enabled) bgcolor.setEnabled(self.dataModel.bgcolor_enabled) bgcolor.stateChanged.connect(self.change_bgcolor_enable) btn_layout.addWidget(bgcolor) self.bgcolor_global = QCheckBox(_('Column min/max')) self.bgcolor_global.setChecked(self.dataModel.colum_avg_enabled) self.bgcolor_global.setEnabled(not self.is_series and self.dataModel.bgcolor_enabled) self.bgcolor_global.stateChanged.connect(self.dataModel.colum_avg) btn_layout.addWidget(self.bgcolor_global) btn_layout.addStretch() bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) btn_layout.addWidget(bbox) self.layout.addLayout(btn_layout, 2, 0) return True def change_bgcolor_enable(self, state): """ This is implementet so column min/max is only active when bgcolor is """ self.dataModel.bgcolor(state) self.bgcolor_global.setEnabled(not self.is_series and state > 0) def change_format(self): """ Ask user for display format for floats and use it. This function also checks whether the format is valid and emits `sig_option_changed`. """ format, valid = QInputDialog.getText(self, _('Format'), _("Float formatting"), QLineEdit.Normal, self.dataModel.get_format()) if valid: format = str(format) try: format % 1.1 except: msg = _("Format ({}) is incorrect").format(format) QMessageBox.critical(self, _("Error"), msg) return if not format.startswith('%'): msg = _("Format ({}) should start with '%'").format(format) QMessageBox.critical(self, _("Error"), msg) return self.dataModel.set_format(format) self.sig_option_changed.emit('dataframe_format', format) def get_value(self): """Return modified Dataframe -- this is *not* a copy""" # It is import to avoid accessing Qt C++ object as it has probably # already been destroyed, due to the Qt.WA_DeleteOnClose attribute df = self.dataModel.get_data() if self.is_series: return df.iloc[:, 0] else: return df def resize_to_contents(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.dataTable.resizeColumnsToContents() self.dataModel.fetch_more(columns=True) self.dataTable.resizeColumnsToContents() QApplication.restoreOverrideCursor()
def insert_row(self, row=-1, title='', sample_runs='', sample_mass_density='N/A', sample_chemical_formula='N/A', packing_fraction='N/A', align_and_focus_args={}, sample_placzek_arguments={}, normalization_placzek_arguments={}): self.table_ui.insertRow(row) self.set_row_height(row, COLUMN_DEFAULT_HEIGHT) _list_ui_to_unlock = [self.table_ui] _dimension_widgets = {'label': None, 'value': 'N/A', 'units': None} _full_dimension_widgets = {'radius': copy.deepcopy(_dimension_widgets), 'radius2': copy.deepcopy(_dimension_widgets), 'height': copy.deepcopy(_dimension_widgets)} _text_button = {'text': None, 'button': None} _mass_density_options = {'value': "N/A", "selected": False} _mass_density_infos = {'number_density': copy.deepcopy(_mass_density_options), 'mass_density': copy.deepcopy(_mass_density_options), 'mass': copy.deepcopy(_mass_density_options), 'molecular_mass': np.NaN, 'total_number_of_atoms': np.NaN, } _material_infos = {'mantid_format': None, 'addie_format': None} _mass_density_infos['mass_density']["selected"] = True _master_table_row_ui = {'active': None, 'title': None, 'sample': {'runs': None, 'background': {'runs': None, 'background': None, }, 'material': copy.deepcopy(_text_button), 'material_infos': copy.deepcopy(_material_infos), 'mass_density': copy.deepcopy(_text_button), 'mass_density_infos': copy.deepcopy(_mass_density_infos), 'packing_fraction': None, 'geometry': copy.deepcopy(_full_dimension_widgets), 'shape': None, 'abs_correction': None, 'mult_scat_correction': None, 'inelastic_correction': None, 'placzek_button': None, 'placzek_infos': None, }, 'normalization': {'runs': None, 'background': {'runs': None, 'background': None, }, 'material': copy.deepcopy(_text_button), 'material_infos': copy.deepcopy(_material_infos), 'mass_density': copy.deepcopy(_text_button), 'mass_density_infos': copy.deepcopy(_mass_density_infos), 'packing_fraction': None, 'geometry': copy.deepcopy(_full_dimension_widgets), 'shape': None, 'abs_correction': None, 'mult_scat_correction': None, 'inelastic_correction': None, 'placzek_button': None, 'placzek_infos': None, }, 'align_and_focus_args_button': None, 'align_and_focus_args_infos': {}, } random_key = self.generate_random_key() self.key = random_key # block main table events self.table_ui.blockSignals(True) # column 0 (active or not checkBox) _layout = QHBoxLayout() _widget = QCheckBox() _widget.setCheckState(QtCore.Qt.Checked) _widget.setEnabled(True) _master_table_row_ui['active'] = _widget _spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) _layout.addItem(_spacer) _layout.addWidget(_widget) _spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) _layout.addItem(_spacer) _layout.addStretch() _new_widget = QWidget() _new_widget.setLayout(_layout) _widget.stateChanged.connect(lambda state=0, key=random_key: self.main_window.master_table_select_state_changed(state, key)) column = 0 self.table_ui.setCellWidget(row, column, _new_widget) # sample column += 1 # column 1 - title _item = QTableWidgetItem(title) _master_table_row_ui['title'] = _item self.table_ui.setItem(row, column, _item) # column 2 - sample runs column += 1 _item = QTableWidgetItem(sample_runs) _master_table_row_ui['sample']['runs'] = _item self.table_ui.setItem(row, column, _item) # column 3 - background runs column += 1 _item = QTableWidgetItem("") _master_table_row_ui['sample']['background']['runs'] = _item self.table_ui.setItem(row, column, _item) # column 4 - background background column += 1 _item = QTableWidgetItem("") _master_table_row_ui['sample']['background']['background'] = _item self.table_ui.setItem(row, column, _item) # column 5 - material (chemical formula) column += 1 clean_sample_chemical_formula = format_chemical_formula_equation(sample_chemical_formula) _material_text = QLineEdit(clean_sample_chemical_formula) _material_text = QLabel(clean_sample_chemical_formula) _material_button = QPushButton("...") _material_button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _material_button.setFixedWidth(CONFIG_BUTTON_WIDTH) _material_button.pressed.connect(lambda key=random_key: self.main_window.master_table_sample_material_button_pressed(key)) _verti_layout = QVBoxLayout() _verti_layout.addWidget(_material_text) _verti_layout.addWidget(_material_button) _material_widget = QWidget() _material_widget.setLayout(_verti_layout) self.table_ui.setCellWidget(row, column, _material_widget) _master_table_row_ui['sample']['material']['text'] = _material_text _master_table_row_ui['sample']['material']['button'] = _material_button # column 6 - mass density column += 1 _mass_text = QLineEdit(sample_mass_density) _mass_text.returnPressed.connect(lambda key=random_key: self.main_window.master_table_sample_mass_density_line_edit_entered(key)) _mass_units = QLabel("g/cc") _top_widget = QWidget() _top_layout = QHBoxLayout() _top_layout.addWidget(_mass_text) _top_layout.addWidget(_mass_units) _top_widget.setLayout(_top_layout) _mass_button = QPushButton("...") _mass_button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _mass_button.setFixedWidth(CONFIG_BUTTON_WIDTH) _mass_button.pressed.connect(lambda key=random_key: self.main_window.master_table_sample_mass_density_button_pressed(key)) _verti_layout = QVBoxLayout() _verti_layout.addWidget(_top_widget) _verti_layout.addWidget(_mass_button) _mass_widget = QWidget() _mass_widget.setLayout(_verti_layout) self.table_ui.setCellWidget(row, column, _mass_widget) _master_table_row_ui['sample']['mass_density']['text'] = _mass_text _master_table_row_ui['sample']['mass_density']['button'] = _mass_button # column 7 - packing fraction column += 1 if packing_fraction == "N/A": packing_fraction = "{}".format(self.main_window.packing_fraction) _item = QTableWidgetItem(packing_fraction) _master_table_row_ui['sample']['packing_fraction'] = _item self.table_ui.setItem(row, column, _item) # column 8 - shape (cylindrical or spherical) column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget = QComboBox() _shape_default_index = 0 _widget.currentIndexChanged.connect(lambda index=_shape_default_index, key=random_key: self.main_window.master_table_sample_shape_changed(index, key)) _list_ui_to_unlock.append(_widget) _widget.blockSignals(True) _widget.addItem("cylindrical") _widget.addItem("spherical") _widget.addItem("hollow cylinder") _master_table_row_ui['sample']['shape'] = _widget _layout.addWidget(_widget) _w = QWidget() _w.setLayout(_layout) self.table_ui.setCellWidget(row, column, _w) # column 9 - dimensions column += 1 # layout 1 _grid_layout = QGridLayout() _label1 = QLabel("Radius:") _grid_layout.addWidget(_label1, 1, 0) _value1 = QLabel("N/A") _grid_layout.addWidget(_value1, 1, 1) _dim1 = QLabel("cm") _grid_layout.addWidget(_dim1, 1, 2) _label2 = QLabel("Radius:") _label2.setVisible(False) _grid_layout.addWidget(_label2, 2, 0) _value2 = QLabel("N/A") _value2.setVisible(False) _grid_layout.addWidget(_value2, 2, 1) _dim2 = QLabel("cm") _dim2.setVisible(False) _grid_layout.addWidget(_dim2, 2, 2) _label3 = QLabel("Height:") _grid_layout.addWidget(_label3, 3, 0) _value3 = QLabel("N/A") _grid_layout.addWidget(_value3, 3, 1) _dim3 = QLabel("cm") _grid_layout.addWidget(_dim3, 3, 2) _master_table_row_ui['sample']['geometry']['radius']['value'] = _value1 _master_table_row_ui['sample']['geometry']['radius2']['value'] = _value2 _master_table_row_ui['sample']['geometry']['height']['value'] = _value3 _master_table_row_ui['sample']['geometry']['radius']['label'] = _label1 _master_table_row_ui['sample']['geometry']['radius2']['label'] = _label2 _master_table_row_ui['sample']['geometry']['height']['label'] = _label3 _master_table_row_ui['sample']['geometry']['radius']['units'] = _dim1 _master_table_row_ui['sample']['geometry']['radius2']['units'] = _dim2 _master_table_row_ui['sample']['geometry']['height']['units'] = _dim3 _geometry_widget = QWidget() _geometry_widget.setLayout(_grid_layout) _set_dimensions_button = QPushButton("...") _set_dimensions_button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _set_dimensions_button.setFixedWidth(CONFIG_BUTTON_WIDTH) _verti_layout = QVBoxLayout() _verti_layout.addWidget(_geometry_widget) _verti_layout.addWidget(_set_dimensions_button) _verti_widget = QWidget() _verti_widget.setLayout(_verti_layout) _set_dimensions_button.pressed.connect(lambda key=random_key: self.main_window.master_table_sample_dimensions_setter_button_pressed(key)) self.table_ui.setCellWidget(row, column, _verti_widget) # column 10 - abs. correction column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget = QComboBox() _shape_default_value = 0 list_abs_correction = self.get_absorption_correction_list(shape=_shape_default_value) _widget.currentIndexChanged.connect(lambda value=list_abs_correction[0], key = random_key: self.main_window.master_table_sample_abs_correction_changed(value, key)) _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) for _item in list_abs_correction: _widget.addItem(_item) _master_table_row_ui['sample']['abs_correction'] = _widget _layout.addWidget(_widget) _w = QWidget() _w.setLayout(_layout) self.table_ui.setCellWidget(row, column, _w) # column 11 - multi. scattering correction column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget = QComboBox() list_multi_scat_correction = self.get_multi_scat_correction_list(shape=_shape_default_value) _widget.currentIndexChanged.connect(lambda value=list_multi_scat_correction[0], key=random_key: self.main_window.master_table_sample_multi_scattering_correction_changed(value, key)) _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) for _item in list_multi_scat_correction: _widget.addItem(_item) _master_table_row_ui['sample']['mult_scat_correction'] = _widget _layout.addWidget(_widget) _w = QWidget() _w.setLayout(_layout) self.table_ui.setCellWidget(row, column, _w) # column 12 - inelastic correction column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget1 = QComboBox() _widget1.setMinimumHeight(20) list_inelastic_correction = self.get_inelastic_scattering_list(shape=_shape_default_value) for _item in list_inelastic_correction: _widget1.addItem(_item) _master_table_row_ui['sample']['inelastic_correction'] = _widget1 _button = QPushButton("...") _button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _button.setFixedWidth(CONFIG_BUTTON_WIDTH) _button.pressed.connect(lambda key=random_key: self.main_window.master_table_sample_placzek_button_pressed(key)) _master_table_row_ui['sample']['placzek_button'] = _button _button.setVisible(False) _master_table_row_ui['sample']['placzek_button'] = _button _layout.addWidget(_widget1) _layout.addWidget(_button) _widget = QWidget() _widget.setLayout(_layout) _default_value = 'None' _widget1.currentIndexChanged.connect(lambda value=_default_value, key=random_key: self.main_window.master_table_sample_inelastic_correction_changed(value, key)) _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) self.table_ui.setCellWidget(row, column, _widget) # save default placzek settings _sample_formated_placzek_default = self.formated_placzek_default(sample_placzek_arguments) _master_table_row_ui['sample']['placzek_infos'] = _sample_formated_placzek_default ## normalization # column 13 - sample runs column += 1 _item = QTableWidgetItem("") self.table_ui.setItem(row, column, _item) # column 14 - background runs column += 1 _item = QTableWidgetItem("") self.table_ui.setItem(row, column, _item) # column 15 - background background column += 1 _item = QTableWidgetItem("") self.table_ui.setItem(row, column, _item) # column 16 - material (chemical formula) column += 1 #_material_text = QLineEdit("") _material_text = QLabel("N/A") _material_button = QPushButton("...") _material_button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _material_button.setFixedWidth(CONFIG_BUTTON_WIDTH) _material_button.pressed.connect(lambda key=random_key: self.main_window.master_table_normalization_material_button_pressed(key)) _verti_layout = QVBoxLayout() _verti_layout.addWidget(_material_text) _verti_layout.addWidget(_material_button) _material_widget = QWidget() _material_widget.setLayout(_verti_layout) self.table_ui.setCellWidget(row, column, _material_widget) _master_table_row_ui['normalization']['material']['text'] = _material_text _master_table_row_ui['normalization']['material']['button'] = _material_button # column 17 - mass density column += 1 _mass_text = QLineEdit("N/A") _mass_text.returnPressed.connect(lambda key=random_key: self.main_window.master_table_normalization_mass_density_line_edit_entered(key)) _mass_units = QLabel("g/cc") _top_widget = QWidget() _top_layout = QHBoxLayout() _top_layout.addWidget(_mass_text) _top_layout.addWidget(_mass_units) _top_widget.setLayout(_top_layout) _mass_button = QPushButton("...") _mass_button.setFixedWidth(CONFIG_BUTTON_WIDTH) _mass_button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _mass_button.pressed.connect(lambda key=random_key: self.main_window.master_table_normalization_mass_density_button_pressed(key)) _verti_layout = QVBoxLayout() _verti_layout.addWidget(_top_widget) _verti_layout.addWidget(_mass_button) _mass_widget = QWidget() _mass_widget.setLayout(_verti_layout) self.table_ui.setCellWidget(row, column, _mass_widget) _master_table_row_ui['normalization']['mass_density']['text'] = _mass_text _master_table_row_ui['normalization']['mass_density']['button'] = _mass_button # column 18 - packing fraction column += 1 _item = QTableWidgetItem("") self.table_ui.setItem(row, column, _item) # column 19 - shape (cylindrical or spherical) column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget = QComboBox() _widget.currentIndexChanged.connect(lambda value=_shape_default_value, key=random_key: self.main_window.master_table_normalization_shape_changed(value, key)) _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) _widget.addItem("cylindrical") _widget.addItem("spherical") _widget.addItem("hollow cylinder") _master_table_row_ui['normalization']['shape'] = _widget _layout.addWidget(_widget) _w = QWidget() _w.setLayout(_layout) self.table_ui.setCellWidget(row, column, _w) # column 20 - dimensions column += 1 # layout 1 _grid_layout = QGridLayout() _label1 = QLabel("Radius:") _grid_layout.addWidget(_label1, 1, 0) _value1 = QLabel("N/A") _grid_layout.addWidget(_value1, 1, 1) _dim1 = QLabel("cm") _grid_layout.addWidget(_dim1, 1, 2) _label2 = QLabel("Radius:") _label2.setVisible(False) _grid_layout.addWidget(_label2, 2, 0) _value2 = QLabel("N/A") _value2.setVisible(False) _grid_layout.addWidget(_value2, 2, 1) _dim2 = QLabel("cm") _dim2.setVisible(False) _grid_layout.addWidget(_dim2, 2, 2) _label3 = QLabel("Height:") _grid_layout.addWidget(_label3, 3, 0) _value3 = QLabel("N/A") _grid_layout.addWidget(_value3, 3, 1) _dim3 = QLabel("cm") _grid_layout.addWidget(_dim3, 3, 2) _master_table_row_ui['normalization']['geometry']['radius']['value'] = _value1 _master_table_row_ui['normalization']['geometry']['radius2']['value'] = _value2 _master_table_row_ui['normalization']['geometry']['height']['value'] = _value3 _master_table_row_ui['normalization']['geometry']['radius']['label'] = _label1 _master_table_row_ui['normalization']['geometry']['radius2']['label'] = _label2 _master_table_row_ui['normalization']['geometry']['height']['label'] = _label3 _master_table_row_ui['normalization']['geometry']['radius']['units'] = _dim1 _master_table_row_ui['normalization']['geometry']['radius2']['units'] = _dim2 _master_table_row_ui['normalization']['geometry']['height']['units'] = _dim3 _geometry_widget = QWidget() _geometry_widget.setLayout(_grid_layout) _set_dimensions_button = QPushButton("...") _set_dimensions_button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _set_dimensions_button.setFixedWidth(CONFIG_BUTTON_WIDTH) _verti_layout = QVBoxLayout() _verti_layout.addWidget(_geometry_widget) _verti_layout.addWidget(_set_dimensions_button) _verti_widget = QWidget() _verti_widget.setLayout(_verti_layout) _set_dimensions_button.pressed.connect(lambda key=random_key: self.main_window.master_table_normalization_dimensions_setter_button_pressed(key)) # noqa self.table_ui.setCellWidget(row, column, _verti_widget) # column 21 - abs. correction column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget = QComboBox() _widget.currentIndexChanged.connect(lambda value=list_abs_correction[0], key=random_key: self.main_window.master_table_normalization_abs_correction_changed(value, key)) # noqa _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) for _item in list_abs_correction: _widget.addItem(_item) _widget.setCurrentIndex(0) _master_table_row_ui['normalization']['abs_correction'] = _widget _layout.addWidget(_widget) _w = QWidget() _w.setLayout(_layout) self.table_ui.setCellWidget(row, column, _w) # column 24 - multi. scattering correction column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget = QComboBox() _widget.currentIndexChanged.connect(lambda value=list_multi_scat_correction[0], key=random_key: self.main_window.master_table_normalization_multi_scattering_correction_changed(value, key)) # noqa _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) for _item in list_multi_scat_correction: _widget.addItem(_item) _widget.setCurrentIndex(0) _master_table_row_ui['normalization']['mult_scat_correction'] = _widget _layout.addWidget(_widget) _w = QWidget() _w.setLayout(_layout) self.table_ui.setCellWidget(row, column, _w) # column 22 - inelastic correction column += 1 _layout = QHBoxLayout() _layout.setContentsMargins(0, 0, 0, 0) _widget1 = QComboBox() _widget1.setMinimumHeight(20) list_inelastic_correction = self.get_inelastic_scattering_list(shape=_shape_default_value) for _item in list_inelastic_correction: _widget1.addItem(_item) _widget1.setCurrentIndex(0) _master_table_row_ui['normalization']['inelastic_correction'] = _widget1 _button = QPushButton("...") _button.setFixedWidth(CONFIG_BUTTON_WIDTH) _button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _button.pressed.connect(lambda key=random_key: self.main_window.master_table_normalization_placzek_button_pressed(key)) _master_table_row_ui['normalization']['placzek_button'] = _button _button.setVisible(False) _layout.addWidget(_widget1) _layout.addWidget(_button) _widget = QWidget() _widget.setLayout(_layout) _default_value = 'None' _widget1.currentIndexChanged.connect( lambda value=_default_value, key=random_key: self.main_window.master_table_normalization_inelastic_correction_changed(value, key)) # noqa _widget.blockSignals(True) _list_ui_to_unlock.append(_widget) self.table_ui.setCellWidget(row, column, _widget) # automatically populate placzek infos with default values _norm_formated_placzek_default = self.formated_placzek_default(normalization_placzek_arguments) _master_table_row_ui['normalization']['placzek_infos'] = _norm_formated_placzek_default # column 23 - key/value pair column += 1 _layout = QHBoxLayout() _spacer_kv1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) _layout.addItem(_spacer_kv1) _button = QPushButton("...") _layout.addWidget(_button) _button.setFixedWidth(CONFIG_BUTTON_WIDTH) _button.setFixedHeight(CONFIG_BUTTON_HEIGHT) _button.pressed.connect(lambda key=random_key: self.main_window.master_table_keyvalue_button_pressed(key)) _new_widget = QWidget() _new_widget.setLayout(_layout) self.table_ui.setCellWidget(row, column, _new_widget) _master_table_row_ui['align_and_focus_args_button'] = _button _spacer_kv2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) _layout.addItem(_spacer_kv2) _layout.addStretch() _master_table_row_ui['align_and_focus_args_infos'] = align_and_focus_args ## recap self.main_window.master_table_list_ui[random_key] = _master_table_row_ui self.unlock_signals_ui(list_ui=_list_ui_to_unlock) self.main_window.check_status_of_right_click_buttons()
class FeaturesPlotWidget(CustomWidget): call_manage_settings = Signal() def __init__(self, *args, **kwargs): # define status images self.status_icons = { -2: QPixmap( os.path.join(os.path.dirname(__file__), 'icons', 'depth_status_delay.png')), -1: QPixmap( os.path.join(os.path.dirname(__file__), 'icons', 'depth_status_in_use.png')), 1: QPixmap( os.path.join(os.path.dirname(__file__), 'icons', 'depth_status_done.png')), 0: QPixmap( os.path.join(os.path.dirname(__file__), 'icons', 'depth_status_off.png')), } # Settings self.subject_settings = None self.procedure_settings = None self.depth_settings = None self.features_settings = None # Plot options self.plot_config = {} self.y_range = uVRANGE self.plot_stack = QStackedWidget() # generate a dict {chan_label: {Feature:[stack idx, latest_datum]}} self.stack_dict = {} # shared memory to display the currently monitored electrode self.monitored_channel_mem = QSharedMemory() self.monitored_channel_mem.setKey("MonitoredChannelMemory") self.monitored_channel_mem.attach(QSharedMemory.ReadOnly) # wrap up init super(FeaturesPlotWidget, self).__init__(*args, **kwargs) self.move(WINDOWDIMS_FEATURES[0], WINDOWDIMS_FEATURES[1]) self.resize(WINDOWDIMS_FEATURES[2], WINDOWDIMS_FEATURES[3]) self.setMaximumWidth(WINDOWDIMS_FEATURES[2]) # initialize plots self.layout().addWidget(self.plot_stack) self.refresh_axes() # Extra time on purpose. # Define and start processes # will only start processes when settings are received self.depth_wrapper = ProcessWrapper('Depth_Process') self.depth_process_running = False self.features_wrapper = ProcessWrapper('Features_Process') self.features_process_running = False def create_control_panel(self): # define Qt GUI elements layout = QHBoxLayout() layout_L = QVBoxLayout() layout_L1 = QHBoxLayout() # layout_L1.addSpacing(10) layout_L1.addWidget( QLabel("Electrode: ", alignment=Qt.AlignVCenter | Qt.AlignRight)) # Channel selection self.chan_select = QComboBox() self.chan_select.addItem("None") self.chan_select.setMinimumWidth(70) self.chan_select.setEnabled(False) layout_L1.addWidget(self.chan_select) layout_L1.addSpacing(20) # features selection layout_L1.addWidget( QLabel("Feature set: ", alignment=Qt.AlignVCenter | Qt.AlignRight)) self.feature_select = QComboBox() self.feature_select.setMinimumWidth(60) self.feature_select.addItems(['Raw', 'Mapping']) self.feature_select.setCurrentIndex(0) layout_L1.addWidget(self.feature_select) layout_L.addLayout(layout_L1) layout_L.addSpacing(5) layout_L2 = QHBoxLayout() layout_L2.addSpacing(10) layout_L2.addWidget( QLabel("+/- ", alignment=Qt.AlignVCenter | Qt.AlignRight)) self.range_edit = QLineEdit("{:.2f}".format(uVRANGE)) self.range_edit.setMaximumWidth(50) layout_L2.addWidget(self.range_edit) layout_L2.addSpacing(30) self.do_hp = QCheckBox('HP') self.do_hp.setChecked(True) layout_L2.addWidget(self.do_hp) layout_L2.addSpacing(30) self.sweep_control = QCheckBox("Match SweepGUI.") self.sweep_control.setChecked(True) self.sweep_control.setEnabled(True) layout_L2.addWidget(self.sweep_control) layout_L.addLayout(layout_L2) layout_R = QHBoxLayout() self.bt_refresh = QPushButton("Refresh") self.bt_refresh.setMaximumWidth(50) layout_R.addWidget(self.bt_refresh) layout_R.addSpacing(20) self.btn_settings = QPushButton("Settings") self.btn_settings.setMaximumWidth(50) layout_R.addWidget(self.btn_settings) layout_R.addSpacing(20) self.features_process_btn = QPushButton('Features') self.features_process_btn.setMaximumWidth(50) self.features_process_btn.setStyleSheet("QPushButton { color: white; " "background-color : red; " "border-color : red; " "border-width: 2px}") self.features_process_btn.clicked.connect( self.features_process_btn_callback) layout_R.addWidget(self.features_process_btn) layout_R.addSpacing(5) self.depth_process_btn = QPushButton('Record') self.depth_process_btn.setMaximumWidth(50) self.depth_process_btn.setStyleSheet("QPushButton { color: white; " "background-color : red; " "border-color : red; " "border-width: 2px}") self.depth_process_btn.clicked.connect(self.depth_process_btn_callback) layout_R.addWidget(self.depth_process_btn) layout_R.addSpacing(20) self.status_label = QLabel() self.status_label.setPixmap(self.status_icons[0]) layout_R.addWidget(self.status_label) layout_R.addSpacing(10) layout.addLayout(layout_L) layout.addStretch() layout.addLayout(layout_R) # layout.addSpacing(10) self.layout().addLayout(layout) # callbacks self.btn_settings.clicked.connect(self.call_manage_settings.emit) self.chan_select.currentIndexChanged.connect( self.manage_feat_chan_select) self.feature_select.currentIndexChanged.connect( self.manage_feat_chan_select) self.sweep_control.clicked.connect(self.manage_sweep_control) self.range_edit.editingFinished.connect(self.manage_range_edit) self.bt_refresh.clicked.connect(self.manage_refresh) def depth_process_btn_callback(self): # kill if self.depth_process_running: self.manage_depth_process(False) self.manage_nsp(False) else: # if we terminate and re-start the processes, we need to re-enable the shared memory self.depth_wrapper.manage_shared_memory() # re-send the settings self.depth_wrapper.send_settings(self.depth_settings) # start nsp recording if self.manage_nsp(True) == 0: # re-start the worker self.manage_depth_process(True) def features_process_btn_callback(self): # kill if self.features_process_running: self.manage_feature_process(False) else: # if we terminate and re-start the processes, we need to re-enable the shared memory self.features_wrapper.manage_shared_memory() # re-send the settings self.features_wrapper.send_settings(self.features_settings) # re-start the worker self.manage_feature_process(True) # GUI Callbacks def manage_feat_chan_select(self): self.plot_stack.setCurrentIndex( self.stack_dict[self.chan_select.currentText()][ self.feature_select.currentText()][0]) def manage_sweep_control(self): if self.sweep_control.isChecked( ) and self.monitored_channel_mem.isAttached(): self.chan_select.setEnabled(False) self.do_hp.setEnabled(False) self.range_edit.setEnabled(False) self.read_from_shared_memory() else: self.chan_select.setEnabled(True) self.do_hp.setEnabled(True) self.range_edit.setEnabled(True) def manage_range_edit(self): # need to do it like this because if we simply read the QLineEdit.text() on update calls, it breaks during # typing the new range values. self.y_range = float(self.range_edit.text()) @staticmethod def parse_patient_name(full_name): # parse the subject information names = full_name.split(' ') m_name = '' m_idx = -1 l_idx = -1 for idx, n in enumerate(names): if all([x.isupper() for x in n]): m_idx = idx l_idx = idx + 1 m_name = n break f_name = str.join(' ', names[:m_idx]) l_name = str.join(' ', names[l_idx:]) return f_name, m_name, l_name def manage_nsp(self, on_off): f_name, m_name, l_name = self.parse_patient_name( self.subject_settings['name']) file_info = { 'filename': os.path.normpath( os.path.join( BASEPATH, self.subject_settings['id'], self.procedure_settings['date'].strftime('%m%d%y') + '_' + self.subject_settings['id'] + '_' + self.procedure_settings['target_name'] + '_' + self.procedure_settings['recording_config'])), 'comment': self.subject_settings['NSP_comment'], 'patient_info': { 'ID': self.subject_settings['id'], # if only single name, returned in l_name 'firstname': f_name if f_name else l_name, 'middlename': m_name, # TODO: implement MiddleName 'lastname': l_name, 'DOBMonth': self.subject_settings['birthday'].month, 'DOBDay': self.subject_settings['birthday'].day, 'DOBYear': self.subject_settings['birthday'].year } } if not CbSdkConnection().is_connected: CbSdkConnection().connect() return CbSdkConnection().set_recording_state(on_off, file_info) def manage_depth_process(self, on_off): # start process if on_off and not self.depth_process_running: self.depth_wrapper.start_worker() self.depth_process_running = True else: self.depth_wrapper.kill_worker() self.depth_process_running = False def manage_feature_process(self, on_off): if on_off and not self.features_process_running: self.features_wrapper.start_worker() self.features_process_running = True else: self.features_wrapper.kill_worker() self.features_process_running = False def manage_refresh(self): self.plot_stack.widget(self.stack_dict[self.chan_select.currentText()][ self.feature_select.currentText()][0]).clear_plot() self.stack_dict[self.chan_select.currentText()][ self.feature_select.currentText()][1] = 0 def process_settings(self, sub_sett, proc_sett, depth_sett, feat_sett): self.subject_settings = dict(sub_sett) self.procedure_settings = dict(proc_sett) self.depth_settings = dict(depth_sett) self.features_settings = dict(feat_sett) # validate that we have some data in the electrode_settings. If the NSP is not connected we will have # to load the channel names from the DB. Also we want to keep the FeaturesGUI unaware of the DB channels. if len(self.depth_settings['electrode_settings']) == 0: self.depth_settings['electrode_settings'] = {} for lbl in DBWrapper().list_channel_labels(): self.depth_settings['electrode_settings'][lbl] = DEPTHSETTINGS CbSdkConnection().is_simulating = True # set new features self.feature_select.setCurrentIndex(0) # Raw while self.feature_select.count() > 2: # Raw and Mapping self.feature_select.removeItem(2) self.feature_select.addItems(self.features_settings['features'].keys()) # set new channels self.chan_select.setCurrentIndex(0) # None while self.chan_select.count() > 1: self.chan_select.removeItem(1) self.chan_select.addItems( self.depth_settings['electrode_settings'].keys()) # clear and update stacked widget to_delete = [ self.plot_stack.widget(x) for x in range(self.plot_stack.count()) ] for wid in to_delete: self.plot_stack.removeWidget(wid) wid.deleteLater() wid = None self.stack_dict = {} self.create_plots() self.depth_wrapper.send_settings(self.depth_settings) self.features_wrapper.send_settings(self.features_settings) if not self.features_process_running: self.manage_feature_process(True) # self.clear() self.read_from_shared_memory() def create_plots(self, theme='dark', **kwargs): # Collect PlotWidget configuration self.plot_config['theme'] = theme self.plot_config['color_iterator'] = -1 self.plot_config['x_range'] = XRANGE_FEATURES self.plot_config['y_range'] = uVRANGE self.plot_config['do_hp'] = True labels = [] for ii in range(0, self.chan_select.count()): labels.append(self.chan_select.itemText(ii)) # labels.extend(self.channel_labels) features = [] for ii in range(0, self.feature_select.count()): features.append(self.feature_select.itemText(ii)) stack_idx = 0 for lbl_idx, lbl in enumerate(labels): self.stack_dict[lbl] = {} self.plot_config['color_iterator'] = lbl_idx - 1 self.plot_config['title'] = lbl for feat in features: self.stack_dict[lbl][feat] = [stack_idx, 0] # TODO: not hard-coding?? if feat == 'Raw': self.plot_stack.addWidget(RawPlots(dict(self.plot_config))) elif feat == 'Mapping': self.plot_stack.addWidget( MappingPlots(dict(self.plot_config))) elif feat == 'STN': self.plot_stack.addWidget(STNPlots(dict(self.plot_config))) elif feat == 'LFP': self.plot_stack.addWidget(LFPPlots(dict(self.plot_config))) elif feat == 'Spikes': self.plot_stack.addWidget( SpikePlots(dict(self.plot_config))) else: self.plot_stack.addWidget( NullPlotWidget(dict(self.plot_config))) stack_idx += 1 self.plot_stack.setCurrentIndex(0) def refresh_axes(self): pass def clear(self): # set the current datum of all stacks to 0 for lbl in self.stack_dict: for feat in self.stack_dict[lbl]: self.stack_dict[lbl][feat][1] = 0 self.plot_stack.widget( self.stack_dict[lbl][feat][0]).clear_plot() def update(self): # Depth process output = self.depth_wrapper.worker_status() self.status_label.setPixmap(self.status_icons[output]) if self.depth_wrapper.is_running(): self.depth_process_btn.setStyleSheet("QPushButton { color: white; " "background-color : green; " "border-color : green; " "border-width: 2px}") else: self.depth_process_running = False self.depth_process_btn.setStyleSheet("QPushButton { color: white; " "background-color : red; " "border-color : red; " "border-width: 2px}") if self.features_wrapper.is_running(): self.features_process_btn.setStyleSheet( "QPushButton { color: white; " "background-color : green; " "border-color : green; " "border-width: 2px}") else: self.features_process_running = False self.features_process_btn.setStyleSheet( "QPushButton { color: white; " "background-color : red; " "border-color : red; " "border-width: 2px}") if self.sweep_control.isChecked(): self.read_from_shared_memory() # features plot curr_chan_lbl = self.chan_select.currentText() if curr_chan_lbl != 'None': curr_feat = self.feature_select.currentText() do_hp = self.do_hp.isChecked() if do_hp != self.plot_stack.currentWidget().plot_config['do_hp'] or \ self.y_range != self.plot_stack.currentWidget().plot_config['y_range']: self.plot_stack.currentWidget().clear_plot() self.stack_dict[curr_chan_lbl][curr_feat][1] = 0 self.plot_stack.currentWidget().plot_config['do_hp'] = do_hp self.plot_stack.currentWidget( ).plot_config['y_range'] = self.y_range curr_datum = self.stack_dict[curr_chan_lbl][curr_feat][1] if curr_feat == 'Raw': all_data = DBWrapper().load_depth_data(chan_lbl=curr_chan_lbl, gt=curr_datum, do_hp=do_hp, return_uV=True) elif curr_feat == 'Mapping': all_data = DBWrapper().load_mapping_response( chan_lbl=curr_chan_lbl, gt=curr_datum) else: all_data = DBWrapper().load_features_data( category=curr_feat, chan_lbl=curr_chan_lbl, gt=curr_datum) if all_data: self.plot_stack.currentWidget().update_plot(dict(all_data)) self.stack_dict[curr_chan_lbl][curr_feat][1] = max( all_data.keys()) def kill_processes(self): self.manage_depth_process(False) self.manage_feature_process(False) def read_from_shared_memory(self): if self.monitored_channel_mem.isAttached(): self.monitored_channel_mem.lock() settings = np.frombuffer(self.monitored_channel_mem.data(), dtype=np.float)[-3:] self.chan_select.setCurrentIndex(int(settings[0])) self.range_edit.setText(str(settings[1])) self.manage_range_edit() self.do_hp.setChecked(bool(settings[2])) self.monitored_channel_mem.unlock() else: self.monitored_channel_mem.attach() # self.sweep_control.setChecked(False) self.manage_sweep_control()
class MxArrayViewer(QWidget): # mx change """Array Editor Dialog""" def __init__(self, parent=None): super().__init__(parent) # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.data = None self.arraywidget = None self.stack = None self.layout = None self.btn_save_and_close = None self.btn_close = None # Values for 3d array editor self.dim_indexes = [{}, {}, {}] self.last_dim = 0 # Adjust this for changing the startup dimension def setup_and_check(self, data, title='', readonly=False, xlabels=None, ylabels=None): """ Setup ArrayEditor: return False if data is not supported, True otherwise """ self.data = data readonly = readonly or not self.data.flags.writeable is_record_array = data.dtype.names is not None is_masked_array = isinstance(data, np.ma.MaskedArray) if data.ndim > 3: self.error( _("Arrays with more than 3 dimensions are not " "supported")) return False if xlabels is not None and len(xlabels) != self.data.shape[1]: self.error( _("The 'xlabels' argument length do no match array " "column number")) return False if ylabels is not None and len(ylabels) != self.data.shape[0]: self.error( _("The 'ylabels' argument length do no match array row " "number")) return False if not is_record_array: dtn = data.dtype.name if dtn == 'object': # If the array doesn't have shape, we can't display it if data.shape == (): self.error( _("Object arrays without shape are not " "supported")) return False # We don't know what's inside these arrays, so we can't handle # edits self.readonly = readonly = True elif (dtn not in SUPPORTED_FORMATS and not dtn.startswith('str') and not dtn.startswith('unicode')): arr = _("%s arrays") % data.dtype.name self.error(_("%s are currently not supported") % arr) return False self.layout = QGridLayout() self.setLayout(self.layout) if title: title = to_text_string(title) + " - " + _("NumPy object array") else: title = _("Array editor") if readonly: title += ' (' + _('read only') + ')' self.setWindowTitle(title) # ---- Stack widget self.stack = QStackedWidget(self) if is_record_array: for name in data.dtype.names: self.stack.addWidget( ArrayEditorWidget(self, data[name], readonly, xlabels, ylabels)) elif is_masked_array: self.stack.addWidget( ArrayEditorWidget(self, data, readonly, xlabels, ylabels)) self.stack.addWidget( ArrayEditorWidget(self, data.data, readonly, xlabels, ylabels)) self.stack.addWidget( ArrayEditorWidget(self, data.mask, readonly, xlabels, ylabels)) elif data.ndim == 3: # We create here the necessary widgets for current_dim_changed to # work. The rest are created below. # QSpinBox self.index_spin = QSpinBox(self, keyboardTracking=False) self.index_spin.valueChanged.connect(self.change_active_widget) # Labels self.shape_label = QLabel() self.slicing_label = QLabel() # Set the widget to display when launched self.current_dim_changed(self.last_dim) else: self.stack.addWidget( ArrayEditorWidget(self, data, readonly, xlabels, ylabels)) self.arraywidget = self.stack.currentWidget() self.arraywidget.model.dataChanged.connect(self.save_and_close_enable) self.stack.currentChanged.connect(self.current_widget_changed) self.layout.addWidget(self.stack, 1, 0) # ---- Top row of buttons btn_layout_top = None if is_record_array or is_masked_array or data.ndim == 3: btn_layout_top = QHBoxLayout() if is_record_array: btn_layout_top.addWidget(QLabel(_("Record array fields:"))) names = [] for name in data.dtype.names: field = data.dtype.fields[name] text = name if len(field) >= 3: title = field[2] if not is_text_string(title): title = repr(title) text += ' - ' + title names.append(text) else: names = [_('Masked data'), _('Data'), _('Mask')] if data.ndim == 3: # QComboBox names = [str(i) for i in range(3)] ra_combo = QComboBox(self) ra_combo.addItems(names) ra_combo.currentIndexChanged.connect(self.current_dim_changed) # Adding the widgets to layout label = QLabel(_("Axis:")) btn_layout_top.addWidget(label) btn_layout_top.addWidget(ra_combo) btn_layout_top.addWidget(self.shape_label) label = QLabel(_("Index:")) btn_layout_top.addWidget(label) btn_layout_top.addWidget(self.index_spin) btn_layout_top.addWidget(self.slicing_label) else: ra_combo = QComboBox(self) ra_combo.currentIndexChanged.connect( self.stack.setCurrentIndex) ra_combo.addItems(names) btn_layout_top.addWidget(ra_combo) if is_masked_array: label = QLabel( _("<u>Warning</u>: Changes are applied separately")) label.setToolTip( _("For performance reasons, changes applied " "to masked arrays won't be reflected in " "array's data (and vice-versa).")) btn_layout_top.addWidget(label) btn_layout_top.addStretch() # ---- Bottom row of buttons btn_layout_bottom = QHBoxLayout() btn_format = QPushButton(_("Format")) # disable format button for int type btn_format.setEnabled(is_float(self.arraywidget.data.dtype)) btn_layout_bottom.addWidget(btn_format) btn_format.clicked.connect(lambda: self.arraywidget.change_format()) btn_resize = QPushButton(_("Resize")) btn_layout_bottom.addWidget(btn_resize) btn_resize.clicked.connect( lambda: self.arraywidget.view.resize_to_contents()) self.bgcolor = QCheckBox(_('Background color')) self.bgcolor.setEnabled(self.arraywidget.model.bgcolor_enabled) self.bgcolor.setChecked(self.arraywidget.model.bgcolor_enabled) self.bgcolor.stateChanged.connect( lambda state: self.arraywidget.model.bgcolor(state)) btn_layout_bottom.addWidget(self.bgcolor) btn_layout_bottom.addStretch() if not readonly: self.btn_save_and_close = QPushButton(_('Save and Close')) self.btn_save_and_close.setDisabled(True) self.btn_save_and_close.clicked.connect(self.accept) btn_layout_bottom.addWidget(self.btn_save_and_close) # mx change # self.btn_close = QPushButton(_('Close')) # self.btn_close.setAutoDefault(True) # self.btn_close.setDefault(True) # self.btn_close.clicked.connect(self.reject) # btn_layout_bottom.addWidget(self.btn_close) # ---- Final layout btn_layout_bottom.setContentsMargins(4, 4, 4, 4) if btn_layout_top is not None: btn_layout_top.setContentsMargins(4, 4, 4, 4) self.layout.addLayout(btn_layout_top, 2, 0) self.layout.addLayout(btn_layout_bottom, 3, 0) else: self.layout.addLayout(btn_layout_bottom, 2, 0) # Set minimum size self.setMinimumSize(500, 300) # Make the dialog act as a window # self.setWindowFlags(Qt.Window) # mx change return True @Slot(QModelIndex, QModelIndex) def save_and_close_enable(self, left_top, bottom_right): """Handle the data change event to enable the save and close button.""" if self.btn_save_and_close: self.btn_save_and_close.setEnabled(True) self.btn_save_and_close.setAutoDefault(True) self.btn_save_and_close.setDefault(True) def current_widget_changed(self, index): self.arraywidget = self.stack.widget(index) self.arraywidget.model.dataChanged.connect(self.save_and_close_enable) self.bgcolor.setChecked(self.arraywidget.model.bgcolor_enabled) def change_active_widget(self, index): """ This is implemented for handling negative values in index for 3d arrays, to give the same behavior as slicing """ string_index = [':'] * 3 string_index[self.last_dim] = '<font color=red>%i</font>' self.slicing_label.setText( (r"Slicing: [" + ", ".join(string_index) + "]") % index) if index < 0: data_index = self.data.shape[self.last_dim] + index else: data_index = index slice_index = [slice(None)] * 3 slice_index[self.last_dim] = data_index stack_index = self.dim_indexes[self.last_dim].get(data_index) if stack_index is None: stack_index = self.stack.count() try: self.stack.addWidget( ArrayEditorWidget(self, self.data[tuple(slice_index)])) except IndexError: # Handle arrays of size 0 in one axis self.stack.addWidget(ArrayEditorWidget(self, self.data)) self.dim_indexes[self.last_dim][data_index] = stack_index self.stack.update() self.stack.setCurrentIndex(stack_index) def current_dim_changed(self, index): """ This change the active axis the array editor is plotting over in 3D """ self.last_dim = index string_size = ['%i'] * 3 string_size[index] = '<font color=red>%i</font>' self.shape_label.setText( ('Shape: (' + ', '.join(string_size) + ') ') % self.data.shape) if self.index_spin.value() != 0: self.index_spin.setValue(0) else: # this is done since if the value is currently 0 it does not emit # currentIndexChanged(int) self.change_active_widget(0) self.index_spin.setRange(-self.data.shape[index], self.data.shape[index] - 1) @Slot() def accept(self): """Reimplement Qt method.""" try: for index in range(self.stack.count()): self.stack.widget(index).accept_changes() QDialog.accept(self) except RuntimeError: # Sometimes under CI testing the object the following error appears # RuntimeError: wrapped C/C++ object has been deleted pass def get_value(self): """Return modified array -- this is *not* a copy""" # It is important to avoid accessing Qt C++ object as it has probably # already been destroyed, due to the Qt.WA_DeleteOnClose attribute return self.data def error(self, message): """An error occurred, closing the dialog box""" QMessageBox.critical(self, _("Array editor"), message) self.setAttribute(Qt.WA_DeleteOnClose) self.reject() @Slot() def reject(self): """Reimplement Qt method""" if self.arraywidget is not None: for index in range(self.stack.count()): self.stack.widget(index).reject_changes() QDialog.reject(self)
class MCSDialog(QDialog): """A dialog to perform minimal cut set computation""" def __init__(self, appdata: CnaData, centralwidget): QDialog.__init__(self) self.setWindowTitle("Minimal Cut Sets Computation") self.appdata = appdata self.centralwidget = centralwidget self.eng = appdata.engine self.out = io.StringIO() self.err = io.StringIO() self.layout = QVBoxLayout() l1 = QLabel("Target Region(s)") self.layout.addWidget(l1) s1 = QHBoxLayout() completer = QCompleter( self.appdata.project.cobra_py_model.reactions.list_attr("id"), self) completer.setCaseSensitivity(Qt.CaseInsensitive) self.target_list = QTableWidget(1, 4) self.target_list.setHorizontalHeaderLabels( ["region no", "T", "≥/≤", "t"]) self.target_list.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.target_list.horizontalHeader().setSectionResizeMode(0, QHeaderView.Fixed) self.target_list.horizontalHeader().resizeSection(0, 100) self.target_list.horizontalHeader().setSectionResizeMode(2, QHeaderView.Fixed) self.target_list.horizontalHeader().resizeSection(2, 50) item = QLineEdit("1") self.target_list.setCellWidget(0, 0, item) item2 = QLineEdit("") item2.setCompleter(completer) self.target_list.setCellWidget(0, 1, item2) combo = QComboBox(self.target_list) combo.insertItem(1, "≤") combo.insertItem(2, "≥") self.target_list.setCellWidget(0, 2, combo) item = QLineEdit("0") self.target_list.setCellWidget(0, 3, item) s1.addWidget(self.target_list) s11 = QVBoxLayout() self.add_target = QPushButton("+") self.add_target.clicked.connect(self.add_target_region) self.rem_target = QPushButton("-") self.rem_target.clicked.connect(self.rem_target_region) s11.addWidget(self.add_target) s11.addWidget(self.rem_target) s1.addItem(s11) self.layout.addItem(s1) l2 = QLabel("Desired Region(s)") self.layout.addWidget(l2) s2 = QHBoxLayout() self.desired_list = QTableWidget(1, 4) self.desired_list.setHorizontalHeaderLabels( ["region no", "D", "≥/≤", "d"]) self.desired_list.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.desired_list.horizontalHeader().setSectionResizeMode(0, QHeaderView.Fixed) self.desired_list.horizontalHeader().resizeSection(0, 100) self.desired_list.horizontalHeader().setSectionResizeMode(2, QHeaderView.Fixed) self.desired_list.horizontalHeader().resizeSection(2, 50) item = QLineEdit("1") self.desired_list.setCellWidget(0, 0, item) item2 = QLineEdit("") item2.setCompleter(completer) self.desired_list.setCellWidget(0, 1, item2) combo = QComboBox(self.desired_list) combo.insertItem(1, "≤") combo.insertItem(2, "≥") self.desired_list.setCellWidget(0, 2, combo) item = QLineEdit("0") self.desired_list.setCellWidget(0, 3, item) s2.addWidget(self.desired_list) s21 = QVBoxLayout() self.add_desire = QPushButton("+") self.add_desire.clicked.connect(self.add_desired_region) self.rem_desire = QPushButton("-") self.rem_desire.clicked.connect(self.rem_desired_region) s21.addWidget(self.add_desire) s21.addWidget(self.rem_desire) s2.addItem(s21) self.layout.addItem(s2) s3 = QHBoxLayout() sgx = QVBoxLayout() self.gen_kos = QCheckBox("Gene KOs") self.exclude_boundary = QCheckBox( "Exclude boundary\nreactions as cuts") sg1 = QHBoxLayout() s31 = QVBoxLayout() l = QLabel("Max. Solutions") s31.addWidget(l) l = QLabel("Max. Size") s31.addWidget(l) l = QLabel("Time Limit [sec]") s31.addWidget(l) sg1.addItem(s31) s32 = QVBoxLayout() self.max_solu = QLineEdit("inf") self.max_solu.setMaximumWidth(50) s32.addWidget(self.max_solu) self.max_size = QLineEdit("7") self.max_size.setMaximumWidth(50) s32.addWidget(self.max_size) self.time_limit = QLineEdit("inf") self.time_limit.setMaximumWidth(50) s32.addWidget(self.time_limit) sg1.addItem(s32) sgx.addWidget(self.gen_kos) sgx.addWidget(self.exclude_boundary) sgx.addItem(sg1) s3.addItem(sgx) g3 = QGroupBox("Solver") s33 = QVBoxLayout() self.bg1 = QButtonGroup() optlang_solver_name = interface_to_str( appdata.project.cobra_py_model.problem) self.solver_optlang = QRadioButton(f"{optlang_solver_name} (optlang)") self.solver_optlang.setToolTip( "Uses the solver specified by the current model.") s33.addWidget(self.solver_optlang) self.bg1.addButton(self.solver_optlang) self.solver_cplex_matlab = QRadioButton("CPLEX (MATLAB)") self.solver_cplex_matlab.setToolTip( "Only enabled with MATLAB and CPLEX") s33.addWidget(self.solver_cplex_matlab) self.bg1.addButton(self.solver_cplex_matlab) self.solver_cplex_java = QRadioButton("CPLEX (Octave)") self.solver_cplex_java.setToolTip("Only enabled with Octave and CPLEX") s33.addWidget(self.solver_cplex_java) self.bg1.addButton(self.solver_cplex_java) self.solver_intlinprog = QRadioButton("intlinprog (MATLAB)") self.solver_intlinprog.setToolTip("Only enabled with MATLAB") s33.addWidget(self.solver_intlinprog) self.bg1.addButton(self.solver_intlinprog) self.solver_glpk = QRadioButton("GLPK (Octave/MATLAB)") s33.addWidget(self.solver_glpk) self.bg1.addButton(self.solver_glpk) self.bg1.buttonClicked.connect(self.configure_solver_options) g3.setLayout(s33) s3.addWidget(g3) g4 = QGroupBox("MCS search") s34 = QVBoxLayout() self.bg2 = QButtonGroup() self.any_mcs = QRadioButton("any MCS (fast)") self.any_mcs.setChecked(True) s34.addWidget(self.any_mcs) self.bg2.addButton(self.any_mcs) # Search type: by cardinality only with CPLEX possible self.mcs_by_cardinality = QRadioButton("by cardinality") s34.addWidget(self.mcs_by_cardinality) self.bg2.addButton(self.mcs_by_cardinality) self.smalles_mcs_first = QRadioButton("smallest MCS first") s34.addWidget(self.smalles_mcs_first) self.bg2.addButton(self.smalles_mcs_first) g4.setLayout(s34) s3.addWidget(g4) self.layout.addItem(s3) # Disable incompatible combinations if appdata.selected_engine == 'None': self.solver_optlang.setChecked(True) self.solver_cplex_matlab.setEnabled(False) self.solver_cplex_java.setEnabled(False) self.solver_glpk.setEnabled(False) self.solver_intlinprog.setEnabled(False) if optlang_solver_name != 'cplex': self.mcs_by_cardinality.setEnabled(False) else: self.solver_glpk.setChecked(True) if not self.eng.is_cplex_matlab_ready(): self.solver_cplex_matlab.setEnabled(False) if not self.eng.is_cplex_java_ready(): self.solver_cplex_java.setEnabled(False) if self.appdata.is_matlab_set(): self.solver_cplex_java.setEnabled(False) if not self.appdata.is_matlab_set(): self.solver_cplex_matlab.setEnabled(False) self.solver_intlinprog.setEnabled(False) self.configure_solver_options() s4 = QVBoxLayout() self.consider_scenario = QCheckBox( "Consider constraint given by scenario") s4.addWidget(self.consider_scenario) self.advanced = QCheckBox( "Advanced: Define knockout/addition costs for genes/reactions") self.advanced.setEnabled(False) s4.addWidget(self.advanced) self.layout.addItem(s4) buttons = QHBoxLayout() # self.save = QPushButton("save") # buttons.addWidget(self.save) # self.load = QPushButton("load") # buttons.addWidget(self.load) self.compute_mcs = QPushButton("Compute MCS") buttons.addWidget(self.compute_mcs) # self.compute_mcs2 = QPushButton("Compute MCS2") # buttons.addWidget(self.compute_mcs2) self.cancel = QPushButton("Close") buttons.addWidget(self.cancel) self.layout.addItem(buttons) # max width for buttons self.add_target.setMaximumWidth(20) self.rem_target.setMaximumWidth(20) self.add_desire.setMaximumWidth(20) self.rem_desire.setMaximumWidth(20) self.setLayout(self.layout) # Connecting the signal self.cancel.clicked.connect(self.reject) self.compute_mcs.clicked.connect(self.compute) @Slot() def configure_solver_options(self): optlang_solver_name = interface_to_str( self.appdata.project.cobra_py_model.problem) if self.solver_optlang.isChecked(): self.gen_kos.setChecked(False) self.gen_kos.setEnabled(False) self.exclude_boundary.setEnabled(True) if optlang_solver_name != 'cplex': if self.mcs_by_cardinality.isChecked(): self.mcs_by_cardinality.setChecked(False) self.any_mcs.setChecked(True) self.mcs_by_cardinality.setEnabled(False) self.mcs_by_cardinality.setChecked(False) else: self.gen_kos.setEnabled(True) self.exclude_boundary.setChecked(False) self.exclude_boundary.setEnabled(False) self.mcs_by_cardinality.setEnabled(True) def add_target_region(self): i = self.target_list.rowCount() self.target_list.insertRow(i) completer = QCompleter( self.appdata.project.cobra_py_model.reactions.list_attr("id"), self) completer.setCaseSensitivity(Qt.CaseInsensitive) item = QLineEdit("1") self.target_list.setCellWidget(i, 0, item) item2 = QLineEdit("") item2.setCompleter(completer) self.target_list.setCellWidget(i, 1, item2) combo = QComboBox(self.target_list) combo.insertItem(1, "≤") combo.insertItem(2, "≥") self.target_list.setCellWidget(i, 2, combo) item = QLineEdit("0") self.target_list.setCellWidget(i, 3, item) def add_desired_region(self): i = self.desired_list.rowCount() self.desired_list.insertRow(i) completer = QCompleter( self.appdata.project.cobra_py_model.reactions.list_attr("id"), self) completer.setCaseSensitivity(Qt.CaseInsensitive) item = QLineEdit("1") self.desired_list.setCellWidget(i, 0, item) item2 = QLineEdit("") item2.setCompleter(completer) self.desired_list.setCellWidget(i, 1, item2) combo = QComboBox(self.desired_list) combo.insertItem(1, "≤") combo.insertItem(2, "≥") self.desired_list.setCellWidget(i, 2, combo) item = QLineEdit("0") self.desired_list.setCellWidget(i, 3, item) def rem_target_region(self): i = self.target_list.rowCount() self.target_list.removeRow(i-1) def rem_desired_region(self): i = self.desired_list.rowCount() self.desired_list.removeRow(i-1) def compute(self): if self.solver_optlang.isChecked(): self.compute_optlang() else: self.compute_legacy() def compute_legacy(self): self.setCursor(Qt.BusyCursor) # create CobraModel for matlab with self.appdata.project.cobra_py_model as model: if self.consider_scenario.isChecked(): # integrate scenario into model bounds for r in self.appdata.project.scen_values.keys(): model.reactions.get_by_id( r).bounds = self.appdata.project.scen_values[r] cobra.io.save_matlab_model(model, os.path.join( self.appdata.cna_path, "cobra_model.mat"), varname="cbmodel") self.eng.eval("load('cobra_model.mat')", nargout=0) try: self.eng.eval("cnap = CNAcobra2cna(cbmodel);", nargout=0, stdout=self.out, stderr=self.err) except Exception: output = io.StringIO() traceback.print_exc(file=output) exstr = output.getvalue() print(exstr) QMessageBox.warning(self, 'Unknown exception occured!', exstr+'\nPlease report the problem to:\n\ \nhttps://github.com/cnapy-org/CNApy/issues') return self.eng.eval("genes = [];", nargout=0, stdout=self.out, stderr=self.err) cmd = "maxSolutions = " + str(float(self.max_solu.text())) + ";" self.eng.eval(cmd, nargout=0, stdout=self.out, stderr=self.err) cmd = "maxSize = " + str(int(self.max_size.text())) + ";" self.eng.eval(cmd, nargout=0, stdout=self.out, stderr=self.err) cmd = "milp_time_limit = " + str(float(self.time_limit.text())) + ";" self.eng.eval(cmd, nargout=0, stdout=self.out, stderr=self.err) if self.gen_kos.isChecked(): self.eng.eval("gKOs = 1;", nargout=0) else: self.eng.eval("gKOs = 0;", nargout=0) if self.advanced.isChecked(): self.eng.eval("advanced_on = 1;", nargout=0) else: self.eng.eval("advanced_on = 0;", nargout=0) if self.solver_intlinprog.isChecked(): self.eng.eval("solver = 'intlinprog';", nargout=0) if self.solver_cplex_java.isChecked(): self.eng.eval("solver = 'java_cplex_new';", nargout=0) if self.solver_cplex_matlab.isChecked(): self.eng.eval("solver = 'matlab_cplex';", nargout=0) if self.solver_glpk.isChecked(): self.eng.eval("solver = 'glpk';", nargout=0) if self.any_mcs.isChecked(): self.eng.eval("mcs_search_mode = 'search_1';", nargout=0) elif self.mcs_by_cardinality.isChecked(): self.eng.eval("mcs_search_mode = 'search_2';", nargout=0) elif self.smalles_mcs_first.isChecked(): self.eng.eval("mcs_search_mode = 'search_3';", nargout=0) rows = self.target_list.rowCount() for i in range(0, rows): p1 = self.target_list.cellWidget(i, 0).text() p2 = self.target_list.cellWidget(i, 1).text() if self.target_list.cellWidget(i, 2).currentText() == '≤': p3 = "<=" else: p3 = ">=" p4 = self.target_list.cellWidget(i, 3).text() cmd = "dg_T = {[" + p1+"], '" + p2 + \ "', '" + p3 + "', [" + p4 + "']};" self.eng.eval(cmd, nargout=0, stdout=self.out, stderr=self.err) rows = self.desired_list.rowCount() for i in range(0, rows): p1 = self.desired_list.cellWidget(i, 0).text() p2 = self.desired_list.cellWidget(i, 1).text() if self.desired_list.cellWidget(i, 2).currentText() == '≤': p3 = "<=" else: p3 = ">=" p4 = self.desired_list.cellWidget(i, 3).text() cmd = "dg_D = {[" + p1+"], '" + p2 + \ "', '" + p3 + "', [" + p4 + "']};" self.eng.eval(cmd, nargout=0) # get some data self.eng.eval("reac_id = cellstr(cnap.reacID).';", nargout=0, stdout=self.out, stderr=self.err) mcs = [] values = [] reactions = [] reac_id = [] if self.appdata.is_matlab_set(): reac_id = self.eng.workspace['reac_id'] try: self.eng.eval("[mcs] = cnapy_compute_mcs(cnap, genes, maxSolutions, maxSize, milp_time_limit, gKOs, advanced_on, solver, mcs_search_mode, dg_T,dg_D);", nargout=0) except Exception: output = io.StringIO() traceback.print_exc(file=output) exstr = output.getvalue() print(exstr) QMessageBox.warning(self, 'Unknown exception occured!', exstr+'\nPlease report the problem to:\n\ \nhttps://github.com/cnapy-org/CNApy/issues') return else: self.eng.eval("[reaction, mcs, value] = find(mcs);", nargout=0, stdout=self.out, stderr=self.err) reactions = self.eng.workspace['reaction'] mcs = self.eng.workspace['mcs'] values = self.eng.workspace['value'] elif self.appdata.is_octave_ready(): reac_id = self.eng.pull('reac_id') reac_id = reac_id[0] try: self.eng.eval("[mcs] = cnapy_compute_mcs(cnap, genes, maxSolutions, maxSize, milp_time_limit, gKOs, advanced_on, solver, mcs_search_mode, dg_T,dg_D);", nargout=0) except Exception: output = io.StringIO() traceback.print_exc(file=output) exstr = output.getvalue() print(exstr) QMessageBox.warning(self, 'Unknown exception occured!', exstr+'\nPlease report the problem to:\n\ \nhttps://github.com/cnapy-org/CNApy/issues') return else: self.eng.eval("[reaction, mcs, value] = find(mcs);", nargout=0, stdout=self.out, stderr=self.err) reactions = self.eng.pull('reaction') mcs = self.eng.pull('mcs') values = self.eng.pull('value') if len(mcs) == 0: QMessageBox.information(self, 'No cut sets', 'Cut sets have not been calculated or do not exist.') else: last_mcs = 1 omcs = [] current_mcs = {} for i in range(0, len(reactions)): reacid = int(reactions[i][0]) reaction = reac_id[reacid-1] c_mcs = int(mcs[i][0]) c_value = int(values[i][0]) if c_value == -1: # -1 stands for removed which is 0 in the ui c_value = 0 if c_mcs > last_mcs: omcs.append(current_mcs) last_mcs = c_mcs current_mcs = {} current_mcs[reaction] = c_value omcs.append(current_mcs) self.appdata.project.modes = omcs self.centralwidget.mode_navigator.current = 0 QMessageBox.information(self, 'Cut sets found', str(len(omcs))+' Cut sets have been calculated.') self.centralwidget.update_mode() self.centralwidget.mode_navigator.title.setText("MCS Navigation") self.setCursor(Qt.ArrowCursor) def compute_optlang(self): self.setCursor(Qt.BusyCursor) max_mcs_num = float(self.max_solu.text()) max_mcs_size = int(self.max_size.text()) timeout = float(self.time_limit.text()) if timeout is float('inf'): timeout = None # if self.gen_kos.isChecked(): # self.eng.eval("gKOs = 1;", nargout=0) # else: # self.eng.eval("gKOs = 0;", nargout=0) # if self.advanced.isChecked(): # self.eng.eval("advanced_on = 1;", nargout=0) # else: # self.eng.eval("advanced_on = 0;", nargout=0) if self.smalles_mcs_first.isChecked(): enum_method = 1 elif self.mcs_by_cardinality.isChecked(): enum_method = 2 elif self.any_mcs.isChecked(): enum_method = 3 with self.appdata.project.cobra_py_model as model: if self.consider_scenario.isChecked(): # integrate scenario into model bounds for r in self.appdata.project.scen_values.keys(): model.reactions.get_by_id( r).bounds = self.appdata.project.scen_values[r] reac_id = model.reactions.list_attr("id") reac_id_symbols = cMCS_enumerator.get_reac_id_symbols(reac_id) rows = self.target_list.rowCount() targets = dict() for i in range(0, rows): p1 = self.target_list.cellWidget(i, 0).text() p2 = self.target_list.cellWidget(i, 1).text() if len(p1) > 0 and len(p2) > 0: if self.target_list.cellWidget(i, 2).currentText() == '≤': p3 = "<=" else: p3 = ">=" p4 = float(self.target_list.cellWidget(i, 3).text()) targets.setdefault(p1, []).append((p2, p3, p4)) targets = list(targets.values()) targets = [cMCS_enumerator.relations2leq_matrix(cMCS_enumerator.parse_relations( t, reac_id_symbols=reac_id_symbols), reac_id) for t in targets] rows = self.desired_list.rowCount() desired = dict() for i in range(0, rows): p1 = self.desired_list.cellWidget(i, 0).text() p2 = self.desired_list.cellWidget(i, 1).text() if len(p1) > 0 and len(p2) > 0: if self.desired_list.cellWidget(i, 2).currentText() == '≤': p3 = "<=" else: p3 = ">=" p4 = float(self.desired_list.cellWidget(i, 3).text()) desired.setdefault(p1, []).append((p2, p3, p4)) desired = list(desired.values()) desired = [cMCS_enumerator.relations2leq_matrix(cMCS_enumerator.parse_relations( d, reac_id_symbols=reac_id_symbols), reac_id) for d in desired] try: mcs = cMCS_enumerator.compute_mcs(model, targets=targets, desired=desired, enum_method=enum_method, max_mcs_size=max_mcs_size, max_mcs_num=max_mcs_num, timeout=timeout, exclude_boundary_reactions_as_cuts=self.exclude_boundary.isChecked()) except cMCS_enumerator.InfeasibleRegion as e: QMessageBox.warning(self, 'Cannot calculate MCS', str(e)) return targets, desired except Exception: output = io.StringIO() traceback.print_exc(file=output) exstr = output.getvalue() print(exstr) QMessageBox.warning(self, 'An exception has occured!', exstr+'\nPlease report the problem to:\n\ \nhttps://github.com/cnapy-org/CNApy/issues') return targets, desired finally: self.setCursor(Qt.ArrowCursor) if len(mcs) == 0: QMessageBox.information(self, 'No cut sets', 'Cut sets have not been calculated or do not exist.') return targets, desired omcs = [{reac_id[i]: 0 for i in m} for m in mcs] self.appdata.project.modes = omcs self.centralwidget.mode_navigator.current = 0 QMessageBox.information(self, 'Cut sets found', str(len(omcs))+' Cut sets have been calculated.') self.centralwidget.update_mode() self.centralwidget.mode_navigator.title.setText("MCS Navigation")
class DepthGUI(QMainWindow): def __init__(self): super(DepthGUI, self).__init__() self.display_string = None # Serial port config self.ser = serial.Serial() self.ser.baudrate = 19200 self.depth_stream = None self._prev_port = None self.setup_ui() def setup_ui(self): self.setWindowTitle('Neuroport DBS - Electrodes Depth') self.setWindowFlags(Qt.FramelessWindowHint) self.move(WINDOWDIMS_DEPTH[0], WINDOWDIMS_DEPTH[1]) self.setFixedSize(WINDOWDIMS_DEPTH[2], WINDOWDIMS_DEPTH[3]) self.show() self.plot_widget = QWidget() self.setCentralWidget(self.plot_widget) # define Qt GUI elements v_layout = QVBoxLayout() v_layout.setSpacing(0) v_layout.setContentsMargins(10, 0, 10, 10) h_layout = QHBoxLayout() self.comboBox_com_port = QComboBox() h_layout.addWidget(self.comboBox_com_port) self.pushButton_open = QPushButton("Open") h_layout.addWidget(self.pushButton_open) h_layout.addStretch() self.label_offset = QLabel("Offset: ") h_layout.addWidget(self.label_offset) self.doubleSpinBox_offset = QDoubleSpinBox() self.doubleSpinBox_offset.setMinimum(-100.00) self.doubleSpinBox_offset.setMaximum(100.00) self.doubleSpinBox_offset.setSingleStep(1.00) self.doubleSpinBox_offset.setDecimals(2) self.doubleSpinBox_offset.setValue(-10.00) self.doubleSpinBox_offset.setFixedWidth(60) h_layout.addWidget(self.doubleSpinBox_offset) h_layout.addStretch() h_layout.addWidget(QLabel("Stream to :")) self.chk_NSP = QCheckBox("NSP") self.chk_NSP.setChecked(True) h_layout.addWidget(self.chk_NSP) h_layout.addSpacing(5) self.chk_LSL = QCheckBox("LSL") self.chk_LSL.clicked.connect(self.on_chk_LSL_clicked) self.chk_LSL.click( ) # default is enabled, click call to trigger LSL stream creation. h_layout.addWidget(self.chk_LSL) h_layout.addSpacing(5) send_btn = QPushButton("Send") send_btn.clicked.connect(self.send) h_layout.addWidget(send_btn) h_layout.addSpacing(5) quit_btn = QPushButton('X') quit_btn.setMaximumWidth(20) quit_btn.clicked.connect(QApplication.instance().quit) quit_btn.setStyleSheet("QPushButton { color: white; " "background-color : red; " "border-color : red; " "border-width: 2px}") h_layout.addWidget(quit_btn) v_layout.addLayout(h_layout) # define Qt GUI elements # add a frame for the LCD numbers self.lcd_frame = QFrame() self.lcd_frame.setFrameShape(1) lcd_layout = QGridLayout() self.lcd_frame.setLayout(lcd_layout) # RAW reading from DDU self.raw_ddu = QLCDNumber() self.raw_ddu.setDigitCount(7) self.raw_ddu.setFrameShape(0) self.raw_ddu.setSmallDecimalPoint(True) self.raw_ddu.setFixedHeight(50) self.raw_ddu.display("{0:.3f}".format(0)) lcd_layout.addWidget(self.raw_ddu, 0, 3, 2, 3) self.offset_ddu = QLCDNumber() self.offset_ddu.setDigitCount(7) self.offset_ddu.setFixedHeight(150) self.offset_ddu.display("{0:.3f}".format(-10)) self.offset_ddu.setFrameShape(0) lcd_layout.addWidget(self.offset_ddu, 2, 0, 5, 6) v_layout.addWidget(self.lcd_frame) self.plot_widget.setLayout(v_layout) # Populate control panel items for port in serial.tools.list_ports.comports(): self.comboBox_com_port.addItem(port.device) self.comboBox_com_port.addItem("cbsdk playback") # Connect signals & slots self.pushButton_open.clicked.connect(self.on_open_clicked) self.comboBox_com_port.currentIndexChanged.connect( self.on_comboBox_com_port_changed) # TODO: Add signal for comboBox_com_port --> when cbsdk playback, uncheck NSP then re-open connection. def on_chk_LSL_clicked(self, state): print(f"LSL clicked state: {state}") if self.chk_LSL.isChecked(): outlet_info = pylsl.StreamInfo(name='electrode_depth', type='depth', channel_count=1, nominal_srate=pylsl.IRREGULAR_RATE, channel_format=pylsl.cf_float32, source_id='depth1214') self.depth_stream = pylsl.StreamOutlet(outlet_info) else: self.depth_stream = None def _do_close(self, from_port): if from_port == "cbsdk playback": CbSdkConnection().disconnect() else: self.ser.close() self.pushButton_open.setText("Open") def on_comboBox_com_port_changed(self, new_ix): # If a connection was already open, close it before proceeding. if self.pushButton_open.text() == "Close": self._do_close(self._prev_port) # at this point the Open/Close pushbutton should show: Open # we will only enable/disable the Send to NSP button to leave the current checked status. # The default should be checked. if self.comboBox_com_port.currentText() == "cbsdk playback": # If switching _to_ cbsdk playback, disable sending out comments. self.chk_NSP.setEnabled(False) elif self._prev_port == "cbsdk playback": # If switching _from_ cbsdk playback, re-enable sending out comments. self.chk_NSP.setEnabled(True) self._prev_port = self.comboBox_com_port.currentText() def on_open_clicked(self): com_port = self.comboBox_com_port.currentText() cmd_text = self.pushButton_open.text() if cmd_text == 'Open': if com_port == "cbsdk playback": CbSdkConnection().connect() CbSdkConnection().cbsdk_config = { 'reset': True, 'get_events': False, 'get_comments': True, 'buffer_parameter': { 'comment_length': 10 } } self.pushButton_open.setText("Close") else: if self.chk_NSP.isEnabled() and self.chk_NSP.isChecked(): CbSdkConnection().connect() CbSdkConnection().cbsdk_config = { 'reset': True, 'get_events': False, 'get_comments': False } if not self.ser.is_open: self.ser.port = com_port try: self.ser.open() # TODO: Add timeout; Add error. self.ser.write('AXON+\r'.encode()) except serial.serialutil.SerialException: print("Could not open serial port") finally: self.pushButton_open.setText( "Close" if self.ser.is_open else "Open") else: self._do_close(com_port) def update(self): # Added new_value handling for playback if we ever want to post-process depth # on previously recorded sessions. new_value = False out_value = None if self.comboBox_com_port.currentText() == "cbsdk playback": cbsdk_conn = CbSdkConnection() if cbsdk_conn.is_connected: comments = cbsdk_conn.get_comments() if comments: comment_strings = [x[1].decode('utf8') for x in comments] else: comment_strings = "" dtts = [] for comm_str in comment_strings: if 'DTT:' in comm_str: dtts.append(float(comm_str[4:])) if len(dtts) > 0: out_value = dtts[-1] new_value = True self.offset_ddu.display("{0:.3f}".format(out_value)) offset = self.doubleSpinBox_offset.value() self.raw_ddu.display("{0:.3f}".format(out_value - offset)) elif self.ser.is_open: in_str = self.ser.readline().decode('utf-8').strip() if in_str: try: in_value = float(in_str) # in_value /= DDUSCALEFACTOR # Uncomment this for FHC DDU V2. self.raw_ddu.display("{0:.3f}".format(in_value)) out_value = in_value + self.doubleSpinBox_offset.value() display_string = "{0:.3f}".format(out_value) self.offset_ddu.display(display_string) # Check if new value if display_string != self.display_string: new_value = True self.display_string = display_string # Push to NSP cbsdk_conn = CbSdkConnection() if cbsdk_conn.is_connected: if self.chk_NSP.isChecked() and self.chk_NSP.isEnabled( ) and new_value: cbsdk_conn.set_comments("DTT:" + display_string) else: # try connecting if not connected but button is active if self.chk_NSP.isChecked() and self.chk_NSP.isEnabled( ): cbsdk_conn.connect() cbsdk_conn.cbsdk_config = { 'reset': True, 'get_events': False, 'get_comments': False } # set button to connection status self.chk_NSP.setChecked(cbsdk_conn.is_connected) except ValueError: print("DDU result: {}".format(in_str)) # Push to LSL if self.depth_stream is not None and new_value: self.depth_stream.push_sample([out_value]) def send(self): self.display_string = None # make sure the update function runs self.update()
class LSPServerEditor(QDialog): DEFAULT_HOST = '127.0.0.1' DEFAULT_PORT = 2084 DEFAULT_CMD = '' DEFAULT_ARGS = '' DEFAULT_CONFIGURATION = '{}' DEFAULT_EXTERNAL = False HOST_REGEX = re.compile(r'^\w+([.]\w+)*$') NON_EMPTY_REGEX = re.compile(r'^\S+$') JSON_VALID = _('JSON valid') JSON_INVALID = _('JSON invalid') def __init__(self, parent, language=None, cmd='', host='127.0.0.1', port=2084, args='', external=False, configurations={}, **kwargs): super(LSPServerEditor, self).__init__(parent) self.parent = parent self.external = external bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = bbox.button(QDialogButtonBox.Ok) self.button_cancel = bbox.button(QDialogButtonBox.Cancel) self.button_ok.setEnabled(False) description = _('To create a new configuration, ' 'you need to select a programming ' 'language, along with a executable ' 'name for the server to execute ' '(If the instance is local), ' 'and the host and port. Finally, ' 'you need to provide the ' 'arguments that the server accepts. ' 'The placeholders <tt>%(host)s</tt> and ' '<tt>%(port)s</tt> refer to the host ' 'and the port, respectively.') server_settings_description = QLabel(description) server_settings_description.setWordWrap(True) lang_label = QLabel(_('Language:')) self.lang_cb = QComboBox(self) self.lang_cb.setToolTip(_('Programming language provided ' 'by the LSP server')) self.lang_cb.addItem(_('Select a language')) self.lang_cb.addItems(LSP_LANGUAGES) if language is not None: idx = LSP_LANGUAGES.index(language) self.lang_cb.setCurrentIndex(idx + 1) self.button_ok.setEnabled(True) host_label = QLabel(_('Host:')) self.host_input = QLineEdit(self) self.host_input.setToolTip(_('Name of the host that will provide ' 'access to the server')) self.host_input.setText(host) self.host_input.textChanged.connect(lambda x: self.validate()) port_label = QLabel(_('Port:')) self.port_spinner = QSpinBox(self) self.port_spinner.setToolTip(_('TCP port number of the server')) self.port_spinner.setMinimum(1) self.port_spinner.setMaximum(60000) self.port_spinner.setValue(port) cmd_label = QLabel(_('Command to execute:')) self.cmd_input = QLineEdit(self) self.cmd_input.setToolTip(_('Command used to start the ' 'LSP server locally')) self.cmd_input.setText(cmd) if not external: self.cmd_input.textChanged.connect(lambda x: self.validate()) args_label = QLabel(_('Server arguments:')) self.args_input = QLineEdit(self) self.args_input.setToolTip(_('Additional arguments required to ' 'start the server')) self.args_input.setText(args) conf_label = QLabel(_('LSP Server Configurations:')) self.conf_input = CodeEditor(None) self.conf_input.textChanged.connect(self.validate) color_scheme = CONF.get('color_schemes', 'selected') self.conf_input.setup_editor( language='JSON', color_scheme=color_scheme, wrap=False, edge_line=True, highlight_current_line=True, highlight_current_cell=True, occurrence_highlighting=True, auto_unindent=True, font=get_font(), filename='config.json') self.conf_input.setToolTip(_('Additional LSP server configurations ' 'set at runtime. JSON required')) conf_text = '{}' try: conf_text = json.dumps(configurations, indent=4, sort_keys=True) except Exception: pass self.conf_input.set_text(conf_text) self.json_label = QLabel(self.JSON_VALID, self) self.external_cb = QCheckBox(_('External server'), self) self.external_cb.setToolTip(_('Check if the server runs ' 'on a remote location')) self.external_cb.setChecked(external) self.external_cb.stateChanged.connect(self.set_local_options) hlayout = QHBoxLayout() general_vlayout = QVBoxLayout() general_vlayout.addWidget(server_settings_description) vlayout = QVBoxLayout() lang_layout = QVBoxLayout() lang_layout.addWidget(lang_label) lang_layout.addWidget(self.lang_cb) # layout2 = QHBoxLayout() # layout2.addLayout(lang_layout) lang_layout.addWidget(self.external_cb) vlayout.addLayout(lang_layout) host_layout = QVBoxLayout() host_layout.addWidget(host_label) host_layout.addWidget(self.host_input) port_layout = QVBoxLayout() port_layout.addWidget(port_label) port_layout.addWidget(self.port_spinner) conn_info_layout = QHBoxLayout() conn_info_layout.addLayout(host_layout) conn_info_layout.addLayout(port_layout) vlayout.addLayout(conn_info_layout) cmd_layout = QVBoxLayout() cmd_layout.addWidget(cmd_label) cmd_layout.addWidget(self.cmd_input) vlayout.addLayout(cmd_layout) args_layout = QVBoxLayout() args_layout.addWidget(args_label) args_layout.addWidget(self.args_input) vlayout.addLayout(args_layout) conf_layout = QVBoxLayout() conf_layout.addWidget(conf_label) conf_layout.addWidget(self.conf_input) conf_layout.addWidget(self.json_label) hlayout.addLayout(vlayout) hlayout.addLayout(conf_layout) general_vlayout.addLayout(hlayout) general_vlayout.addWidget(bbox) self.setLayout(general_vlayout) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) self.lang_cb.currentIndexChanged.connect( self.lang_selection_changed) self.form_status(False) if language is not None: self.form_status(True) self.validate() @Slot() def validate(self): host_text = self.host_input.text() cmd_text = self.cmd_input.text() if not self.HOST_REGEX.match(host_text): self.button_ok.setEnabled(False) self.host_input.setStyleSheet("QLineEdit{border: 1px solid red;}") self.host_input.setToolTip('Hostname must be valid') return else: self.host_input.setStyleSheet( "QLineEdit{border: 1px solid green;}") self.host_input.setToolTip('Hostname is valid') self.button_ok.setEnabled(True) if not self.external: if not self.NON_EMPTY_REGEX.match(cmd_text): self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid red;}") self.cmd_input.setToolTip('Command must be non empty') return if find_program(cmd_text) is None: self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid red;}") self.cmd_input.setToolTip('Program was not found ' 'on your system') return else: self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid green;}") self.cmd_input.setToolTip('Program was found on your system') self.button_ok.setEnabled(True) try: json.loads(self.conf_input.toPlainText()) try: self.json_label.setText(self.JSON_VALID) except: pass except (ValueError, json.decoder.JSONDecodeError): try: self.json_label.setText(self.JSON_INVALID) self.button_ok.setEnabled(False) except: pass def form_status(self, status): self.host_input.setEnabled(status) self.port_spinner.setEnabled(status) self.external_cb.setEnabled(status) self.cmd_input.setEnabled(status) self.args_input.setEnabled(status) self.conf_input.setEnabled(status) self.json_label.setVisible(status) @Slot() def lang_selection_changed(self): idx = self.lang_cb.currentIndex() if idx == 0: self.set_defaults() self.form_status(False) self.button_ok.setEnabled(False) else: server = self.parent.get_server_by_lang(LSP_LANGUAGES[idx - 1]) self.form_status(True) if server is not None: self.host_input.setText(server.host) self.port_spinner.setValue(server.port) self.external_cb.setChecked(server.external) self.cmd_input.setText(server.cmd) self.args_input.setText(server.args) self.conf_input.set_text(json.dumps(server.configurations)) self.json_label.setText(self.JSON_VALID) self.button_ok.setEnabled(True) else: self.set_defaults() def set_defaults(self): self.cmd_input.setStyleSheet('') self.host_input.setStyleSheet('') self.host_input.setText(self.DEFAULT_HOST) self.port_spinner.setValue(self.DEFAULT_PORT) self.external_cb.setChecked(self.DEFAULT_EXTERNAL) self.cmd_input.setText(self.DEFAULT_CMD) self.args_input.setText(self.DEFAULT_ARGS) self.conf_input.set_text(self.DEFAULT_CONFIGURATION) self.json_label.setText(self.JSON_VALID) @Slot(bool) @Slot(int) def set_local_options(self, enabled): self.external = enabled self.cmd_input.setEnabled(True) self.args_input.setEnabled(True) if enabled: self.cmd_input.setEnabled(False) self.cmd_input.setStyleSheet('') self.args_input.setEnabled(False) try: self.validate() except: pass def get_options(self): language_idx = self.lang_cb.currentIndex() language = LSP_LANGUAGES[language_idx - 1] host = self.host_input.text() port = int(self.port_spinner.value()) external = self.external_cb.isChecked() args = self.args_input.text() cmd = self.cmd_input.text() configurations = json.loads(self.conf_input.toPlainText()) server = LSPServer(language=language.lower(), cmd=cmd, args=args, host=host, port=port, external=external, configurations=configurations) return server
def setup_and_check(self, data, title=''): """ Setup DataFrameEditor: return False if data is not supported, True otherwise. Supported types for data are DataFrame, Series and Index. """ self._selection_rec = False self._model = None self.layout = QGridLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.setWindowIcon(ima.icon('arredit')) if title: title = to_text_string(title) + " - %s" % data.__class__.__name__ else: title = _("%s editor") % data.__class__.__name__ if isinstance(data, Series): self.is_series = True data = data.to_frame() elif isinstance(data, Index): data = DataFrame(data) self.setWindowTitle(title) # self.resize(600, 500) # mx change self.hscroll = QScrollBar(Qt.Horizontal) self.vscroll = QScrollBar(Qt.Vertical) # Create the view for the level self.create_table_level() # Create the view for the horizontal header self.create_table_header() # Create the view for the vertical index self.create_table_index() # Create the model and view of the data self.dataModel = DataFrameModel(data, parent=self) # self.dataModel.dataChanged.connect(self.save_and_close_enable) # mx change self.create_data_table() self.layout.addWidget(self.hscroll, 2, 0, 1, 2) self.layout.addWidget(self.vscroll, 0, 2, 2, 1) # autosize columns on-demand self._autosized_cols = set() # Set limit time to calculate column sizeHint to 300ms, # See spyder-ide/spyder#11060 self._max_autosize_ms = 300 self.dataTable.installEventFilter(self) avg_width = self.fontMetrics().averageCharWidth() self.min_trunc = avg_width * 12 # Minimum size for columns self.max_width = avg_width * 64 # Maximum size for columns self.setLayout(self.layout) self.setMinimumSize(400, 300) # Make the dialog act as a window # self.setWindowFlags(Qt.Window) # mx change btn_layout = QHBoxLayout() btn = QPushButton(_("Format")) # disable format button for int type btn_layout.addWidget(btn) btn.clicked.connect(self.change_format) btn = QPushButton(_('Resize')) btn_layout.addWidget(btn) btn.clicked.connect(self.resize_to_contents) bgcolor = QCheckBox(_('Background color')) bgcolor.setChecked(self.dataModel.bgcolor_enabled) bgcolor.setEnabled(self.dataModel.bgcolor_enabled) bgcolor.stateChanged.connect(self.change_bgcolor_enable) btn_layout.addWidget(bgcolor) self.bgcolor_global = QCheckBox(_('Column min/max')) self.bgcolor_global.setChecked(self.dataModel.colum_avg_enabled) self.bgcolor_global.setEnabled(not self.is_series and self.dataModel.bgcolor_enabled) self.bgcolor_global.stateChanged.connect(self.dataModel.colum_avg) btn_layout.addWidget(self.bgcolor_global) btn_layout.addStretch() # mx change # self.btn_save_and_close = QPushButton(_('Save and Close')) # self.btn_save_and_close.setDisabled(True) # self.btn_save_and_close.clicked.connect(self.accept) # btn_layout.addWidget(self.btn_save_and_close) # # self.btn_close = QPushButton(_('Close')) # self.btn_close.setAutoDefault(True) # self.btn_close.setDefault(True) # self.btn_close.clicked.connect(self.reject) # btn_layout.addWidget(self.btn_close) btn_layout.setContentsMargins(4, 4, 4, 4) self.layout.addLayout(btn_layout, 4, 0, 1, 2) self.setModel(self.dataModel) self.resizeColumnsToContents() return True
class SampleLogsView(QSplitter): """Sample Logs View This contains a table of the logs, a plot of the currently selected logs, and the statistics of the selected log. """ def __init__(self, presenter, parent = None, name = '', isMD=False, noExp = 0): super(SampleLogsView, self).__init__(parent) self.presenter = presenter self.setWindowTitle("{} sample logs".format(name)) self.setWindowFlags(Qt.Window) self.setAttribute(Qt.WA_DeleteOnClose, True) # left hand side self.frame_left = QFrame() layout_left = QVBoxLayout() # add a spin box for MD workspaces if isMD: layout_mult_expt_info = QHBoxLayout() layout_mult_expt_info.addWidget(QLabel("Experiment Info #")) self.experimentInfo = QSpinBox() self.experimentInfo.setMaximum(noExp-1) self.experimentInfo.valueChanged.connect(self.presenter.changeExpInfo) layout_mult_expt_info.addWidget(self.experimentInfo) layout_mult_expt_info.addSpacerItem(QSpacerItem(10, 10, QSizePolicy.Expanding)) layout_left.addLayout(layout_mult_expt_info) # Create sample log table self.table = QTableView() self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.doubleClicked.connect(self.presenter.doubleClicked) self.table.contextMenuEvent = self.tableMenu layout_left.addWidget(self.table) self.frame_left.setLayout(layout_left) self.addWidget(self.frame_left) #right hand side self.frame_right = QFrame() layout_right = QVBoxLayout() #Add full_time and experimentinfo options layout_options = QHBoxLayout() if isMD: layout_options.addWidget(QLabel("Experiment Info #")) self.experimentInfo = QSpinBox() self.experimentInfo.setMaximum(noExp-1) self.experimentInfo.valueChanged.connect(self.presenter.changeExpInfo) layout_options.addWidget(self.experimentInfo) #check boxes self.full_time = QCheckBox("Relative Time") self.full_time.setToolTip( "Shows relative time in seconds from the start of the run.") self.full_time.setChecked(True) self.full_time.stateChanged.connect(self.presenter.plot_logs) layout_options.addWidget(self.full_time) self.show_filtered = QCheckBox("Filtered Data") self.show_filtered.setToolTip( "Filtered data only shows data while running and in this period.\nInvalid values are also filtered.") self.show_filtered.setChecked(True) self.show_filtered.stateChanged.connect(self.presenter.filtered_changed) layout_options.addWidget(self.show_filtered) self.spaceItem = QSpacerItem(10, 10, QSizePolicy.Expanding) layout_options.addSpacerItem(self.spaceItem) layout_right.addLayout(layout_options) # Sample log plot self.fig = Figure() self.canvas = FigureCanvas(self.fig) self.canvas.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding) self.canvas.mpl_connect('button_press_event', self.presenter.plot_clicked) self.ax = self.fig.add_subplot(111, projection='mantid') layout_right.addWidget(self.canvas) # Sample stats self.create_stats_widgets() layout_stats = QFormLayout() layout_stats.addRow('', QLabel("Log Statistics")) layout_stats.addRow('Min:', self.stats_widgets["minimum"]) layout_stats.addRow('Max:', self.stats_widgets["maximum"]) layout_stats.addRow('Time Avg:', self.stats_widgets["time_mean"]) layout_stats.addRow('Time Std Dev:', self.stats_widgets["time_standard_deviation"]) layout_stats.addRow('Mean (unweighted):', self.stats_widgets["mean"]) layout_stats.addRow('Median (unweighted):', self.stats_widgets["median"]) layout_stats.addRow('Std Dev:', self.stats_widgets["standard_deviation"]) layout_stats.addRow('Duration:', self.stats_widgets["duration"]) layout_right.addLayout(layout_stats) self.frame_right.setLayout(layout_right) self.addWidget(self.frame_right) self.setStretchFactor(0,1) self.resize(1200,800) self.show() def closeEvent(self, event): self.deleteLater() super(SampleLogsView, self).closeEvent(event) def tableMenu(self, event): """Right click menu for table, can plot or print selected logs""" menu = QMenu(self) plotAction = menu.addAction("Plot selected") plotAction.triggered.connect(self.presenter.new_plot_logs) plotAction = menu.addAction("Print selected") plotAction.triggered.connect(self.presenter.print_selected_logs) menu.exec_(event.globalPos()) def set_model(self, model): """Set the model onto the table""" self.model = model self.table.setModel(self.model) self.table.resizeColumnsToContents() self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self.table.selectionModel().selectionChanged.connect(self.presenter.update) def show_plot_and_stats(self, show_plot_and_stats): """sets wether the plot and stats section should be visible""" if self.frame_right.isVisible() != show_plot_and_stats: # the desired state is nor the current state self.setUpdatesEnabled(False) current_width = self.frame_right.width() if current_width: self.last_width = current_width else: current_width = self.last_width if show_plot_and_stats: self.resize(self.width() + current_width, self.height()) else: self.resize(self.width() - current_width, self.height()) self.frame_right.setVisible(show_plot_and_stats) self.setUpdatesEnabled(True) def plot_selected_logs(self, ws, exp, rows): """Update the plot with the selected rows""" if self.frame_right.isVisible(): self.ax.clear() self.create_ax_by_rows(self.ax, ws, exp, rows) try: self.fig.canvas.draw() except ValueError as ve: #this can throw an error if the plot has recently been hidden, but the error does not matter if not str(ve).startswith("Image size of"): raise def new_plot_selected_logs(self, ws, exp, rows): """Create a new plot, in a separate window for selected rows""" fig, ax = plt.subplots(subplot_kw={'projection': 'mantid'}) self.create_ax_by_rows(ax, ws, exp, rows) fig.show() def create_ax_by_rows(self, ax, ws, exp, rows): """Creates the plots for given rows onto axis ax""" for row in rows: log_text = self.get_row_log_name(row) ax.plot(ws, LogName=log_text, label=log_text, FullTime=not self.full_time.isChecked(), Filtered=self.show_filtered.isChecked(), ExperimentInfo=exp) ax.set_ylabel('') if ax.get_legend_handles_labels()[0]: ax.legend() def set_log_controls(self,are_logs_filtered): """Sets log specific settings based on the log clicked on""" self.show_filtered.setEnabled(are_logs_filtered) def get_row_log_name(self, i): """Returns the log name of particular row""" return str(self.model.item(i, 0).text()) def get_exp(self): """Get set experiment info number""" return self.experimentInfo.value() def get_selected_row_indexes(self): """Return a list of selected row from table""" return [row.row() for row in self.table.selectionModel().selectedRows()] def set_selected_rows(self, rows): """Set seleceted rows in table""" mode = QItemSelectionModel.Select | QItemSelectionModel.Rows for row in rows: self.table.selectionModel().select(self.model.index(row, 0), mode) def create_stats_widgets(self): """Creates the statistics widgets""" self.stats_widgets = {"minimum": QLineEdit(), "maximum": QLineEdit(), "mean": QLineEdit(), "median": QLineEdit(), "standard_deviation": QLineEdit(), "time_mean": QLineEdit(), "time_standard_deviation": QLineEdit(), "duration": QLineEdit()} for widget in self.stats_widgets.values(): widget.setReadOnly(True) def set_statistics(self, stats): """Updates the statistics widgets from stats dictionary""" for param in self.stats_widgets.keys(): self.stats_widgets[param].setText('{:.6}'.format(getattr(stats, param))) def clear_statistics(self): """Clears the values in statistics widgets""" for widget in self.stats_widgets.values(): widget.clear()
def populate_gui(self): _contain_parsed = self.contain_parsed for _row, _entry in enumerate(_contain_parsed): if _entry == ['']: continue self.parent.ui.table.insertRow(_row) # select _layout = QHBoxLayout() _widget = QCheckBox() _widget.setEnabled(True) _layout.addWidget(_widget) _layout.addStretch() _new_widget = QWidget() _new_widget.setLayout(_layout) _widget.stateChanged.connect(lambda state=0, row=_row: self.parent.table_select_state_changed(state, row)) self.parent.ui.table.setCellWidget(_row, 0, _new_widget) # name _item = QTableWidgetItem(_entry[1]) self.parent.ui.table.setItem(_row, 1, _item) # runs _item = QTableWidgetItem(_entry[2]) self.parent.ui.table.setItem(_row, 2, _item) # Sample formula if _entry[3]: _item = QTableWidgetItem(_entry[3]) else: _item = QTableWidgetItem("") self.parent.ui.table.setItem(_row, 3, _item) # mass density if _entry[4]: _item = QTableWidgetItem(_entry[4]) else: _item = QTableWidgetItem("") self.parent.ui.table.setItem(_row, 4, _item) # radius if _entry[5]: _item = QTableWidgetItem(_entry[5]) else: _item = QTableWidgetItem("") self.parent.ui.table.setItem(_row, 5, _item) # packing fraction if _entry[6]: _item = QTableWidgetItem(_entry[6]) else: _item = QTableWidgetItem("") self.parent.ui.table.setItem(_row, 6, _item) # sample shape _widget = QComboBox() _widget.addItem("cylindrical") _widget.addItem("spherical") if _entry[7] == "spherical": _widget.setCurrentIndex(1) self.parent.ui.table.setCellWidget(_row, 7, _widget) # do abs corr _layout = QHBoxLayout() _widget = QCheckBox() if _entry[8] == "True": _widget.setCheckState(Qt.Checked) _widget.setStyleSheet("border: 2px; solid-black") _widget.setEnabled(True) _layout.addStretch() _layout.addWidget(_widget) _layout.addStretch() _new_widget = QWidget() _new_widget.setLayout(_layout) self.parent.ui.table.setCellWidget(_row, 8, _new_widget) for _row, _entry in enumerate(_contain_parsed): if _entry == ['']: continue # select _widget = self.parent.ui.table.cellWidget(_row, 0).children()[1] if _entry[0] == "True": _widget.setChecked(True)
class HighlightWindow(PyDialog): """ +-----------+ | Highlight | +--------------------------+ | Nodes ______ | | Elements ______ | | | | Highlight Close | +--------------------------+ """ def __init__(self, data, win_parent=None): """ Saves the data members from data and performs type checks """ PyDialog.__init__(self, data, win_parent) gui = win_parent if gui is None: # pragma: no cover self.highlight_color_float = [0., 0., 0.] self.highlight_color_int = [0, 0, 0] self._highlight_opacity = 0.9 self._point_size = 10 self._label_size = 10.0 self._default_text_size = 10.0 else: #self.highlight_color_float = gui.settings.highlight_color #self.highlight_color_int = [int(val) for val in self.highlight_color_float] self.highlight_color_float, self.highlight_color_int = _check_color( gui.settings.highlight_color) #self.highlight_color_int = gui.settings.highlight_color_int self._highlight_opacity = gui.settings.highlight_opacity self._point_size = 10 self._label_size = 10.0 self._default_text_size = 10.0 self._updated_highlight = False self.actors = [] self._default_font_size = data['font_size'] self.model_name = data['model_name'] assert len(self.model_name) > 0, self.model_name #self._default_annotation_size = data['annotation_size'] # int #self.default_magnify = data['magnify'] if 'nodes_pound' in data: # testing nodes_pound = data['nodes_pound'] elements_pound = data['elements_pound'] nodes = np.arange(1, nodes_pound + 1) elements = np.arange(1, elements_pound + 1) else: # gui nodes = gui.get_node_ids(model_name=self.model_name) elements = gui.get_element_ids(model_name=self.model_name) nodes_pound = nodes.max() elements_pound = elements.max() self.nodes = nodes self.elements = elements self._nodes_pound = nodes_pound self._elements_pound = elements_pound self.setWindowTitle('Highlight') self.create_widgets() self.create_layout() self.set_connections() self.on_font(self._default_font_size) #self.show() def create_widgets(self): """creates the display window""" # Text Size model_name = self.model_name self.nodes_label = QLabel("Nodes:") self.nodes_edit = QNodeEdit(self, model_name, pick_style='area', cleanup=True, tab_to_next=False) self.elements_label = QLabel("Elements:") self.elements_edit = QElementEdit(self, model_name, pick_style='area', cleanup=True, tab_to_next=False) #----------------------------------------------------------------------- # Highlight Color self.highlight_opacity_label = QLabel("Highlight Opacity:") self.highlight_opacity_edit = QDoubleSpinBox(self) self.highlight_opacity_edit.setValue(self._highlight_opacity) self.highlight_opacity_edit.setRange(0.1, 1.0) self.highlight_opacity_edit.setDecimals(1) self.highlight_opacity_edit.setSingleStep(0.1) self.highlight_opacity_button = QPushButton("Default") # Text Color self.highlight_color_label = QLabel("Highlight Color:") self.highlight_color_edit = QPushButtonColor(self.highlight_color_int) self.point_size_label = QLabel("Point Size:") self.point_size_edit = QSpinBox(self) self.point_size_edit.setValue(self._point_size) self.point_size_edit.setRange(7, 30) #self.point_size_button = QPushButton("Default") self.label_size_label = QLabel("Label Size:") self.label_size_edit = QSpinBox(self) self.label_size_edit.setValue(self._default_text_size) self.label_size_edit.setRange(7, 30) #self.label_size_button = QPushButton("Default") #----------------------------------------------------------------------- # closing self.show_button = QPushButton("Show") self.mark_button = QCheckBox("Mark") self.clear_button = QPushButton("Clear") self.close_button = QPushButton("Close") def create_layout(self): """displays the menu objects""" grid = QGridLayout() irow = 0 grid.addWidget(self.nodes_label, irow, 0) grid.addWidget(self.nodes_edit, irow, 1) irow += 1 grid.addWidget(self.elements_label, irow, 0) grid.addWidget(self.elements_edit, irow, 1) irow += 1 # TODO: enable me grid.addWidget(self.highlight_color_label, irow, 0) grid.addWidget(self.highlight_color_edit, irow, 1) self.highlight_color_label.setEnabled(False) self.highlight_color_edit.setEnabled(False) irow += 1 # TODO: enable me grid.addWidget(self.highlight_opacity_label, irow, 0) grid.addWidget(self.highlight_opacity_edit, irow, 1) self.highlight_opacity_label.setEnabled(False) self.highlight_opacity_edit.setEnabled(False) irow += 1 # TODO: enable me grid.addWidget(self.point_size_label, irow, 0) grid.addWidget(self.point_size_edit, irow, 1) self.point_size_label.setEnabled(False) self.point_size_edit.setEnabled(False) irow += 1 # TODO: enable me grid.addWidget(self.label_size_label, irow, 0) grid.addWidget(self.label_size_edit, irow, 1) self.label_size_label.setEnabled(False) self.label_size_edit.setEnabled(False) irow += 1 self.mark_button.setEnabled(False) #self.create_legend_widgets() #grid2 = self.create_legend_layout() ok_cancel_box = QHBoxLayout() ok_cancel_box.addWidget(self.mark_button) ok_cancel_box.addWidget(self.show_button) ok_cancel_box.addWidget(self.clear_button) ok_cancel_box.addWidget(self.close_button) vbox = QVBoxLayout() vbox.addLayout(grid) #vbox.addStretch() #vbox.addLayout(grid2) vbox.addStretch() vbox.addLayout(ok_cancel_box) self.setLayout(vbox) def set_connections(self): """creates the actions for the menu""" self.highlight_color_edit.clicked.connect(self.on_highlight_color) self.highlight_opacity_edit.valueChanged.connect( self.on_highlight_opacity) self.nodes_edit.textChanged.connect(self.on_validate) self.elements_edit.textChanged.connect(self.on_validate) self.show_button.clicked.connect(self.on_show) self.clear_button.clicked.connect(self.on_remove_actors) self.close_button.clicked.connect(self.on_close) # closeEvent def on_font(self, value=None): """update the font for the current window""" if value is None: value = self.font_size_edit.value() font = QtGui.QFont() font.setPointSize(value) self.setFont(font) def on_highlight_color(self): """ Choose a highlight color TODO: not implemented """ title = "Choose a highlight color" rgb_color_ints = self.highlight_color_int color_edit = self.highlight_color_edit func_name = 'set_highlight_color' passed, rgb_color_ints, rgb_color_floats = self._background_color( title, color_edit, rgb_color_ints, func_name) if passed: self.highlight_color_int = rgb_color_ints self.highlight_color_float = rgb_color_floats def on_highlight_opacity(self, value=None): """ update the highlight opacity TODO: not implemented """ if value is None: value = self.highlight_opacity_edit.value() self._highlight_opacity = value if self.win_parent is not None: self.win_parent.settings.set_highlight_opacity(value) def _background_color(self, title, color_edit, rgb_color_ints, func_name): """helper method for ``on_background_color`` and ``on_background_color2``""" passed, rgb_color_ints, rgb_color_floats = self.on_color( color_edit, rgb_color_ints, title) if passed: if self.win_parent is not None: settings = self.win_parent.settings func_background_color = getattr(settings, func_name) func_background_color(rgb_color_floats) return passed, rgb_color_ints, rgb_color_floats def on_color(self, color_edit, rgb_color_ints, title): """pops a color dialog""" col = QColorDialog.getColor(QtGui.QColor(*rgb_color_ints), self, title) if not col.isValid(): return False, rgb_color_ints, None color_float = col.getRgbF()[:3] # floats color_int = [int(colori * 255) for colori in color_float] assert isinstance(color_float[0], float), color_float assert isinstance(color_int[0], int), color_int color_edit.setStyleSheet("QPushButton {" "background-color: rgb(%s, %s, %s);" % tuple(color_int) + #"border:1px solid rgb(255, 170, 255); " "}") return True, color_int, color_float #--------------------------------------------------------------------------- def on_validate(self): """makes sure that all attributes are valid before doing any actions""" unused_nodes, flag1 = check_patran_syntax(self.nodes_edit, pound=self._nodes_pound) unused_elements, flag2 = check_patran_syntax( self.elements_edit, pound=self._elements_pound) if all([flag1, flag2]): self.out_data['clicked_ok'] = True return True return False def on_show(self): """show the highlight""" passed = self.on_validate() self.parent().mouse_actions.get_grid_selected(self.model_name) if passed and self.win_parent is not None: nodes, unused_flag1 = check_patran_syntax(self.nodes_edit, pound=self._nodes_pound) elements, unused_flag2 = check_patran_syntax( self.elements_edit, pound=self._elements_pound) if len(nodes) == 0 and len(elements) == 0: return False nodes_filtered = np.intersect1d(self.nodes, nodes) elements_filtered = np.intersect1d(self.elements, elements) nnodes = len(nodes_filtered) nelements = len(elements_filtered) if nnodes == 0 and nelements == 0: return False self.on_remove_actors() gui = self.parent() mouse_actions = gui.mouse_actions grid = mouse_actions.get_grid_selected(self.model_name) actors = create_highlighted_actors(gui, grid, all_nodes=self.nodes, nodes=nodes_filtered, set_node_scalars=True, all_elements=self.elements, elements=elements_filtered, set_element_scalars=True) iactor = 0 make_element_labels = True make_node_labels = True if make_node_labels and nnodes: mapper = actors[iactor].GetMapper() mygrid = mapper.GetInput() point_id_filter = get_ids_filter(mygrid, idsname='Ids_points', is_nids=True, is_eids=False) point_id_filter.SetFieldData(1) point_id_filter.SetPointIds(0) point_id_filter.FieldDataOn() label_actor = create_node_labels(point_id_filter, mygrid, gui.rend, label_size=self._label_size) actors.append(label_actor) iactor += 1 if make_element_labels and nelements: mapper = actors[iactor].GetMapper() mygrid = mapper.GetInput() element_id_filter = get_ids_filter(mygrid, idsname='Ids_cells', is_nids=False, is_eids=True) element_id_filter.SetFieldData(1) element_id_filter.SetCellIds(0) element_id_filter.FieldDataOn() # Create labels for cells cell_centers = vtk.vtkCellCenters() cell_centers.SetInputConnection( element_id_filter.GetOutputPort()) cell_mapper = vtk.vtkLabeledDataMapper() cell_mapper.SetInputConnection(cell_centers.GetOutputPort()) cell_mapper.SetLabelModeToLabelScalars() label_actor = vtk.vtkActor2D() label_actor.SetMapper(cell_mapper) actors.append(label_actor) iactor += 1 if actors: add_actors_to_gui(gui, actors, render=True) self.actors = actors return passed def on_remove_actors(self): """removes multiple vtk actors""" gui = self.parent() if gui is not None: if self.nodes_edit.style is not None: self.nodes_edit.style.remove_actors() if self.elements_edit.style is not None: self.elements_edit.style.remove_actors() remove_actors_from_gui(gui, self.actors, render=True, force_render=True) self.actors = [] def on_close(self): """close the window""" self.on_remove_actors() self.out_data['close'] = True self.close()
class MxDataFrameViewer(QWidget): """ Dialog for displaying and editing DataFrame and related objects. Based on the gtabview project (ExtTableView). For more information please see: https://github.com/wavexx/gtabview/blob/master/gtabview/viewer.py Signals ------- sig_option_changed(str, object): Raised if an option is changed. Arguments are name of option and its new value. """ sig_option_changed = Signal(str, object) def __init__(self, parent=None): QWidget.__init__(self, parent) # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.is_series = False self.layout = None # if not data: # data = DataFrame() # self.setup_and_check(data) # mx change def setup_and_check(self, data, title=''): """ Setup DataFrameEditor: return False if data is not supported, True otherwise. Supported types for data are DataFrame, Series and Index. """ self._selection_rec = False self._model = None self.layout = QGridLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.setWindowIcon(ima.icon('arredit')) if title: title = to_text_string(title) + " - %s" % data.__class__.__name__ else: title = _("%s editor") % data.__class__.__name__ if isinstance(data, Series): self.is_series = True data = data.to_frame() elif isinstance(data, Index): data = DataFrame(data) self.setWindowTitle(title) # self.resize(600, 500) # mx change self.hscroll = QScrollBar(Qt.Horizontal) self.vscroll = QScrollBar(Qt.Vertical) # Create the view for the level self.create_table_level() # Create the view for the horizontal header self.create_table_header() # Create the view for the vertical index self.create_table_index() # Create the model and view of the data self.dataModel = DataFrameModel(data, parent=self) # self.dataModel.dataChanged.connect(self.save_and_close_enable) # mx change self.create_data_table() self.layout.addWidget(self.hscroll, 2, 0, 1, 2) self.layout.addWidget(self.vscroll, 0, 2, 2, 1) # autosize columns on-demand self._autosized_cols = set() # Set limit time to calculate column sizeHint to 300ms, # See spyder-ide/spyder#11060 self._max_autosize_ms = 300 self.dataTable.installEventFilter(self) avg_width = self.fontMetrics().averageCharWidth() self.min_trunc = avg_width * 12 # Minimum size for columns self.max_width = avg_width * 64 # Maximum size for columns self.setLayout(self.layout) self.setMinimumSize(400, 300) # Make the dialog act as a window # self.setWindowFlags(Qt.Window) # mx change btn_layout = QHBoxLayout() btn = QPushButton(_("Format")) # disable format button for int type btn_layout.addWidget(btn) btn.clicked.connect(self.change_format) btn = QPushButton(_('Resize')) btn_layout.addWidget(btn) btn.clicked.connect(self.resize_to_contents) bgcolor = QCheckBox(_('Background color')) bgcolor.setChecked(self.dataModel.bgcolor_enabled) bgcolor.setEnabled(self.dataModel.bgcolor_enabled) bgcolor.stateChanged.connect(self.change_bgcolor_enable) btn_layout.addWidget(bgcolor) self.bgcolor_global = QCheckBox(_('Column min/max')) self.bgcolor_global.setChecked(self.dataModel.colum_avg_enabled) self.bgcolor_global.setEnabled(not self.is_series and self.dataModel.bgcolor_enabled) self.bgcolor_global.stateChanged.connect(self.dataModel.colum_avg) btn_layout.addWidget(self.bgcolor_global) btn_layout.addStretch() # mx change # self.btn_save_and_close = QPushButton(_('Save and Close')) # self.btn_save_and_close.setDisabled(True) # self.btn_save_and_close.clicked.connect(self.accept) # btn_layout.addWidget(self.btn_save_and_close) # # self.btn_close = QPushButton(_('Close')) # self.btn_close.setAutoDefault(True) # self.btn_close.setDefault(True) # self.btn_close.clicked.connect(self.reject) # btn_layout.addWidget(self.btn_close) btn_layout.setContentsMargins(4, 4, 4, 4) self.layout.addLayout(btn_layout, 4, 0, 1, 2) self.setModel(self.dataModel) self.resizeColumnsToContents() return True @Slot(QModelIndex, QModelIndex) def save_and_close_enable(self, top_left, bottom_right): """Handle the data change event to enable the save and close button.""" self.btn_save_and_close.setEnabled(True) self.btn_save_and_close.setAutoDefault(True) self.btn_save_and_close.setDefault(True) def create_table_level(self): """Create the QTableView that will hold the level model.""" self.table_level = QTableView() self.table_level.setEditTriggers(QTableWidget.NoEditTriggers) self.table_level.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_level.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_level.setFrameStyle(QFrame.Plain) self.table_level.horizontalHeader().sectionResized.connect( self._index_resized) self.table_level.verticalHeader().sectionResized.connect( self._header_resized) # self.table_level.setItemDelegate(QItemDelegate()) # mx change self.layout.addWidget(self.table_level, 0, 0) self.table_level.setContentsMargins(0, 0, 0, 0) self.table_level.horizontalHeader().sectionClicked.connect( self.sortByIndex) def create_table_header(self): """Create the QTableView that will hold the header model.""" self.table_header = QTableView() self.table_header.verticalHeader().hide() self.table_header.setEditTriggers(QTableWidget.NoEditTriggers) self.table_header.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_header.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_header.setHorizontalScrollMode(QTableView.ScrollPerPixel) self.table_header.setHorizontalScrollBar(self.hscroll) self.table_header.setFrameStyle(QFrame.Plain) self.table_header.horizontalHeader().sectionResized.connect( self._column_resized) # self.table_header.setItemDelegate(QItemDelegate()) # mx change self.layout.addWidget(self.table_header, 0, 1) def create_table_index(self): """Create the QTableView that will hold the index model.""" self.table_index = QTableView() self.table_index.horizontalHeader().hide() self.table_index.setEditTriggers(QTableWidget.NoEditTriggers) self.table_index.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_index.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_index.setVerticalScrollMode(QTableView.ScrollPerPixel) self.table_index.setVerticalScrollBar(self.vscroll) self.table_index.setFrameStyle(QFrame.Plain) self.table_index.verticalHeader().sectionResized.connect( self._row_resized) # self.table_index.setItemDelegate(QItemDelegate()) # mx change self.layout.addWidget(self.table_index, 1, 0) self.table_index.setContentsMargins(0, 0, 0, 0) def create_data_table(self): """Create the QTableView that will hold the data model.""" self.dataTable = DataFrameView(self, self.dataModel, self.table_header.horizontalHeader(), self.hscroll, self.vscroll) self.dataTable.verticalHeader().hide() self.dataTable.horizontalHeader().hide() self.dataTable.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.dataTable.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.dataTable.setHorizontalScrollMode(QTableView.ScrollPerPixel) self.dataTable.setVerticalScrollMode(QTableView.ScrollPerPixel) self.dataTable.setFrameStyle(QFrame.Plain) # self.dataTable.setItemDelegate(QItemDelegate()) # mx change self.layout.addWidget(self.dataTable, 1, 1) self.setFocusProxy(self.dataTable) self.dataTable.sig_sort_by_column.connect(self._sort_update) self.dataTable.sig_fetch_more_columns.connect(self._fetch_more_columns) self.dataTable.sig_fetch_more_rows.connect(self._fetch_more_rows) def sortByIndex(self, index): """Implement a Index sort.""" self.table_level.horizontalHeader().setSortIndicatorShown(True) sort_order = self.table_level.horizontalHeader().sortIndicatorOrder() self.table_index.model().sort(index, sort_order) self._sort_update() def model(self): """Get the model of the dataframe.""" return self._model def _column_resized(self, col, old_width, new_width): """Update the column width.""" self.dataTable.setColumnWidth(col, new_width) self._update_layout() def _row_resized(self, row, old_height, new_height): """Update the row height.""" self.dataTable.setRowHeight(row, new_height) self._update_layout() def _index_resized(self, col, old_width, new_width): """Resize the corresponding column of the index section selected.""" self.table_index.setColumnWidth(col, new_width) self._update_layout() def _header_resized(self, row, old_height, new_height): """Resize the corresponding row of the header section selected.""" self.table_header.setRowHeight(row, new_height) self._update_layout() def _update_layout(self): """Set the width and height of the QTableViews and hide rows.""" h_width = max(self.table_level.verticalHeader().sizeHint().width(), self.table_index.verticalHeader().sizeHint().width()) self.table_level.verticalHeader().setFixedWidth(h_width) self.table_index.verticalHeader().setFixedWidth(h_width) last_row = self._model.header_shape[0] - 1 if last_row < 0: hdr_height = self.table_level.horizontalHeader().height() else: hdr_height = self.table_level.rowViewportPosition(last_row) + \ self.table_level.rowHeight(last_row) + \ self.table_level.horizontalHeader().height() # Check if the header shape has only one row (which display the # same info than the horizontal header). if last_row == 0: self.table_level.setRowHidden(0, True) self.table_header.setRowHidden(0, True) self.table_header.setFixedHeight(hdr_height) self.table_level.setFixedHeight(hdr_height) last_col = self._model.header_shape[1] - 1 if last_col < 0: idx_width = self.table_level.verticalHeader().width() else: idx_width = self.table_level.columnViewportPosition(last_col) + \ self.table_level.columnWidth(last_col) + \ self.table_level.verticalHeader().width() self.table_index.setFixedWidth(idx_width) self.table_level.setFixedWidth(idx_width) self._resizeVisibleColumnsToContents() def _reset_model(self, table, model): """Set the model in the given table.""" old_sel_model = table.selectionModel() table.setModel(model) if old_sel_model: del old_sel_model def setAutosizeLimitTime(self, limit_ms): """Set maximum time to calculate size hint for columns.""" self._max_autosize_ms = limit_ms def setModel(self, model, relayout=True): """Set the model for the data, header/index and level views.""" self._model = model # mx change # sel_model = self.dataTable.selectionModel() # sel_model.currentColumnChanged.connect( # self._resizeCurrentColumnToContents) # mx change self._reset_model(self.dataTable, model) # Asociate the models (level, vertical index and horizontal header) # with its corresponding view. self._reset_model( self.table_level, DataFrameLevelModel(model, self.palette(), self.font())) self._reset_model(self.table_header, DataFrameHeaderModel(model, 0, self.palette())) self._reset_model(self.table_index, DataFrameHeaderModel(model, 1, self.palette())) # Needs to be called after setting all table models if relayout: self._update_layout() def setCurrentIndex(self, y, x): """Set current selection.""" self.dataTable.selectionModel().setCurrentIndex( self.dataTable.model().index(y, x), QItemSelectionModel.ClearAndSelect) def _sizeHintForColumn(self, table, col, limit_ms=None): """Get the size hint for a given column in a table.""" max_row = table.model().rowCount() lm_start = perf_counter() lm_row = 64 if limit_ms else max_row max_width = self.min_trunc for row in range(max_row): v = table.sizeHintForIndex(table.model().index(row, col)) max_width = max(max_width, v.width()) if row > lm_row: lm_now = perf_counter() lm_elapsed = (lm_now - lm_start) * 1000 if lm_elapsed >= limit_ms: break lm_row = int((row / lm_elapsed) * limit_ms) return max_width def _resizeColumnToContents(self, header, data, col, limit_ms): """Resize a column by its contents.""" hdr_width = self._sizeHintForColumn(header, col, limit_ms) data_width = self._sizeHintForColumn(data, col, limit_ms) if data_width > hdr_width: width = min(self.max_width, data_width) elif hdr_width > data_width * 2: width = max(min(hdr_width, self.min_trunc), min(self.max_width, data_width)) else: width = max(min(self.max_width, hdr_width), self.min_trunc) header.setColumnWidth(col, width) def _resizeColumnsToContents(self, header, data, limit_ms): """Resize all the colummns to its contents.""" max_col = data.model().columnCount() if limit_ms is None: max_col_ms = None else: max_col_ms = limit_ms / max(1, max_col) for col in range(max_col): self._resizeColumnToContents(header, data, col, max_col_ms) def eventFilter(self, obj, event): """Override eventFilter to catch resize event.""" if obj == self.dataTable and event.type() == QEvent.Resize: self._resizeVisibleColumnsToContents() return False def _resizeVisibleColumnsToContents(self): """Resize the columns that are in the view.""" index_column = self.dataTable.rect().topLeft().x() start = col = self.dataTable.columnAt(index_column) width = self._model.shape[1] end = self.dataTable.columnAt(self.dataTable.rect().bottomRight().x()) end = width if end == -1 else end + 1 if self._max_autosize_ms is None: max_col_ms = None else: max_col_ms = self._max_autosize_ms / max(1, end - start) while col < end: resized = False if col not in self._autosized_cols: self._autosized_cols.add(col) resized = True self._resizeColumnToContents(self.table_header, self.dataTable, col, max_col_ms) col += 1 if resized: # As we resize columns, the boundary will change index_column = self.dataTable.rect().bottomRight().x() end = self.dataTable.columnAt(index_column) end = width if end == -1 else end + 1 if max_col_ms is not None: max_col_ms = self._max_autosize_ms / max(1, end - start) def _resizeCurrentColumnToContents(self, new_index, old_index): """Resize the current column to its contents.""" if new_index.column() not in self._autosized_cols: # Ensure the requested column is fully into view after resizing self._resizeVisibleColumnsToContents() self.dataTable.scrollTo(new_index) def resizeColumnsToContents(self): """Resize the columns to its contents.""" self._autosized_cols = set() self._resizeColumnsToContents(self.table_level, self.table_index, self._max_autosize_ms) self._update_layout() def change_bgcolor_enable(self, state): """ This is implementet so column min/max is only active when bgcolor is """ self.dataModel.bgcolor(state) self.bgcolor_global.setEnabled(not self.is_series and state > 0) def change_format(self): """ Ask user for display format for floats and use it. This function also checks whether the format is valid and emits `sig_option_changed`. """ format, valid = QInputDialog.getText(self, _('Format'), _("Float formatting"), QLineEdit.Normal, self.dataModel.get_format()) if valid: format = str(format) try: format % 1.1 except: msg = _("Format ({}) is incorrect").format(format) QMessageBox.critical(self, _("Error"), msg) return if not format.startswith('%'): msg = _("Format ({}) should start with '%'").format(format) QMessageBox.critical(self, _("Error"), msg) return self.dataModel.set_format(format) self.sig_option_changed.emit('dataframe_format', format) def get_value(self): """Return modified Dataframe -- this is *not* a copy""" # It is import to avoid accessing Qt C++ object as it has probably # already been destroyed, due to the Qt.WA_DeleteOnClose attribute df = self.dataModel.get_data() if self.is_series: return df.iloc[:, 0] else: return df def _update_header_size(self): """Update the column width of the header.""" self.table_header.resizeColumnsToContents() column_count = self.table_header.model().columnCount() for index in range(0, column_count): if index < column_count: column_width = self.dataTable.columnWidth(index) header_width = self.table_header.columnWidth(index) if column_width > header_width: self.table_header.setColumnWidth(index, column_width) else: self.dataTable.setColumnWidth(index, header_width) else: break def _sort_update(self): """ Update the model for all the QTableView objects. Uses the model of the dataTable as the base. """ self.setModel(self.dataTable.model()) def _fetch_more_columns(self): """Fetch more data for the header (columns).""" self.table_header.model().fetch_more() def _fetch_more_rows(self): """Fetch more data for the index (rows).""" self.table_index.model().fetch_more() def resize_to_contents(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.dataTable.resizeColumnsToContents() self.dataModel.fetch_more(columns=True) self.dataTable.resizeColumnsToContents() self._update_header_size() QApplication.restoreOverrideCursor()