Ejemplo n.º 1
0
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())
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
 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)
Ejemplo n.º 10
0
    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)
Ejemplo n.º 11
0
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()
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
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)
Ejemplo n.º 14
0
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
Ejemplo n.º 15
0
    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)
Ejemplo n.º 16
0
    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)
Ejemplo n.º 17
0
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()
Ejemplo n.º 18
0
    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()
Ejemplo n.º 19
0
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()
Ejemplo n.º 20
0
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()
Ejemplo n.º 22
0
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))
Ejemplo n.º 23
0
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()
Ejemplo n.º 24
0
    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()
Ejemplo n.º 25
0
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()
Ejemplo n.º 26
0
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)
Ejemplo n.º 27
0
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")
Ejemplo n.º 28
0
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()
Ejemplo n.º 29
0
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
Ejemplo n.º 30
0
    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
Ejemplo n.º 31
0
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()
Ejemplo n.º 32
0
    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)
Ejemplo n.º 33
0
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()
Ejemplo n.º 34
0
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()