예제 #1
0
파일: main.py 프로젝트: lnls-sirius/hla
    def get_orbitdetails_widget(self, parent):
        """."""
        grp_bx = QWidget(parent)
        grp_bx.setLayout(QVBoxLayout())

        lbl = CALabel('OfflineOrb:', grp_bx)
        combo = OfflineOrbControl(
            grp_bx, self.device, self.ctrls, prefix=self.prefix, acc=self.acc)
        rules = (
            '[{"name": "EnblRule", "property": "Visible", ' +
            '"expression": "not ch[0]", "channels": [{"channel": "' +
            self.devpref.substitute(propty='SOFBMode-Sts') +
            '", "trigger": true}]}]')
        combo.rules = rules
        lbl.rules = rules
        fbl = QFormLayout()
        grp_bx.layout().addLayout(fbl)
        fbl.addRow(lbl, combo)
        grp_bx.layout().addStretch()

        hbl = QHBoxLayout()
        grp_bx.layout().addLayout(hbl)
        fbl = QFormLayout()
        hbl.addItem(fbl)
        lbl = QLabel('Orbit [Hz]', grp_bx, alignment=Qt.AlignCenter)
        wid = self.create_pair(grp_bx, 'OrbAcqRate')
        fbl.addRow(lbl, wid)
        lbl = QLabel('Kicks [Hz]', grp_bx, alignment=Qt.AlignCenter)
        wid = self.create_pair(grp_bx, 'KickAcqRate')
        fbl.addRow(lbl, wid)

        wid = QWidget(grp_bx)
        wid.setStyleSheet('max-width:6em;')
        hbl.addWidget(wid)
        vbl = QVBoxLayout(wid)
        vbl.setContentsMargins(0, 0, 0, 0)
        lab = QLabel('Sync. Injection', wid, alignment=Qt.AlignCenter)
        vbl.addWidget(lab)
        hbl = QHBoxLayout()
        hbl.setContentsMargins(0, 0, 0, 0)
        vbl.addItem(hbl)
        spt = PyDMStateButton(
            wid, self.devpref.substitute(propty='SyncWithInjection-Sel'))
        rdb = SiriusLedState(
            wid, self.devpref.substitute(propty='SyncWithInjection-Sts'))
        hbl.addWidget(spt)
        hbl.addWidget(rdb)
        grp_bx.layout().addStretch()

        fbl = QFormLayout()
        grp_bx.layout().addLayout(fbl)
        lbl = QLabel('Smooth Method', grp_bx, alignment=Qt.AlignCenter)
        wid = self.create_pair_sel(grp_bx, 'SmoothMethod')
        fbl.addRow(lbl, wid)
        if self.isring:
            lbl = QLabel('Extend Ring', grp_bx, alignment=Qt.AlignCenter)
            wid = self.create_pair(grp_bx, 'RingSize')
            fbl.addRow(lbl, wid)

        return grp_bx
예제 #2
0
파일: status.py 프로젝트: lnls-sirius/hla
    def setupui(self):
        vbl = QVBoxLayout(self)

        tip = 'Configure ' + ('Acquisition' if self.is_orb else 'Correctors')
        pv = 'TrigAcqConfig-Cmd' if self.is_orb else 'CorrConfig-Cmd'
        conf = PyDMPushButton(self,
                              init_channel=self.devpref.substitute(propty=pv),
                              pressValue=1)
        conf.setToolTip(tip)
        conf.setIcon(qta.icon('fa5s.sync'))
        conf.setObjectName('conf')
        conf.setStyleSheet(
            '#conf{min-width:25px; max-width:25px; icon-size:20px;}')
        vbl.addWidget(conf)

        pv = 'Orb' if self.is_orb else 'Corr'
        pdm_led = SiriusLedAlert(
            self, self.devpref.substitute(propty=pv + 'Status-Mon'))

        hbl = QHBoxLayout()
        hbl.setSpacing(9)
        hbl.addWidget(pdm_led)
        hbl.addWidget(QLabel('Global Status', self))
        hbl.addStretch()
        hbl.addWidget(conf)
        vbl.addItem(hbl)

        grpbx = self.creategroupbox()
        vbl.addWidget(grpbx)
예제 #3
0
class PMFlowAreaWidget(QWidget):
    def __init__(self):
        super().__init__()
        from pmgwidgets import PMFlowLayout

        self.outer_layout = QVBoxLayout()

        self.flow_layout = PMFlowLayout()
        self.setMinimumWidth(100)
        self.outer_layout.addLayout(self.flow_layout)
        spacer_v = QSpacerItem(20, 20, QSizePolicy.Minimum,
                               QSizePolicy.Expanding)

        self.outer_layout.addItem(spacer_v)
        self.setLayout(self.outer_layout)

    def add_widget(self, w: 'QWidget'):
        self.flow_layout.add_widget(w)

    def setup_ui(self):
        if hasattr(self.widget(), 'setup_ui'):
            self.widget().setup_ui()

    def resizeEvent(self, a0: 'QResizeEvent') -> None:
        super().resizeEvent(a0)
        layout: 'PMFlowLayout' = self.flow_layout
        layout.on_resize()
예제 #4
0
    def setupui(self):
        if self.position:
            text = 'Positions'
            names = ('X', 'Y', 'Q', 'SUM')
            colors = ('blue', 'red', 'green', 'black')
        else:
            text = 'Antennas'
            names = ('A', 'B', 'C', 'D')
            colors = ('blue', 'red', 'green', 'magenta')

        vbl = QVBoxLayout(self)

        graph = GraphWave(self,
                          prefix=self.prefix,
                          bpm=self.bpm,
                          data_prefix=self.data_prefix)
        graph.setLabel('left', text='Amplitude', units='a.u.')
        graph.setLabel('bottom', text='Frequency', units='Hz')
        for name, cor in zip(names, colors):
            opts = dict(y_channel=self.get_pvname(name + 'FFTData-RB.AMP'),
                        x_channel=self.get_pvname(name + 'FFTFreq-RB.AVAL'),
                        name=text[:3] + name,
                        color=cor,
                        lineStyle=1,
                        lineWidth=1)  # NOTE: If > 1: very low performance
            graph.addChannel(**opts)
        vbl.addWidget(graph)

        graph = GraphWave(self,
                          prefix=self.prefix,
                          bpm=self.bpm,
                          data_prefix=self.data_prefix)
        graph.setLabel('left', text='Phase', units='rad')
        graph.setLabel('bottom', text='Frequency', units='Hz')
        for name, cor in zip(names, colors):
            opts = dict(y_channel=self.get_pvname(name + 'FFTData-RB.PHA'),
                        x_channel=self.get_pvname(name + 'FFTFreq-RB.AVAL'),
                        name=text[:3] + name,
                        color=cor,
                        lineStyle=1,
                        lineWidth=1)  # NOTE: If > 1: very low performance
            graph.addChannel(**opts)
        vbl.addWidget(graph)

        hbl = QHBoxLayout()
        hbl.addStretch()
        vbl.addItem(hbl)
        pb = QPushButton('Open FFT Config', self)
        hbl.addWidget(pb)
        hbl.addStretch()
        Window = create_window_from_widget(FFTConfig,
                                           title=self.bpm + ': FFT Config')
        util.connect_window(pb,
                            Window,
                            parent=self,
                            prefix=self.prefix,
                            bpm=self.bpm,
                            data_prefix=self.data_prefix,
                            position=self.position)
예제 #5
0
class EFMtoolDialog(QDialog):
    """A dialog to set up EFM calculation"""

    def __init__(self, appdata: CnaData, centralwidget):
        QDialog.__init__(self)
        self.setWindowTitle("Elementary Flux Mode Computation")

        self.appdata = appdata
        self.centralwidget = centralwidget

        self.layout = QVBoxLayout()

        l1 = QHBoxLayout()
        self.constraints = QCheckBox("consider 0 in current scenario as off")
        self.constraints.setCheckState(Qt.Checked)
        l1.addWidget(self.constraints)
        self.layout.addItem(l1)

        lx = QHBoxLayout()
        self.button = QPushButton("Compute")
        self.cancel = QPushButton("Close")
        lx.addWidget(self.button)
        lx.addWidget(self.cancel)
        self.layout.addItem(lx)

        self.setLayout(self.layout)

        # Connecting the signal
        self.cancel.clicked.connect(self.reject)
        self.button.clicked.connect(self.compute)

    def compute(self):
        self.setCursor(Qt.BusyCursor)
        (ems, scenario) = cnapy.core.efm_computation(
            self.appdata.project.cobra_py_model, self.appdata.project.scen_values,
            self.constraints.checkState() == Qt.Checked)

        self.setCursor(Qt.ArrowCursor)
        if ems is None:
            QMessageBox.information(self, 'No modes',
                                    'An error occured and modes have not been calculated.')
        else:
            if len(ems) == 0:
                QMessageBox.information(self, 'No modes',
                                        'No elementary modes exist.')
            else:
                print(scenario)
                self.appdata.project.modes = ems
                self.centralwidget.mode_navigator.current = 0
                self.centralwidget.mode_navigator.scenario = scenario
                self.centralwidget.mode_navigator.title.setText(
                    "Mode Navigation")
                self.centralwidget.update_mode()
예제 #6
0
 def _create_prop_widget(self, name, parent, wids, align_ver=True):
     pwid = QWidget(parent)
     vbl = QVBoxLayout(pwid)
     lab = QLabel(name)
     lab.setAlignment(Qt.AlignCenter)
     vbl.addWidget(lab)
     hbl = QHBoxLayout()
     hbl.setAlignment(Qt.AlignCenter)
     vbl.addItem(hbl)
     for wid in wids:
         wid.setParent(pwid)
         hbl.addWidget(wid)
     return pwid
예제 #7
0
    def setupui(self):
        vbl = QVBoxLayout(self)
        self.stack = QStackedWidget(self)
        vbl.addWidget(self.stack)

        rbA = QRadioButton('Antennas', self)
        rbP = QRadioButton('Positions', self)
        rbA.toggled.connect(_part(self.toggle_button, 0))
        rbP.toggled.connect(_part(self.toggle_button, 1))
        self.radio_buttons.append(rbA)
        self.radio_buttons.append(rbP)
        rbA.setChecked(True)
        hbl = QHBoxLayout()
        hbl.addStretch()
        hbl.addWidget(rbA)
        hbl.addStretch()
        hbl.addWidget(rbP)
        hbl.addStretch()
        vbl.addItem(hbl)

        # ##### Antennas Widget ######
        stack1 = QWidget(self.stack)
        self.stack.addWidget(stack1)
        vbl = QVBoxLayout(stack1)

        graph = self.create_graph(stack1, 'ant')
        vbl.addWidget(graph)
        stats = self.create_statistics(stack1, 'ant')
        vbl.addWidget(stats)

        # ##### Position and Amplitudes Widget ######
        stack2 = QWidget(self.stack)
        self.stack.addWidget(stack2)
        vbl = QVBoxLayout(stack2)

        graph = self.create_graph(stack2, 'pos')
        vbl.addWidget(graph)
        graph = self.create_graph(stack2, 'amp')
        vbl.addWidget(graph)

        self.setStyleSheet("""
            #SinglePassDataGraph{
                min-width:48em;
                min-height:24em;
            }
            QLabel{
                min-width:6em; max-width:6em;
                min-height:1.5em; max-height:1.5em;
            }""")
예제 #8
0
    def __init__(self, *args, **kwargs):
        super(MessageCheckBox, self).__init__(*args, **kwargs)

        self._checkbox = QCheckBox()

        # Set layout to include checkbox
        size = 9
        check_layout = QVBoxLayout()
        check_layout.addItem(QSpacerItem(size, size))
        check_layout.addWidget(self._checkbox, 0, Qt.AlignRight)
        check_layout.addItem(QSpacerItem(size, size))

        # Access the Layout of the MessageBox to add the Checkbox
        layout = self.layout()
        layout.addLayout(check_layout, 1, 1)
예제 #9
0
    def __init__(self, *args, **kwargs):
        super(MessageCheckBox, self).__init__(*args, **kwargs)

        self._checkbox = QCheckBox()

        # Set layout to include checkbox
        size = 9
        check_layout = QVBoxLayout()
        check_layout.addItem(QSpacerItem(size, size))
        check_layout.addWidget(self._checkbox, 0, Qt.AlignRight)
        check_layout.addItem(QSpacerItem(size, size))

        # Access the Layout of the MessageBox to add the Checkbox
        layout = self.layout()
        layout.addLayout(check_layout, 1, 1)
예제 #10
0
파일: respmat.py 프로젝트: lnls-sirius/hla
    def setupui(self):
        vbl = QVBoxLayout(self)

        lab = QLabel('Response Matrix', self, alignment=Qt.AlignCenter)
        lab.setStyleSheet("font-weight: bold;")
        vbl.addWidget(lab)

        graph = Graph(self)
        vbl.addWidget(graph)

        self.spbox = QSpinBoxPlus(self)
        self.spbox.setMaximum(1000)
        self.spbox.setValue(80)
        self.spbox.setKeyboardTracking(False)
        self.spbox.editingFinished.connect(self._update_graph)

        hbl = QHBoxLayout()
        vbl.addItem(hbl)
        hbl.addWidget(QLabel('Lines spacing:', self))
        hbl.addWidget(self.spbox)
        hbl.addStretch()

        graph.setShowLegend(False)
        graph.setLabel('bottom', text='BPM Position', units='m')
        graph.setLabel('left', text='Matrix', units='m/rad')
        for i in range(self._csorb.nr_corrs):
            color = 'blue'
            if i >= self._csorb.nr_ch:
                color = 'red'
            if i >= self._csorb.nr_ch + self._csorb.nr_cv:
                color = 'black'
            opts = dict(
                y_channel='',
                x_channel='',  # self.devpref.substitute(propty='BPMPosS-Mon'),
                name='',
                color=color,
                redraw_mode=2,
                lineStyle=1,
                lineWidth=3,
                symbol='o',
                symbolSize=10)
            graph.addChannel(**opts)
        graph.plotItem.scene().sigMouseMoved.connect(self._show_tooltip)
        self.graph = graph
예제 #11
0
class RenameMapDialog(QDialog):
    """A dialog to rename maps"""
    def __init__(self, appdata: CnaData, central_widget):
        QDialog.__init__(self)
        self.setWindowTitle("Rename map")

        self.appdata = appdata
        self.central_widget = central_widget
        self.layout = QVBoxLayout()
        h1 = QHBoxLayout()
        label = QLabel("Enter new map name")
        self.layout.addWidget(label)
        self.idx = self.central_widget.map_tabs.currentIndex()
        self.old_name = self.central_widget.map_tabs.tabText(self.idx)

        self.name_field = QLineEdit(self.old_name)
        h1.addWidget(self.name_field)
        self.layout.addItem(h1)

        l2 = QHBoxLayout()
        self.button = QPushButton("Rename")
        self.cancel = QPushButton("Cancel")
        l2.addWidget(self.button)
        l2.addWidget(self.cancel)
        self.layout.addItem(l2)
        self.setLayout(self.layout)

        # Connecting the signal
        self.cancel.clicked.connect(self.reject)
        self.button.clicked.connect(self.apply)

    def apply(self):

        new_name = self.name_field.text()
        if not new_name in self.appdata.project.maps.keys():
            self.appdata.project.maps[
                new_name] = self.appdata.project.maps.pop(self.old_name)
            self.central_widget.map_tabs.setTabText(self.idx, new_name)
            m = self.central_widget.map_tabs.widget(self.idx)
            m.name = new_name
        self.accept()
예제 #12
0
    def setupui(self):
        vbl = QVBoxLayout(self)
        lab = QLabel('<h2>BPMs List</h2>', alignment=Qt.AlignCenter)
        vbl.addWidget(lab)
        vbl.addSpacing(20)

        hbl = QHBoxLayout()
        search = QLineEdit(parent=self)
        search.setPlaceholderText("Search for BPMs...")
        search.textEdited.connect(self._filter_bpms)
        hbl.addWidget(search)
        hbl.addStretch()
        self.btnautorange = QPushButton('Auto Range graphics', self)
        hbl.addWidget(self.btnautorange)
        vbl.addItem(hbl)

        sa_class = get_custom_widget_class(QScrollArea)
        scarea = sa_class(self)
        scarea.setSizeAdjustPolicy(scarea.AdjustToContents)
        scarea.setWidgetResizable(True)

        wid = QWidget()
        wid.setObjectName('scrollarea')
        wid.setStyleSheet('#scrollarea {background-color: transparent;}')
        gdl = QGridLayout(wid)
        gdl.setSpacing(15)
        for i, bpm in enumerate(sorted(self.bpm_dict.keys())):
            widb = QWidget(wid)
            vbl2 = QVBoxLayout(widb)
            vbl2.addWidget(
                QLabel('<h3>' + bpm + '</h3>', alignment=Qt.AlignCenter))
            wbpm = self.create_graph(widb, bpm=bpm, typ='ant')
            vbl2.addWidget(wbpm)

            gdl.addWidget(widb, i // 3, i % 3)
            self.bpm_dict[bpm] = widb
        self.gdl = gdl
        vbl.addWidget(scarea)
        scarea.setWidget(wid)
        self.scarea = scarea
예제 #13
0
    def __init__(self, *args, **kwargs):
        super(MessageCheckBox, self).__init__(*args, **kwargs)

        self._checkbox = QCheckBox()

        # Set layout to include checkbox
        size = 9
        check_layout = QVBoxLayout()
        check_layout_h = QHBoxLayout()
        check_layout.addItem(QSpacerItem(size, size))
        check_layout_h.addStretch()
        check_layout_h.addWidget(self._checkbox, 1, Qt.AlignRight)
        check_layout.addLayout(check_layout_h)
        check_layout.addItem(QSpacerItem(size, size))

        # Access the Layout of the MessageBox to add the Checkbox
        layout = self.layout()

        if PYQT4:
            grid_position = 1
        else:
            grid_position = 2

        layout.addLayout(check_layout, 1, grid_position)
예제 #14
0
    def _setupUi(self):
        label = QLabel('<h3>' + self.scrn_prefix + ' Calibration</h3>',
                       self,
                       alignment=Qt.AlignCenter)

        positioning = QGroupBox('Positioning', self)
        positioning.setStyleSheet("""
            .QLabel{
                min-width:16em;\nmax-width:16em;
                qproperty-alignment: AlignRight;\n}""")
        positioning.setLayout(self._setupPositionLayout())

        LED = QGroupBox('LED Brightness', self)
        LED.setStyleSheet("""
            .QLabel{
                min-width:11em;\nmax-width:11em;
                qproperty-alignment: AlignRight;\n}""")
        LED.setLayout(self._setupLEDLayout())

        Img = QGroupBox('Statistics Unit Conversion (Pixels→mm)', self)
        Img.setStyleSheet("""
            .QLabel{
                min-width:12em;\nmax-width:12em;
                qproperty-alignment: AlignRight;\n}""")
        Img.setLayout(self._setupImageCalibLayout())

        vlay = QVBoxLayout()
        vlay.addWidget(label)
        vlay.addItem(
            QSpacerItem(1, 10, QSzPlcy.Fixed, QSzPlcy.MinimumExpanding))
        vlay.addWidget(positioning)
        vlay.addItem(
            QSpacerItem(1, 10, QSzPlcy.Fixed, QSzPlcy.MinimumExpanding))
        vlay.addWidget(LED)
        vlay.addItem(
            QSpacerItem(1, 10, QSzPlcy.Fixed, QSzPlcy.MinimumExpanding))
        vlay.addWidget(Img)
        self.setLayout(vlay)
예제 #15
0
    def setupui(self):
        """."""
        vbl = QVBoxLayout(self)
        vbl.setAlignment(Qt.AlignCenter)

        graphx = self.uigetgraph('x', (45, 15))
        graphy = self.uigetgraph('y', (45, 15))
        suf = 'Orbit' if self.is_orb else 'Correctors'
        lab = QLabel('<h2>Horizontal ' + suf + '</h2>',
                     self,
                     alignment=Qt.AlignLeft)
        lab.setStyleSheet("""min-height:1.5em; max-height:1.5em;""")
        self.hbl_nameh = QHBoxLayout()
        vbl.addItem(self.hbl_nameh)
        self.hbl_nameh.addWidget(lab)
        self.hbl_nameh.addStretch(1)
        vbl.addWidget(graphx)
        vbl.addSpacing(30)
        lab = QLabel('<h2>Vertical ' + suf + '</h2>',
                     self,
                     alignment=Qt.AlignLeft)
        lab.setStyleSheet("""min-height:1.5em; max-height:1.5em;""")
        self.hbl_namev = QHBoxLayout()
        vbl.addItem(self.hbl_namev)
        self.hbl_namev.addWidget(lab)
        self.hbl_namev.addStretch(1)
        vbl.addWidget(graphy)
        self.graph = {'x': graphx, 'y': graphy}

        self.hbl = QHBoxLayout()
        vbl.addItem(self.hbl)
        self.hbl.addStretch(1)
        for i, _ in enumerate(self.line_names):
            grpbx = self.uicreate_groupbox(i)
            grpbx.setObjectName('GroupBox' + str(i))
            self.hbl.addWidget(grpbx)
            self.hbl.addStretch(1)
예제 #16
0
class FunctionDisplay(QGroupBox):
    """
    Display controls for an annotated function in a QGroupBox.

    In order to display function arguments in the user interface, the class
    must be aware of what the type is of each of the parameters. Instead of
    requiring a user to enter this information manually, the class takes
    advantage of the function annotation language described in PEP 3107. This
    allows us to quickly create the appropriate widget for the given parameter
    based on the type.

    If a function parameter is not given an annotation, we will attempt to
    infer it from the default value if available. If this is not possible, and
    the type is not specified in the ``annotations`` dictionary an exception
    will be raised.

    The created user interface consists of a button to execute the function,
    the required parameters are always displayed beneath the button, and
    a :class:`.TogglePanel` object that toggles the view of the optional
    parameters below.

    Attributes
    ----------
    accepted_types : list
        List of types FunctionDisplay can create widgets for.

    Parameters
    ----------
    func : callable

    name : str, optional
        Name to label the box with, by default this will be the function
        meeting.

    annotations : dict, optional
        If the function your are creating a display for is not annotated, you
        may manually supply types for parameters by passing in a dictionary of
        name to type mapping.

    hide_params : list, optional
        List of parameters to exclude from the display. These should have
        appropriate defaults. By default, ``self``, ``args`` and ``kwargs`` are
        all excluded.

    parent : QWidget, optional
    """
    accepted_types = [bool, str, int, float]

    def __init__(self, func, name=None, annotations=None,
                 hide_params=None, parent=None):
        # Function information
        self.func = func
        self.signature = inspect.signature(func)
        self.name = name or self.func.__name__
        # Initialize parent
        super().__init__('{} Parameters'.format(clean_attr(self.name)),
                         parent=parent)
        # Ignore certain parameters, args and kwargs by default
        self.hide_params = ['self', 'args', 'kwargs']
        if hide_params:
            self.hide_params.extend(hide_params)
        # Create basic layout
        self._layout = QVBoxLayout()
        self._layout.setSpacing(2)
        self.setLayout(self._layout)
        # Create an empty list to fill later with parameter widgets
        self.param_controls = list()
        # Add our button to execute the function
        self.execute_button = QPushButton()

        self.docs = {'summary': func.__doc__ or '',
                     'params': {}
                     }

        if func.__doc__ is not None:
            try:
                self.docs.update(**parse_numpy_docstring(func.__doc__))
            except Exception as ex:
                logger.warning('Unable to parse docstring for function %s: %s',
                               name, ex, exc_info=ex)

        self.execute_button.setToolTip(self.docs['summary'])

        self.execute_button.setText(clean_attr(self.name))
        self.execute_button.clicked.connect(self.execute)
        self._layout.addWidget(self.execute_button)
        # Add a panel for the optional parameters
        self.optional = TogglePanel("Optional Parameters")
        self.optional.contents = QWidget()
        self.optional.contents.setLayout(QVBoxLayout())
        self.optional.contents.layout().setSpacing(2)
        self.optional.layout().addWidget(self.optional.contents)
        self.optional.show_contents(False)
        self._layout.addWidget(self.optional)
        self._layout.addItem(QSpacerItem(10, 5, vPolicy=QSizePolicy.Expanding))
        # Create parameters from function signature
        annotations = annotations or dict()
        for param in [param for param in self.signature.parameters.values()
                      if param.name not in self.hide_params]:
            logger.debug("Adding parameter %s ", param.name)
            # See if we received a manual annotation for this parameter
            if param.name in annotations:
                _type = annotations[param.name]
                logger.debug("Found manually specified type %r",
                             _type.__name__)
            # Try and get the type from the function annotation
            elif param.annotation != inspect._empty:
                _type = param.annotation
                logger.debug("Found annotated type %r ",
                             _type.__name__)
            # Try and get the type from the default value
            elif param.default != inspect._empty:
                _type = type(param.default)
                logger.debug("Gathered type %r from parameter default ",
                             _type.__name__)
            # If we don't have a default value or an annotation,
            # we can not make a widget for this parameter. Since
            # this is a required variable (no default), the function
            # will not work without it. Raise an Exception
            else:
                raise TypeError("Parameter {} has an unspecified "
                                "type".format(param.name))

            # Add our parameter
            self.add_parameter(param.name, _type, default=param.default)
        # Hide optional parameter widget if there are no such parameters
        if not self.optional_params:
            self.optional.hide()

    @property
    def required_params(self):
        """
        Required parameters.
        """
        parameters = self.signature.parameters
        return [param.parameter for param in self.param_controls
                if parameters[param.parameter].default == inspect._empty]

    @property
    def optional_params(self):
        """
        Optional parameters.
        """
        parameters = self.signature.parameters
        return [param.parameter for param in self.param_controls
                if parameters[param.parameter].default != inspect._empty]

    @Slot()
    def execute(self):
        """
        Execute :attr:`.func`.

        This takes the parameters configured by the :attr:`.param_controls`
        widgets and passes them into the given callable. All generated
        exceptions are captured and logged.
        """
        logger.info("Executing %s ...", self.name)
        # If our function does not take any argument
        # just pass it on. Otherwise, collect information
        # from the appropriate widgets
        if not self.signature.parameters:
            func = self.func
        else:
            kwargs = dict()
            # Gather information from parameter widgets
            for button in self.param_controls:
                logger.debug("Gathering parameters for %s ...",
                             button.parameter)
                val = button.get_param_value()
                logger.debug("Received %s", val)
                # Watch for NaN values returned from widgets
                # This indicates that there was improper information given
                if np.isnan(val):
                    logger.error("Invalid information supplied for %s "
                                 "parameter", button.parameter)
                    return
                kwargs[button.parameter] = val
            # Button up function call with partial to try below
            func = partial(self.func, **kwargs)
        try:
            # Execute our function
            func()
        except Exception:
            logger.exception("Exception while executing function")
        else:
            logger.info("Operation Complete")

    def add_parameter(self, name, _type, default=inspect._empty, tooltip=None):
        """
        Add a parameter to the function display.

        Parameters
        ----------
        name : str
            Parameter name.

        _type : type
            Type of variable that we are expecting the user to input.

        default : any, optional
            Default value for the parameter.

        tooltip : str, optional
            Tooltip to use for the control widget.  If not specified, docstring
            parameter information will be used if available to generate a
            default.

        Returns
        -------
        widget : QWidget
            The generated widget.
        """
        if tooltip is None:
            tooltip_header = f'{name} - {_type.__name__}'
            tooltip = [
                tooltip_header,
                '-' * len(tooltip_header)
            ]

            if default != inspect._empty:
                tooltip.append(f'Default: {default}')

            try:
                doc_param = self.docs['params'][name]
            except KeyError:
                logger.debug('Parameter information is not available '
                             'for %s(%s)', self.name, name)
            else:
                if doc_param:
                    tooltip.extend(doc_param)

            # If the tooltip is just the header, remove the dashes underneath:
            if len(tooltip) == 2:
                tooltip = tooltip[:1]
            tooltip = '\n'.join(tooltip)

        # Create our parameter control widget
        # QCheckBox field
        if _type == bool:
            cntrl = ParamCheckBox(name, default=default)
        else:
            # Check if this is a valid type
            if _type not in self.accepted_types:
                raise TypeError("Parameter {} has type {} which can not "
                                "be represented in a widget"
                                "".format(name, _type.__name__))
            # Create our QLineEdit
            cntrl = ParamLineEdit(name, default=default, _type=_type)
        # Add our button to the widget
        # If it is required add it above the panel so that it is always
        # visisble. Otherwise, add it to the hideable panel
        self.param_controls.append(cntrl)
        if default == inspect._empty:
            self.layout().insertWidget(len(self.required_params), cntrl)
        else:
            # If this is the first optional parameter,
            # show the whole optional panel
            if self.optional.isHidden():
                self.optional.show()
            # Add the control widget to our contents
            self.optional.contents.layout().addWidget(cntrl)

        cntrl.param_label.setToolTip(tooltip)
        return cntrl

    def sizeHint(self):
        """Suggested size."""
        return QSize(175, 100)
예제 #17
0
    def setupui(self):
        vbl = QVBoxLayout(self)
        lab = QLabel('<h2>' + self.bpm + ' Settings</h2>')
        lab.setAlignment(Qt.AlignCenter)
        vbl.addWidget(lab)
        vbl.addSpacing(10)

        hbl = QHBoxLayout()
        hbl.setSpacing(15)
        hbl.addStretch()
        grpbx = CustomGroupBox('Status', self)
        gdl = QGridLayout(grpbx)
        props = (('asyn.CNCT', 'Connected'), ('asyn.ENBL', 'Enabled'),
                 ('RFFEasyn.CNCT', 'RFFE Connected'),
                 ('RFFEasyn.ENBL', 'RFFE Enabled'), ('ADCAD9510PllStatus-Mon',
                                                     'Clock Synched'))
        for i, prop in enumerate(props):
            led = SiriusLedState(grpbx, init_channel=self.get_pvname(prop[0]))
            led.setOffColor(led.Red)
            lab = QLabel(prop[1], grpbx)
            gdl.addWidget(led, i, 0)
            gdl.addWidget(lab, i, 1)
        hbl.addWidget(grpbx)
        hbl.addStretch()

        grpbx = CustomGroupBox('Advanced Settings', self)
        vbl2 = QVBoxLayout(grpbx)
        vbl2.setSpacing(10)
        pbt = QPushButton('Software')
        Window = create_window_from_widget(AdvancedSettings,
                                           title=self.bpm +
                                           ': Advanced Settings')
        util.connect_window(pbt,
                            Window,
                            parent=grpbx,
                            prefix=self.prefix,
                            bpm=self.bpm)
        vbl2.addWidget(pbt)
        pbt = QPushButton('Hardware')
        Window = create_window_from_widget(HardwareSettings,
                                           title=self.bpm +
                                           ': Hardware Settings')
        util.connect_window(pbt,
                            Window,
                            parent=grpbx,
                            prefix=self.prefix,
                            bpm=self.bpm)
        vbl2.addWidget(pbt)
        hbl.addWidget(grpbx)
        hbl.addStretch()
        vbl.addItem(hbl)
        vbl.addSpacing(20)
        vbl.addStretch()

        grpbx = self._create_formlayout_groupbox(
            'Offset Parameters', (('PosQOffset-SP', 'Offset PosQ'),
                                  ('PosXOffset-SP', 'Offset PosX'),
                                  ('PosYOffset-SP', 'Offset PosY')))
        vbl.addWidget(grpbx)
        vbl.addSpacing(20)
        vbl.addStretch()
        grpbx = self._create_formlayout_groupbox('Gain Parameters',
                                                 (('PosKq-SP', 'Gain PosQ'),
                                                  ('PosKsum-SP', 'Gain Sum'),
                                                  ('PosKx-SP', 'Gain PosX'),
                                                  ('PosKy-SP', 'Gain PosY')))
        vbl.addWidget(grpbx)
        vbl.addSpacing(20)
        vbl.addStretch()
        grpbx = self._create_formlayout_groupbox(
            'Informations', (('INFOHarmonicNumber-SP', 'Harmonic Number'),
                             ('INFOFOFBRate-SP', 'FOFB Rate'),
                             ('INFOMONITRate-SP', 'Monitor Rate'),
                             ('INFOTBTRate-SP', 'TbT Rate'),
                             ('RFFEAtt-SP', 'RFFE Attenuation')))
        vbl.addWidget(grpbx)
        vbl.addSpacing(20)
        vbl.addStretch()
예제 #18
0
class ModelInfo(QWidget):
    """A widget that shows infos about the model"""
    def __init__(self, appdata: CnaData):
        QWidget.__init__(self)
        self.appdata = appdata

        self.layout = QVBoxLayout()
        label = QLabel("Description")
        self.layout.addWidget(label)
        self.description = QTextEdit()
        self.description.setPlaceholderText("Enter a project description")
        self.layout.addWidget(self.description)

        h1 = QHBoxLayout()

        label = QLabel("Optimization direction")
        h1.addWidget(label)
        self.opt_direction = QComboBox()
        self.opt_direction.insertItem(1, "minimize")
        self.opt_direction.insertItem(2, "maximize")
        h1.addWidget(self.opt_direction)
        self.layout.addItem(h1)

        self.setLayout(self.layout)

        self.description.textChanged.connect(self.description_changed)
        self.opt_direction.currentTextChanged.connect(
            self.opt_direction_changed)

        self.update()

    def update(self):
        if "description" in self.appdata.project.meta_data:
            description = self.appdata.project.meta_data["description"]
        else:
            description = ""

        self.description.textChanged.disconnect(self.description_changed)
        self.description.setText(description)

        self.description.textChanged.connect(self.description_changed)

        x = self.appdata.project.cobra_py_model.objective_direction

        self.opt_direction.currentTextChanged.disconnect(
            self.opt_direction_changed)

        if x == "max":
            self.opt_direction.setCurrentIndex(1)
        elif x == "min":
            self.opt_direction.setCurrentIndex(0)

        self.opt_direction.currentTextChanged.connect(
            self.opt_direction_changed)

    def description_changed(self):
        self.appdata.project.meta_data[
            "description"] = self.description.toPlainText()
        self.appdata.window.unsaved_changes()

    def opt_direction_changed(self):

        if self.opt_direction.currentIndex() == 0:
            self.appdata.project.cobra_py_model.objective_direction = "min"
            self.optimizationDirectionChanged.emit("min")
        if self.opt_direction.currentIndex() == 1:
            self.appdata.project.cobra_py_model.objective_direction = "max"
            self.optimizationDirectionChanged.emit("max")

    optimizationDirectionChanged = Signal(str)
예제 #19
0
    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)
예제 #20
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")
예제 #21
0
class PyChopGui(QMainWindow):
    """
    GUI Class using PyQT for PyChop to help users plan inelastic neutron experiments
    at spallation sources by calculating the resolution and flux at a given neutron energies.
    """

    instruments = {}
    choppers = {}
    minE = {}
    maxE = {}

    def __init__(self):
        super(PyChopGui, self).__init__()
        self.folder = os.path.dirname(sys.modules[self.__module__].__file__)
        for fname in os.listdir(self.folder):
            if fname.endswith('.yaml'):
                instobj = Instrument(os.path.join(self.folder, fname))
                self.instruments[instobj.name] = instobj
                self.choppers[instobj.name] = instobj.getChopperNames()
                self.minE[instobj.name] = max([instobj.emin, 0.01])
                self.maxE[instobj.name] = instobj.emax
        self.drawLayout()
        self.setInstrument(list(self.instruments.keys())[0])
        self.resaxes_xlim = 0
        self.qeaxes_xlim = 0
        self.isFramePlotted = 0

    def setInstrument(self, instname):
        """
        Defines the instrument parameters by the name of the instrument.
        """
        self.engine = self.instruments[str(instname)]
        self.tabs.setTabEnabled(self.tdtabID, False)
        self.widgets['ChopperCombo']['Combo'].clear()
        self.widgets['FrequencyCombo']['Combo'].clear()
        self.widgets['FrequencyCombo']['Label'].setText('Frequency')
        self.widgets['PulseRemoverCombo']['Combo'].clear()
        for item in self.choppers[str(instname)]:
            self.widgets['ChopperCombo']['Combo'].addItem(item)
        rep = self.engine.moderator.source_rep
        maxfreq = self.engine.chopper_system.max_frequencies
        # At the moment, the GUI only supports up to two independent frequencies
        if not hasattr(maxfreq, '__len__') or len(maxfreq) == 1:
            self.widgets['PulseRemoverCombo']['Combo'].hide()
            self.widgets['PulseRemoverCombo']['Label'].hide()
            for fq in range(
                    rep,
                (maxfreq[0] if hasattr(maxfreq, '__len__') else maxfreq) + 1,
                    rep):
                self.widgets['FrequencyCombo']['Combo'].addItem(str(fq))
            if hasattr(self.engine.chopper_system, 'frequency_names'):
                self.widgets['FrequencyCombo']['Label'].setText(
                    self.engine.chopper_system.frequency_names[0])
        else:
            self.widgets['PulseRemoverCombo']['Combo'].show()
            self.widgets['PulseRemoverCombo']['Label'].show()
            if hasattr(self.engine.chopper_system, 'frequency_names'):
                for idx, chp in enumerate([
                        self.widgets['FrequencyCombo']['Label'],
                        self.widgets['PulseRemoverCombo']['Label']
                ]):
                    chp.setText(
                        self.engine.chopper_system.frequency_names[idx])
            for fq in range(rep, maxfreq[0] + 1, rep):
                self.widgets['FrequencyCombo']['Combo'].addItem(str(fq))
            for fq in range(rep, maxfreq[1] + 1, rep):
                self.widgets['PulseRemoverCombo']['Combo'].addItem(str(fq))
        if len(self.engine.chopper_system.choppers) > 1:
            self.widgets['MultiRepCheck'].setEnabled(True)
            self.tabs.setTabEnabled(self.tdtabID, True)
        else:
            self.widgets['MultiRepCheck'].setEnabled(False)
            self.widgets['MultiRepCheck'].setChecked(False)
        self.widgets['Chopper2Phase']['Edit'].hide()
        self.widgets['Chopper2Phase']['Label'].hide()
        if self.engine.chopper_system.isPhaseIndependent:
            self.widgets['Chopper2Phase']['Edit'].show()
            self.widgets['Chopper2Phase']['Label'].show()
            self.widgets['Chopper2Phase']['Edit'].setText(
                str(self.engine.chopper_system.defaultPhase[0]))
            self.widgets['Chopper2Phase']['Label'].setText(
                self.engine.chopper_system.phaseNames[0])
            # Special case for MERLIN - hide phase control from normal users
            if 'MERLIN' in str(instname) and not self.instSciAct.isChecked():
                self.widgets['Chopper2Phase']['Edit'].hide()
                self.widgets['Chopper2Phase']['Label'].hide()
        self.engine.setChopper(
            str(self.widgets['ChopperCombo']['Combo'].currentText()))
        self.engine.setFrequency(
            float(self.widgets['FrequencyCombo']['Combo'].currentText()))
        val = self.flxslder.val * self.maxE[self.engine.instname] / 100
        self.flxedt.setText('%3.2f' % (val))
        nframe = self.engine.moderator.n_frame if hasattr(
            self.engine.moderator, 'n_frame') else 1
        self.repfig_nframe_edit.setText(str(nframe))
        if hasattr(self.engine.chopper_system, 'default_frequencies'):
            cb = [
                self.widgets['FrequencyCombo']['Combo'],
                self.widgets['PulseRemoverCombo']['Combo']
            ]
            for idx, freq in enumerate(
                    self.engine.chopper_system.default_frequencies):
                cb[idx].setCurrentIndex([
                    i for i in range(cb[idx].count())
                    if str(freq) in cb[idx].itemText(i)
                ][0])
                if idx > 1:
                    break
        self.tabs.setTabEnabled(self.qetabID, False)
        if self.engine.has_detector and hasattr(self.engine.detector,
                                                'tthlims'):
            self.tabs.setTabEnabled(self.qetabID, True)

    def setChopper(self, choppername):
        """
        Defines the Fermi chopper slit package type by name, or the disk chopper arrangement variant.
        """
        self.engine.setChopper(str(choppername))
        self.engine.setFrequency(
            float(self.widgets['FrequencyCombo']['Combo'].currentText()))
        # Special case for MERLIN - only enable multirep for 'G' chopper
        if 'MERLIN' in self.engine.instname:
            if 'G' in str(choppername):
                self.widgets['MultiRepCheck'].setEnabled(True)
                self.tabs.setTabEnabled(self.tdtabID, True)
                self.widgets['Chopper2Phase']['Edit'].setText('1500')
                self.widgets['Chopper2Phase']['Label'].setText(
                    'Disk chopper phase delay time')
                if self.instSciAct.isChecked():
                    self.widgets['Chopper2Phase']['Edit'].show()
                    self.widgets['Chopper2Phase']['Label'].show()
            else:
                self.widgets['MultiRepCheck'].setEnabled(False)
                self.widgets['MultiRepCheck'].setChecked(False)
                self.tabs.setTabEnabled(self.tdtabID, False)
                self.widgets['Chopper2Phase']['Edit'].hide()
                self.widgets['Chopper2Phase']['Label'].hide()

    def setFreq(self, freqtext=None, **kwargs):
        """
        Sets the chopper frequency(ies), in Hz.
        """
        freq_gui = float(self.widgets['FrequencyCombo']['Combo'].currentText())
        freq_in = kwargs['manual_freq'] if ('manual_freq'
                                            in kwargs.keys()) else freq_gui
        if len(self.engine.getFrequency()) > 1 and (
                not hasattr(freq_in, '__len__') or len(freq_in) == 1):
            freqpr = float(
                self.widgets['PulseRemoverCombo']['Combo'].currentText())
            freq_in = [freq_in, freqpr]
        if not self.widgets['Chopper2Phase']['Label'].isHidden():
            chop2phase = self.widgets['Chopper2Phase']['Edit'].text()
            if isinstance(self.engine.chopper_system.defaultPhase[0],
                          string_types):
                chop2phase = str(chop2phase)
            else:
                chop2phase = float(chop2phase) % (
                    1e6 / self.engine.moderator.source_rep)
            self.engine.setFrequency(freq_in, phase=chop2phase)
        else:
            self.engine.setFrequency(freq_in)

    def setEi(self):
        """
        Sets the incident energy (or focused incident energy for multi-rep case).
        """
        try:
            eitxt = float(self.widgets['EiEdit']['Edit'].text())
            self.engine.setEi(eitxt)
            if self.eiPlots.isChecked():
                self.calc_callback()
        except ValueError:
            raise ValueError('No Ei specified, or Ei string not understood')

    def calc_callback(self):
        """
        Calls routines to calculate the resolution / flux and to update the Matplotlib graphs.
        """
        try:
            if self.engine.getChopper() is None:
                self.setChopper(
                    self.widgets['ChopperCombo']['Combo'].currentText())
            self.setEi()
            self.setFreq()
            self.calculate()
            if self.errormess:
                idx = [
                    i for i, ei in enumerate(self.eis)
                    if np.abs(ei - self.engine.getEi()) < 1.e-4
                ]
                if idx and self.flux[idx[0]] == 0:
                    raise ValueError(self.errormess)
                self.errormessage(self.errormess)
            self.plot_res()
            self.plot_frame()
            if self.instSciAct.isChecked():
                self.update_script()
        except ValueError as err:
            self.errormessage(err)
        self.plot_flux_ei()
        self.plot_flux_hz()

    def calculate(self):
        """
        Performs the resolution and flux calculations.
        """
        self.errormess = None
        if self.engine.getEi() is None:
            self.setEi()
        if self.widgets['MultiRepCheck'].isChecked():
            en = np.linspace(0, 0.95, 200)
            self.eis = self.engine.getAllowedEi()
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter('always', UserWarning)
                self.res = self.engine.getMultiRepResolution(en)
                self.flux = self.engine.getMultiRepFlux()
                if len(w) > 0:
                    mess = [str(w[i].message) for i in range(len(w))]
                    self.errormess = '\n'.join(
                        [m for m in mess if 'tchop' in m])
        else:
            en = np.linspace(0, 0.95 * self.engine.getEi(), 200)
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter('always', UserWarning)
                self.res = self.engine.getResolution(en)
                self.flux = self.engine.getFlux()
                if len(w) > 0:
                    raise ValueError(w[0].message)

    def _set_overplot(self, overplot, axisname):
        axis = getattr(self, axisname)
        if overplot:
            if matplotlib.compare_versions('2.1.0', matplotlib.__version__):
                axis.hold(True)
        else:
            setattr(self, axisname + '_xlim', 0)
            axis.clear()
            axis.axhline(color='k')

    def plot_res(self):
        """
        Plots the resolution in the resolution tab
        """
        overplot = self.widgets['HoldCheck'].isChecked()
        multiplot = self.widgets['MultiRepCheck'].isChecked()
        self._set_overplot(overplot, 'resaxes')
        self._set_overplot(overplot, 'qeaxes')
        inst = self.engine.instname
        freq = self.engine.getFrequency()
        if hasattr(freq, '__len__'):
            freq = freq[0]
        if multiplot:
            if matplotlib.compare_versions('2.1.0', matplotlib.__version__):
                self.resaxes.hold(True)
            for ie, Ei in enumerate(self.eis):
                en = np.linspace(0, 0.95 * Ei, 200)
                if any(self.res[ie]):
                    if not self.flux[ie]:
                        continue
                    line, = self.resaxes.plot(en, self.res[ie])
                    label_text = '%s_%3.2fmeV_%dHz_Flux=%fn/cm2/s' % (
                        inst, Ei, freq, self.flux[ie])
                    line.set_label(label_text)
                    if self.tabs.isTabEnabled(self.qetabID):
                        self.plot_qe(Ei, label_text, hold=True)
                    self.resaxes_xlim = max(Ei, self.resaxes_xlim)
            if matplotlib.compare_versions('2.1.0', matplotlib.__version__):
                self.resaxes.hold(False)
        else:
            ei = self.engine.getEi()
            en = np.linspace(0, 0.95 * ei, 200)
            line, = self.resaxes.plot(en, self.res)
            chopper = self.engine.getChopper()
            label_text = '%s_%s_%3.2fmeV_%dHz_Flux=%fn/cm2/s' % (
                inst, chopper, ei, freq, self.flux)
            line.set_label(label_text)
            if self.tabs.isTabEnabled(self.qetabID):
                self.plot_qe(ei, label_text, overplot)
            self.resaxes_xlim = max(ei, self.resaxes_xlim)
        self.resaxes.set_xlim([0, self.resaxes_xlim])
        self.resaxes.legend().draggable()
        self.resaxes.set_xlabel('Energy Transfer (meV)')
        self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)')
        self.rescanvas.draw()

    def plot_qe(self, Ei, label_text, hold=False):
        """ Plots the Q-E diagram """
        from scipy import constants
        E2q, meV2J = (2. * constants.m_n / (constants.hbar**2),
                      constants.e / 1000.)
        en = np.linspace(-Ei / 5., Ei, 100)
        q2 = []
        for tth in self.engine.detector.tthlims:
            q = np.sqrt(E2q * (2 * Ei - en - 2 * np.sqrt(Ei * (Ei - en)) *
                               np.cos(np.deg2rad(tth))) * meV2J) / 1e10
            q2.append(np.concatenate((np.flipud(q), q)))
        self._set_overplot(hold, 'qeaxes')
        self.qeaxes_xlim = max(np.max(q2), self.qeaxes_xlim)
        line, = self.qeaxes.plot(
            np.hstack(q2),
            np.concatenate((np.flipud(en), en)).tolist() *
            len(self.engine.detector.tthlims))
        line.set_label(label_text)
        self.qeaxes.set_xlim([0, self.qeaxes_xlim])
        self.qeaxes.legend().draggable()
        self.qecanvas.draw()

    def plot_flux_ei(self, **kwargs):
        """
        Plots the flux vs Ei in the middle tab
        """
        inst = self.engine.instname
        chop = self.engine.getChopper()
        freq = self.engine.getFrequency()
        overplot = self.widgets['HoldCheck'].isChecked()
        if hasattr(freq, '__len__'):
            freq = freq[0]
        update = kwargs['update'] if 'update' in kwargs.keys() else False
        # Do not recalculate if all relevant parameters still the same.
        _, labels = self.flxaxes2.get_legend_handles_labels()
        searchStr = '([A-Z]+) "(.+)" ([0-9]+) Hz'
        tmpinst = []
        if (labels and (overplot or len(labels) == 1)) or update:
            for prevtitle in labels:
                prevInst, prevChop, prevFreq = re.search(searchStr,
                                                         prevtitle).groups()
                if update:
                    tmpinst.append(
                        copy.deepcopy(
                            Instrument(self.instruments[prevInst], prevChop,
                                       float(prevFreq))))
                else:
                    if inst == prevInst and chop == prevChop and freq == float(
                            prevFreq):
                        return
        ne = 25
        mn = self.minE[inst]
        mx = (self.flxslder.val / 100) * self.maxE[inst]
        eis = np.linspace(mn, mx, ne)
        flux = eis * 0
        elres = eis * 0
        if update:
            self.flxaxes1.clear()
            self.flxaxes2.clear()
            if matplotlib.compare_versions('2.1.0', matplotlib.__version__):
                self.flxaxes1.hold(True)
                self.flxaxes2.hold(True)
            for ii, instrument in enumerate(tmpinst):
                for ie, ei in enumerate(eis):
                    with warnings.catch_warnings(record=True):
                        warnings.simplefilter('always', UserWarning)
                        flux[ie] = instrument.getFlux(ei)
                        elres[ie] = instrument.getResolution(0., ei)[0]
                self.flxaxes1.plot(eis, flux)
                line, = self.flxaxes2.plot(eis, elres)
                line.set_label(labels[ii])
        else:
            for ie, ei in enumerate(eis):
                with warnings.catch_warnings(record=True):
                    warnings.simplefilter('always', UserWarning)
                    flux[ie] = self.engine.getFlux(ei)
                    elres[ie] = self.engine.getResolution(0., ei)[0]
            if overplot:
                if matplotlib.compare_versions('2.1.0',
                                               matplotlib.__version__):
                    self.flxaxes1.hold(True)
                    self.flxaxes2.hold(True)
            else:
                self.flxaxes1.clear()
                self.flxaxes2.clear()
            self.flxaxes1.plot(eis, flux)
            line, = self.flxaxes2.plot(eis, elres)
            line.set_label('%s "%s" %d Hz' % (inst, chop, freq))
        self.flxaxes1.set_xlim([mn, mx])
        self.flxaxes2.set_xlim([mn, mx])
        self.flxaxes1.set_xlabel('Incident Energy (meV)')
        self.flxaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        self.flxaxes1.set_xlabel('Incident Energy (meV)')
        self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        lg = self.flxaxes2.legend()
        lg.draggable()
        self.flxcanvas.draw()

    def update_slider(self, val=None):
        """
        Callback function for the x-axis slider of the flux tab
        """
        if val is None:
            val = float(
                self.flxedt.text()) / self.maxE[self.engine.instname] * 100
            if val < self.minE[self.engine.instname]:
                self.errormessage("Max Ei must be greater than %2.1f" %
                                  (self.minE[self.engine.instname]))
                val = (self.minE[self.engine.instname] +
                       0.1) / self.maxE[self.engine.instname] * 100
            self.flxslder.set_val(val)
        else:
            val = self.flxslder.val * self.maxE[self.engine.instname] / 100
            self.flxedt.setText('%3.2f' % (val))
        self.plot_flux_ei(update=True)
        self.flxcanvas.draw()

    def plot_flux_hz(self):
        """
        Plots the flux vs freq in the middle tab
        """
        inst = self.engine.instname
        chop = self.engine.getChopper()
        ei = float(self.widgets['EiEdit']['Edit'].text())
        overplot = self.widgets['HoldCheck'].isChecked()
        # Do not recalculate if one of the plots has the same parametersc
        _, labels = self.frqaxes2.get_legend_handles_labels()
        searchStr = '([A-Z]+) "(.+)" Ei = ([0-9.-]+) meV'
        if labels and (overplot or len(labels) == 1):
            for prevtitle in labels:
                prevInst, prevChop, prevEi = re.search(searchStr,
                                                       prevtitle).groups()
                if inst == prevInst and chop == prevChop and abs(
                        ei - float(prevEi)) < 0.01:
                    return
        freq0 = self.engine.getFrequency()
        rep = self.engine.moderator.source_rep
        maxfreq = self.engine.chopper_system.max_frequencies
        freqs = range(
            rep, (maxfreq[0] if hasattr(maxfreq, '__len__') else maxfreq) + 1,
            rep)
        flux = np.zeros(len(freqs))
        elres = np.zeros(len(freqs))
        for ie, freq in enumerate(freqs):
            if hasattr(freq0, '__len__'):
                self.setFreq(manual_freq=[freq] + freq0[1:])
            else:
                self.setFreq(manual_freq=freq)
            with warnings.catch_warnings(record=True):
                warnings.simplefilter('always', UserWarning)
                flux[ie] = self.engine.getFlux(ei)
                elres[ie] = self.engine.getResolution(0., ei)[0]
        if overplot:
            if matplotlib.compare_versions('2.1.0', matplotlib.__version__):
                self.frqaxes1.hold(True)
                self.frqaxes2.hold(True)
        else:
            self.frqaxes1.clear()
            self.frqaxes2.clear()
        self.setFreq(manual_freq=freq0)
        self.frqaxes1.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        line, = self.frqaxes1.plot(freqs, flux, 'o-')
        self.frqaxes1.set_xlim([0, np.max(freqs)])
        self.frqaxes2.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        line, = self.frqaxes2.plot(freqs, elres, 'o-')
        line.set_label('%s "%s" Ei = %5.3f meV' % (inst, chop, ei))
        lg = self.frqaxes2.legend()
        lg.draggable()
        self.frqaxes2.set_xlim([0, np.max(freqs)])
        self.frqcanvas.draw()

    def instSciCB(self):
        """
        Callback function for the "Instrument Scientist Mode" menu option
        """
        # MERLIN is a special case - want to hide ability to change phase from users
        if 'MERLIN' in self.engine.instname and 'G' in self.engine.getChopper(
        ):
            if self.instSciAct.isChecked():
                self.widgets['Chopper2Phase']['Edit'].show()
                self.widgets['Chopper2Phase']['Label'].show()
                self.widgets['Chopper2Phase']['Edit'].setText('1500')
                self.widgets['Chopper2Phase']['Label'].setText(
                    'Disk chopper phase delay time')
            else:
                self.widgets['Chopper2Phase']['Edit'].hide()
                self.widgets['Chopper2Phase']['Label'].hide()
        if self.instSciAct.isChecked():
            self.tabs.insertTab(self.scrtabID, self.scrtab, 'ScriptOutput')
            self.scrtab.show()
        else:
            self.tabs.removeTab(self.scrtabID)
            self.scrtab.hide()

    def errormessage(self, message):
        msg = QMessageBox()
        msg.setText(str(message))
        msg.setStandardButtons(QMessageBox.Ok)
        msg.exec_()

    def loadYaml(self):
        yaml_file = QFileDialog().getOpenFileName(self.mainWidget,
                                                  'Open Instrument YAML File',
                                                  self.folder,
                                                  'Files (*.yaml)')
        if isinstance(yaml_file, tuple):
            yaml_file = yaml_file[0]
        yaml_file = str(yaml_file)
        new_folder = os.path.dirname(yaml_file)
        if new_folder != self.folder:
            self.folder = new_folder
        try:
            new_inst = Instrument(yaml_file)
        except (RuntimeError, AttributeError, ValueError) as err:
            self.errormessage(err)
        newname = new_inst.name
        if newname in self.instruments.keys(
        ) and not self.overwriteload.isChecked():
            overwrite, newname = self._ask_overwrite()
            if overwrite == 1:
                return
            elif overwrite == 0:
                newname = new_inst.name
        self.instruments[newname] = new_inst
        self.choppers[newname] = new_inst.getChopperNames()
        self.minE[newname] = max([new_inst.emin, 0.01])
        self.maxE[newname] = new_inst.emax
        self.updateInstrumentList()
        combo = self.widgets['InstrumentCombo']['Combo']
        idx = [
            i for i in range(combo.count())
            if str(combo.itemText(i)) == newname
        ]
        combo.setCurrentIndex(idx[0])
        self.setInstrument(newname)

    def _ask_overwrite(self):
        msg = QDialog()
        msg.setWindowTitle('Load overwrite')
        layout = QGridLayout()
        layout.addWidget(
            QLabel('Instrument %s already exists in memory. Overwrite this?'),
            0, 0, 1, -1)
        buttons = [
            QPushButton(label) for label in
            ['Load and overwrite', 'Cancel Load', 'Load and rename to']
        ]
        locations = [[1, 0], [1, 1], [2, 0]]
        self.overwrite_flag = 1

        def overwriteCB(idx):
            self.overwrite_flag = idx
            msg.accept()

        for idx, button in enumerate(buttons):
            button.clicked.connect(lambda _, idx=idx: overwriteCB(idx))
            layout.addWidget(button, locations[idx][0], locations[idx][1])
        newname = QLineEdit()
        newname.editingFinished.connect(lambda: overwriteCB(2))
        layout.addWidget(newname, 2, 1)
        msg.setLayout(layout)
        msg.exec_()
        newname = str(newname.text())
        if not newname or newname in self.instruments:
            self.errormessage('Invalid instrument name. Cancelling load.')
            self.overwrite_flag = 1
        return self.overwrite_flag, newname

    def updateInstrumentList(self):
        combo = self.widgets['InstrumentCombo']['Combo']
        old_instruments = [
            str(combo.itemText(i)) for i in range(combo.count())
        ]
        new_instruments = [
            inst for inst in self.instruments if inst not in old_instruments
        ]
        for inst in new_instruments:
            combo.addItem(inst)

    def plot_frame(self):
        """
        Plots the distance-time diagram in the right tab
        """
        if len(self.engine.chopper_system.choppers) > 1:
            self.engine.n_frame = int(self.repfig_nframe_edit.text())
            self.repaxes.clear()
            self.engine.plotMultiRepFrame(self.repaxes)
            self.repcanvas.draw()

    def genText(self):
        """
        Generates text output of the resolution function versus energy transfer and other information.
        """
        en = np.linspace(0, 0.95 * self.engine.getEi(), 10)
        try:
            flux = self.engine.getFlux()
            res = self.engine.getResolution(en)
        except ValueError as err:
            self.errormessage(err)
            raise ValueError(err)
        obj = self.engine
        instname, chtyp, freqs, ei_in = tuple(
            [obj.instname,
             obj.getChopper(),
             obj.getFrequency(),
             obj.getEi()])
        ei = ei_in
        tsqvan, tsqdic, tsqmodchop = obj.getVanVar()
        v_mod, v_chop = tuple(np.sqrt(tsqmodchop[:2]) * 1e6)
        x0, _, x1, x2, _ = obj.chopper_system.getDistances()
        first_component = 'moderator'
        if x0 != tsqmodchop[2]:
            x0 = tsqmodchop[2]
            first_component = 'chopper 1'
        txt = '# ------------------------------------------------------------- #\n'
        txt += '# Chop calculation for instrument %s\n' % (instname)
        if obj.isFermi:
            txt += '#     with chopper %s at %3i Hz\n' % (chtyp, freqs[0])
        else:
            txt += '#     in %s mode with:\n' % (chtyp)
            freq_names = obj.chopper_system.frequency_names
            for idx in range(len(freq_names)):
                txt += '#     %s at %3i Hz\n' % (freq_names[idx], freqs[idx])
        txt += '# ------------------------------------------------------------- #\n'
        txt += '# Flux = %8.2f n/cm2/s\n' % (flux)
        txt += '# Elastic resolution = %6.2f meV\n' % (res[0])
        txt += '# Time width at sample = %6.2f us, of which:\n' % (
            1e6 * np.sqrt(tsqvan))
        for ky, val in list(tsqdic.items()):
            txt += '#     %20s : %6.2f us\n' % (ky, 1e6 * np.sqrt(val))
        txt += '# %s distances:\n' % (instname)
        txt += '#     x0 = %6.2f m (%s to Fermi)\n' % (x0, first_component)
        txt += '#     x1 = %6.2f m (Fermi to sample)\n' % (x1)
        txt += '#     x2 = %6.2f m (sample to detector)\n' % (x2)
        txt += '# Approximate inelastic resolution is given by:\n'
        txt += '#     dE = 2 * E2V * sqrt(ef**3 * t_van**2) / x2\n'
        txt += '#     where:  E2V = 4.373e-4 meV/(m/us) conversion from energy to speed\n'
        txt += '#             t_van**2 = (geom*t_mod)**2 + ((1+geom)*t_chop)**2\n'
        txt += '#             geom = (x1 + x2*(ei/ef)**1.5) / x0\n'
        txt += '#     and t_mod and t_chop are the moderator and chopper time widths at the\n'
        txt += '#     moderator and chopper positions (not at the sample as listed above).\n'
        txt += '# Which in this case is:\n'
        txt += '#     %.4e*sqrt(ef**3 * ( (%6.5f*(%.3f+%.3f*(ei/ef)**1.5))**2 \n' % (
            874.78672e-6 / x2, v_mod, x1 / x0, x2 / x0)
        txt += '#                              + (%6.5f*(%.3f+%.3f*(ei/ef)**1.5))**2) )\n' % (
            v_chop, 1 + x1 / x0, x2 / x0)
        txt += '#  EN (meV)   Full dE (meV)   Approx dE (meV)\n'
        for ii in range(len(res)):
            ef = ei - en[ii]
            approx = (874.78672e-6 / x2) * np.sqrt(ef**3 * (
                (v_mod * ((x1 / x0) + (x2 / x0) * (ei / ef)**1.5))**2 +
                (v_chop * (1 + (x1 / x0) + (x2 / x0) * (ei / ef)**1.5))**2))
            txt += '%12.5f %12.5f %12.5f\n' % (en[ii], res[ii], approx)
        return txt

    def showText(self):
        """
        Creates a dialog to show the generated text output.
        """
        try:
            generatedText = self.genText()
        except ValueError:
            return
        self.txtwin = QDialog()
        self.txtedt = QTextEdit()
        self.txtbtn = QPushButton('OK')
        self.txtwin.layout = QVBoxLayout(self.txtwin)
        self.txtwin.layout.addWidget(self.txtedt)
        self.txtwin.layout.addWidget(self.txtbtn)
        self.txtbtn.clicked.connect(self.txtwin.deleteLater)
        self.txtedt.setText(generatedText)
        self.txtedt.setReadOnly(True)
        self.txtwin.setWindowTitle('Resolution information')
        self.txtwin.setWindowModality(Qt.ApplicationModal)
        self.txtwin.setAttribute(Qt.WA_DeleteOnClose)
        self.txtwin.setMinimumSize(400, 600)
        self.txtwin.resize(400, 600)
        self.txtwin.show()
        self.txtloop = QEventLoop()
        self.txtloop.exec_()

    def saveText(self):
        """
        Saves the generated text to a file (opens file dialog).
        """
        fname = QFileDialog.getSaveFileName(self, 'Open file', '')
        if isinstance(fname, tuple):
            fname = fname[0]
        fid = open(fname, 'w')
        fid.write(self.genText())
        fid.close()

    def update_script(self):
        """
        Updates the text window with information about the previous calculation.
        """
        if self.widgets['MultiRepCheck'].isChecked():
            out = self.engine.getMultiWidths()
            new_str = '\n'
            for ie, ee in enumerate(out['Eis']):
                res = out['Energy'][ie]
                percent = res / ee * 100
                chop_width = out['chopper'][ie]
                mod_width = out['moderator'][ie]
                new_str += 'Ei is %6.2f meV, resolution is %6.2f ueV, percentage resolution is %6.3f\n' % (
                    ee, res * 1000, percent)
                new_str += 'FWHM at sample from chopper and moderator are %6.2f us, %6.2f us\n' % (
                    chop_width, mod_width)
        else:
            ei = self.engine.getEi()
            out = self.engine.getWidths()
            res = out['Energy']
            percent = res / ei * 100
            chop_width = out['chopper']
            mod_width = out['moderator']
            new_str = '\nEi is %6.2f meV, resolution is %6.2f ueV, percentage resolution is %6.3f\n' % (
                ei, res * 1000, percent)
            new_str += 'FWHM at sample from chopper and moderator are %6.2f us, %6.2f us\n' % (
                chop_width, mod_width)
        self.scredt.append(new_str)

    def onHelp(self):
        """
        Shows the help page
        """
        try:
            from pymantidplot.proxies import showCustomInterfaceHelp
            showCustomInterfaceHelp("PyChop")
        except ImportError:
            helpTxt = "PyChop is a tool to allow direct inelastic neutron\nscattering users to estimate the inelastic resolution\n"
            helpTxt += "and incident flux for a given spectrometer setting.\n\nFirst select the instrument, chopper settings and\n"
            helpTxt += "Ei, and then click 'Calculate and Plot'. Data for all\nthe graphs will be generated (may take 1-2s) and\n"
            helpTxt += "all graphs will be updated. If the 'Hold current plot'\ncheck box is ticked, additional settings will be\n"
            helpTxt += "overplotted on the existing graphs if they are\ndifferent from previous settings.\n\nMore in-depth help "
            helpTxt += "can be obtained from the\nMantid help pages."
            self.hlpwin = QDialog()
            self.hlpedt = QLabel(helpTxt)
            self.hlpbtn = QPushButton('OK')
            self.hlpwin.layout = QVBoxLayout(self.hlpwin)
            self.hlpwin.layout.addWidget(self.hlpedt)
            self.hlpwin.layout.addWidget(self.hlpbtn)
            self.hlpbtn.clicked.connect(self.hlpwin.deleteLater)
            self.hlpwin.setWindowTitle('Help')
            self.hlpwin.setWindowModality(Qt.ApplicationModal)
            self.hlpwin.setAttribute(Qt.WA_DeleteOnClose)
            self.hlpwin.setMinimumSize(370, 300)
            self.hlpwin.resize(370, 300)
            self.hlpwin.show()
            self.hlploop = QEventLoop()
            self.hlploop.exec_()

    def drawLayout(self):
        """
        Draws the GUI layout.
        """
        self.widgetslist = [[
            'pair', 'show', 'Instrument', 'combo', self.instruments,
            self.setInstrument, 'InstrumentCombo'
        ],
                            [
                                'pair', 'show', 'Chopper', 'combo', '',
                                self.setChopper, 'ChopperCombo'
                            ],
                            [
                                'pair', 'show', 'Frequency', 'combo', '',
                                self.setFreq, 'FrequencyCombo'
                            ],
                            [
                                'pair', 'hide', 'Pulse remover chopper freq',
                                'combo', '', self.setFreq, 'PulseRemoverCombo'
                            ],
                            [
                                'pair', 'show', 'Ei', 'edit', '', self.setEi,
                                'EiEdit'
                            ],
                            [
                                'pair', 'hide', 'Chopper 2 phase delay time',
                                'edit', '5', self.setFreq, 'Chopper2Phase'
                            ], ['spacer'],
                            [
                                'single', 'show', 'Calculate and Plot',
                                'button', self.calc_callback, 'CalculateButton'
                            ],
                            [
                                'single', 'show', 'Hold current plot', 'check',
                                lambda: None, 'HoldCheck'
                            ],
                            [
                                'single', 'show', 'Show multi-reps', 'check',
                                lambda: None, 'MultiRepCheck'
                            ], ['spacer'],
                            [
                                'single', 'show', 'Show data ascii window',
                                'button', self.showText, 'ShowAsciiButton'
                            ],
                            [
                                'single', 'show', 'Save data as ascii',
                                'button', self.saveText, 'SaveAsciiButton'
                            ]]
        self.droplabels = []
        self.dropboxes = []
        self.singles = []
        self.widgets = {}

        self.leftPanel = QVBoxLayout()
        self.rightPanel = QVBoxLayout()
        self.tabs = QTabWidget(self)
        self.fullWindow = QGridLayout()
        for widget in self.widgetslist:
            if 'pair' in widget[0]:
                self.droplabels.append(QLabel(widget[2]))
                if 'combo' in widget[3]:
                    self.dropboxes.append(QComboBox(self))
                    self.dropboxes[-1].activated['QString'].connect(widget[5])
                    for item in widget[4]:
                        self.dropboxes[-1].addItem(item)
                    self.widgets[widget[-1]] = {
                        'Combo': self.dropboxes[-1],
                        'Label': self.droplabels[-1]
                    }
                elif 'edit' in widget[3]:
                    self.dropboxes.append(QLineEdit(self))
                    self.dropboxes[-1].returnPressed.connect(widget[5])
                    self.widgets[widget[-1]] = {
                        'Edit': self.dropboxes[-1],
                        'Label': self.droplabels[-1]
                    }
                else:
                    raise RuntimeError(
                        'Bug in code - widget %s is not recognised.' %
                        (widget[3]))
                self.leftPanel.addWidget(self.droplabels[-1])
                self.leftPanel.addWidget(self.dropboxes[-1])
                if 'hide' in widget[1]:
                    self.droplabels[-1].hide()
                    self.dropboxes[-1].hide()
            elif 'single' in widget[0]:
                if 'check' in widget[3]:
                    self.singles.append(QCheckBox(widget[2], self))
                    self.singles[-1].stateChanged.connect(widget[4])
                elif 'button' in widget[3]:
                    self.singles.append(QPushButton(widget[2]))
                    self.singles[-1].clicked.connect(widget[4])
                else:
                    raise RuntimeError(
                        'Bug in code - widget %s is not recognised.' %
                        (widget[3]))
                self.leftPanel.addWidget(self.singles[-1])
                if 'hide' in widget[1]:
                    self.singles[-1].hide()
                self.widgets[widget[-1]] = self.singles[-1]
            elif 'spacer' in widget[0]:
                self.leftPanel.addItem(QSpacerItem(0, 35))
            else:
                raise RuntimeError(
                    'Bug in code - widget class %s is not recognised.' %
                    (widget[0]))

        # Right panel, matplotlib figures
        self.resfig = Figure()
        self.resfig.patch.set_facecolor('white')
        self.rescanvas = FigureCanvas(self.resfig)
        self.resaxes = self.resfig.add_subplot(111)
        self.resaxes.axhline(color='k')
        self.resaxes.set_xlabel('Energy Transfer (meV)')
        self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)')
        self.resfig_controls = NavigationToolbar(self.rescanvas, self)
        self.restab = QWidget(self.tabs)
        self.restabbox = QVBoxLayout()
        self.restabbox.addWidget(self.rescanvas)
        self.restabbox.addWidget(self.resfig_controls)
        self.restab.setLayout(self.restabbox)

        self.flxfig = Figure()
        self.flxfig.patch.set_facecolor('white')
        self.flxcanvas = FigureCanvas(self.flxfig)
        self.flxaxes1 = self.flxfig.add_subplot(121)
        self.flxaxes1.set_xlabel('Incident Energy (meV)')
        self.flxaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        self.flxaxes2 = self.flxfig.add_subplot(122)
        self.flxaxes2.set_xlabel('Incident Energy (meV)')
        self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        self.flxfig_controls = NavigationToolbar(self.flxcanvas, self)
        self.flxsldfg = Figure()
        self.flxsldfg.patch.set_facecolor('white')
        self.flxsldcv = FigureCanvas(self.flxsldfg)
        self.flxsldax = self.flxsldfg.add_subplot(111)
        self.flxslder = Slider(self.flxsldax, 'Ei (meV)', 0, 100, valinit=100)
        self.flxslder.valtext.set_visible(False)
        self.flxslder.on_changed(self.update_slider)
        self.flxedt = QLineEdit()
        self.flxedt.setText('1000')
        self.flxedt.returnPressed.connect(self.update_slider)
        self.flxtab = QWidget(self.tabs)
        self.flxsldbox = QHBoxLayout()
        self.flxsldbox.addWidget(self.flxsldcv)
        self.flxsldbox.addWidget(self.flxedt)
        self.flxsldwdg = QWidget()
        self.flxsldwdg.setLayout(self.flxsldbox)
        sz = self.flxsldwdg.maximumSize()
        sz.setHeight(50)
        self.flxsldwdg.setMaximumSize(sz)
        self.flxtabbox = QVBoxLayout()
        self.flxtabbox.addWidget(self.flxcanvas)
        self.flxtabbox.addWidget(self.flxsldwdg)
        self.flxtabbox.addWidget(self.flxfig_controls)
        self.flxtab.setLayout(self.flxtabbox)

        self.frqfig = Figure()
        self.frqfig.patch.set_facecolor('white')
        self.frqcanvas = FigureCanvas(self.frqfig)
        self.frqaxes1 = self.frqfig.add_subplot(121)
        self.frqaxes1.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        self.frqaxes2 = self.frqfig.add_subplot(122)
        self.frqaxes1.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        self.frqfig_controls = NavigationToolbar(self.frqcanvas, self)
        self.frqtab = QWidget(self.tabs)
        self.frqtabbox = QVBoxLayout()
        self.frqtabbox.addWidget(self.frqcanvas)
        self.frqtabbox.addWidget(self.frqfig_controls)
        self.frqtab.setLayout(self.frqtabbox)

        self.repfig = Figure()
        self.repfig.patch.set_facecolor('white')
        self.repcanvas = FigureCanvas(self.repfig)
        self.repaxes = self.repfig.add_subplot(111)
        self.repaxes.axhline(color='k')
        self.repaxes.set_xlabel(r'TOF ($\mu$sec)')
        self.repaxes.set_ylabel('Distance (m)')
        self.repfig_controls = NavigationToolbar(self.repcanvas, self)
        self.repfig_nframe_label = QLabel('Number of frames to plot')
        self.repfig_nframe_edit = QLineEdit('1')
        self.repfig_nframe_button = QPushButton('Replot')
        self.repfig_nframe_button.clicked.connect(lambda: self.plot_frame())
        self.repfig_nframe_box = QHBoxLayout()
        self.repfig_nframe_box.addWidget(self.repfig_nframe_label)
        self.repfig_nframe_box.addWidget(self.repfig_nframe_edit)
        self.repfig_nframe_box.addWidget(self.repfig_nframe_button)
        self.reptab = QWidget(self.tabs)
        self.repfig_nframe = QWidget(self.reptab)
        self.repfig_nframe.setLayout(self.repfig_nframe_box)
        self.repfig_nframe.setSizePolicy(
            QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed))
        self.reptabbox = QVBoxLayout()
        self.reptabbox.addWidget(self.repcanvas)
        self.reptabbox.addWidget(self.repfig_nframe)
        self.reptabbox.addWidget(self.repfig_controls)
        self.reptab.setLayout(self.reptabbox)

        self.qefig = Figure()
        self.qefig.patch.set_facecolor('white')
        self.qecanvas = FigureCanvas(self.qefig)
        self.qeaxes = self.qefig.add_subplot(111)
        self.qeaxes.axhline(color='k')
        self.qeaxes.set_xlabel(r'$|Q| (\mathrm{\AA}^{-1})$')
        self.qeaxes.set_ylabel('Energy Transfer (meV)')
        self.qefig_controls = NavigationToolbar(self.qecanvas, self)
        self.qetabbox = QVBoxLayout()
        self.qetabbox.addWidget(self.qecanvas)
        self.qetabbox.addWidget(self.qefig_controls)
        self.qetab = QWidget(self.tabs)
        self.qetab.setLayout(self.qetabbox)

        self.scrtab = QWidget(self.tabs)
        self.scredt = QTextEdit()
        self.scrcls = QPushButton("Clear")
        self.scrcls.clicked.connect(lambda: self.scredt.clear())
        self.scrbox = QVBoxLayout()
        self.scrbox.addWidget(self.scredt)
        self.scrbox.addWidget(self.scrcls)
        self.scrtab.setLayout(self.scrbox)
        self.scrtab.hide()

        self.tabs.addTab(self.restab, 'Resolution')
        self.tabs.addTab(self.flxtab, 'Flux-Ei')
        self.tabs.addTab(self.frqtab, 'Flux-Freq')
        self.tabs.addTab(self.reptab, 'Time-Distance')
        self.tdtabID = 3
        self.tabs.setTabEnabled(self.tdtabID, False)
        self.tabs.addTab(self.qetab, 'Q-E')
        self.qetabID = 4
        self.tabs.setTabEnabled(self.qetabID, False)
        self.scrtabID = 5
        self.rightPanel.addWidget(self.tabs)

        self.menuLoad = QMenu('Load')
        self.loadAct = QAction('Load YAML', self.menuLoad)
        self.loadAct.triggered.connect(self.loadYaml)
        self.menuLoad.addAction(self.loadAct)
        self.menuOptions = QMenu('Options')
        self.instSciAct = QAction('Instrument Scientist Mode',
                                  self.menuOptions,
                                  checkable=True)
        self.instSciAct.triggered.connect(self.instSciCB)
        self.menuOptions.addAction(self.instSciAct)
        self.eiPlots = QAction('Press Enter in Ei box updates plots',
                               self.menuOptions,
                               checkable=True)
        self.menuOptions.addAction(self.eiPlots)
        self.overwriteload = QAction('Always overwrite instruments in memory',
                                     self.menuOptions,
                                     checkable=True)
        self.menuOptions.addAction(self.overwriteload)
        self.menuBar().addMenu(self.menuLoad)
        self.menuBar().addMenu(self.menuOptions)

        self.leftPanelWidget = QWidget()
        self.leftPanelWidget.setLayout(self.leftPanel)
        self.leftPanelWidget.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred))
        self.fullWindow.addWidget(self.leftPanelWidget, 0, 0)
        self.fullWindow.addLayout(self.rightPanel, 0, 1)
        self.helpbtn = QPushButton("?", self)
        self.helpbtn.setMaximumWidth(30)
        self.helpbtn.clicked.connect(self.onHelp)
        self.fullWindow.addWidget(self.helpbtn, 1, 0, 1, -1)

        self.mainWidget = QWidget()
        self.mainWidget.setLayout(self.fullWindow)
        self.setCentralWidget(self.mainWidget)
        self.setWindowTitle('PyChopGUI')
        self.show()
예제 #22
0
class PhasePlaneDialog(QDialog):
    """A dialog to create phase plane plots"""
    def __init__(self, appdata):
        QDialog.__init__(self)
        self.setWindowTitle("Phase plane plotting")

        self.appdata = appdata

        completer = QCompleter(
            self.appdata.project.cobra_py_model.reactions.list_attr("id"),
            self)
        completer.setCaseSensitivity(Qt.CaseInsensitive)

        self.layout = QVBoxLayout()
        l1 = QHBoxLayout()
        t1 = QLabel("Reaction (x-axis):")
        l1.addWidget(t1)
        self.x_axis = CompleterLineEdit(
            self.appdata.project.cobra_py_model.reactions.list_attr("id"), "")
        self.x_axis.setPlaceholderText("Enter reaction Id")

        l1.addWidget(self.x_axis)
        l2 = QHBoxLayout()
        t2 = QLabel("Reaction (y-axis):")
        l2.addWidget(t2)
        self.y_axis = QLineEdit("")
        self.y_axis.setPlaceholderText("Enter reaction Id")
        self.y_axis.setCompleter(completer)
        l2.addWidget(self.y_axis)
        self.layout.addItem(l1)
        self.layout.addItem(l2)
        l3 = QHBoxLayout()
        self.button = QPushButton("Plot")
        self.cancel = QPushButton("Close")
        l3.addWidget(self.button)
        l3.addWidget(self.cancel)
        self.layout.addItem(l3)
        self.setLayout(self.layout)

        # Connecting the signal
        self.cancel.clicked.connect(self.reject)
        self.button.clicked.connect(self.compute)

    def compute(self):
        self.setCursor(Qt.BusyCursor)
        with self.appdata.project.cobra_py_model as model:
            self.appdata.project.load_scenario_into_model(model)
            x_axis = self.x_axis.text()
            y_axis = self.y_axis.text()
            try:
                x_reac_idx = model.reactions.index(x_axis)
                y_reac_idx = model.reactions.index(y_axis)
            except KeyError:
                return
            points = 100
            with model as ppmodel:
                ppmodel.objective = ppmodel.reactions[x_reac_idx]
                ppmodel.objective.direction = 'min'
                x_lb = ppmodel.slim_optimize()
                ppmodel.objective.direction = 'max'
                x_ub = ppmodel.slim_optimize()
            result2 = numpy.zeros((points, 3))
            result2[:, 0] = numpy.linspace(x_lb, x_ub, num=points)
            var = numpy.linspace(x_lb, x_ub, num=points)
            lb = numpy.full(points, numpy.nan)
            ub = numpy.full(points, numpy.nan)
            with model as ppmodel:
                ppmodel.objective = ppmodel.reactions[y_reac_idx]
                for i in range(points):
                    # without second context the original reaction bounds are not restored (?)
                    with ppmodel as ppmodel2:
                        ppmodel2.reactions[x_reac_idx].lower_bound = result2[i,
                                                                             0]
                        ppmodel2.reactions[x_reac_idx].upper_bound = result2[i,
                                                                             0]
                        ppmodel2.objective.direction = 'min'
                        lb[i] = result2[i, 1] = ppmodel2.slim_optimize()
                        ppmodel2.objective.direction = 'max'
                        ub[i] = result2[i, 2] = ppmodel2.slim_optimize()

            _fig, axes = plt.subplots()
            axes.set_xlabel(model.reactions[x_reac_idx].id)
            axes.set_ylabel(model.reactions[y_reac_idx].id)
            x = [v for v in var] + [v for v in reversed(var)]
            y = [v for v in lb] + [v for v in reversed(ub)]
            if lb[0] != ub[0]:
                x.extend([var[0], var[0]])
                y.extend([lb[0], ub[0]])

            plt.plot(x, y)
            plt.show()

        self.appdata.window.centralWidget().show_bottom_of_console()

        self.setCursor(Qt.ArrowCursor)
예제 #23
0
    def get_measurement_widget(self, parent):
        """."""
        meas_wid = QWidget(parent)
        meas_lay = QVBoxLayout(meas_wid)

        strt = PyDMPushButton(
            meas_wid,
            init_channel=self.devpref.substitute(propty="MeasRespMat-Cmd"),
            pressValue=ConstTLines.MeasRespMatCmd.Start)
        strt.setEnabled(True)
        strt.setToolTip('Start Measurement')
        strt.setIcon(qta.icon('fa5s.play'))
        strt.setObjectName('strt')
        strt.setStyleSheet(
            '#strt{min-width:25px; max-width:25px; icon-size:20px;}')
        stop = PyDMPushButton(
            meas_wid,
            init_channel=self.devpref.substitute(propty="MeasRespMat-Cmd"),
            pressValue=ConstTLines.MeasRespMatCmd.Stop)
        stop.setEnabled(True)
        stop.setToolTip('Stop Measurement')
        stop.setIcon(qta.icon('fa5s.stop'))
        stop.setObjectName('stop')
        stop.setStyleSheet(
            '#stop{min-width:25px; max-width:25px; icon-size:20px;}')
        rst = PyDMPushButton(
            meas_wid,
            init_channel=self.devpref.substitute(propty="MeasRespMat-Cmd"),
            pressValue=ConstTLines.MeasRespMatCmd.Reset)
        rst.setEnabled(True)
        rst.setToolTip('Reset Measurement Status')
        rst.setIcon(qta.icon('fa5s.sync'))
        rst.setObjectName('conf')
        rst.setStyleSheet(
            '#conf{min-width:25px; max-width:25px; icon-size:20px;}')
        lbl = PyDMLabel(
            meas_wid, self.devpref.substitute(propty='MeasRespMat-Mon'))
        lbl.setAlignment(Qt.AlignCenter)
        hbl = QHBoxLayout()
        hbl.setSpacing(8)
        meas_lay.addItem(hbl)
        hbl.addWidget(strt)
        hbl.addWidget(stop)
        hbl.addWidget(rst)
        hbl.addStretch()
        hbl.addWidget(lbl)

        fml = QFormLayout()
        meas_lay.addSpacing(20)
        meas_lay.addItem(fml)
        lbl = QLabel('CH [urad]', meas_wid)
        wid = self.create_pair(meas_wid, 'MeasRespMatKickCH')
        fml.addRow(lbl, wid)
        lbl = QLabel('CV [urad]', meas_wid)
        wid = self.create_pair(meas_wid, 'MeasRespMatKickCV')
        fml.addRow(lbl, wid)
        if self.acc in {'SI', 'BO'}:
            lbl = QLabel('RF [Hz]', meas_wid)
            wid = self.create_pair(meas_wid, 'MeasRespMatKickRF')
            fml.addRow(lbl, wid)
        lbl = QLabel('Wait [s]', meas_wid)
        lbl.setToolTip('Time to wait between kicks')
        wid = self.create_pair(meas_wid, 'MeasRespMatWait')
        fml.addRow(lbl, wid)

        return meas_wid
예제 #24
0
class ConfigDialog(QDialog):
    """A dialog to set values in cnapy-config.txt"""
    def __init__(self, appdata: CnaData):
        QDialog.__init__(self)
        self.setWindowTitle("Configure CNApy")

        cross_icon = QIcon(":/icons/cross.png")
        cross = cross_icon.pixmap(QSize(32, 32))

        self.appdata = appdata
        self.oeng = appdata.octave_engine
        self.meng = appdata.matlab_engine
        self.layout = QVBoxLayout()
        self.cna_ok = False

        descr = QLabel("\
            Some functionalities in CNApy need a working CNA installation.\n \
            To use CNA you need either Matlab >= R2019 or Octave >= 5 .\n \
            Below you can choose a Matlab directory or the Octave executable.\n \
            Only if one of the engines is green your CNA directory can be validated."
                       )
        self.layout.addWidget(descr)
        ml = QHBoxLayout()
        label = QLabel("Matlab")
        label.setFixedWidth(100)
        ml.addWidget(label)
        self.ml_label = QLabel()
        self.ml_label.setPixmap(cross)
        self.ml_label.setFixedWidth(100)
        ml.addWidget(self.ml_label)
        self.choose_ml_path_btn = QPushButton("Choose Matlab folder")
        self.choose_ml_path_btn.setFixedWidth(300)
        ml.addWidget(self.choose_ml_path_btn)
        label2 = QLabel("")
        label2.setFixedWidth(30)
        ml.addWidget(label2)
        self.matlab_path = QLabel()
        self.matlab_path.setMinimumWidth(200)
        self.matlab_path.setText(self.appdata.matlab_path)
        ml.addWidget(self.matlab_path)

        self.layout.addItem(ml)

        oc = QHBoxLayout()
        label = QLabel("Octave")
        label.setFixedWidth(100)
        oc.addWidget(label)
        self.oc_label = QLabel()
        self.oc_label.setPixmap(cross)
        self.oc_label.setFixedWidth(100)
        oc.addWidget(self.oc_label)

        self.choose_oc_exe_btn = QPushButton("Choose Octave octave-cli(.exe)")
        self.choose_oc_exe_btn.setFixedWidth(300)
        oc.addWidget(self.choose_oc_exe_btn)

        label2 = QLabel("")
        label2.setFixedWidth(30)
        oc.addWidget(label2)

        self.oc_exe = QLabel()
        self.oc_exe.setText(self.appdata.octave_executable)
        self.oc_exe.setMinimumWidth(200)
        oc.addWidget(self.oc_exe)

        self.layout.addItem(oc)

        h9 = QHBoxLayout()
        label = QLabel("Selected engine:")
        h9.addWidget(label)
        label2 = QLabel("")
        label2.setFixedWidth(30)
        h9.addWidget(label2)
        self.selected_engine = QComboBox()
        self.selected_engine.addItem("None")
        h9.addWidget(self.selected_engine)
        label2 = QLabel("")
        label2.setMinimumWidth(200)
        h9.addWidget(label2)
        self.layout.addItem(h9)

        cna_l = QHBoxLayout()
        label = QLabel("CNA")
        label.setFixedWidth(100)
        cna_l.addWidget(label)
        self.cna_label = QLabel()
        self.cna_label.setPixmap(cross)
        self.cna_label.setFixedWidth(100)
        cna_l.addWidget(self.cna_label)
        self.choose_cna_path_btn = QPushButton("Choose CNA directory")
        self.choose_cna_path_btn.setFixedWidth(300)
        cna_l.addWidget(self.choose_cna_path_btn)
        label2 = QLabel("")
        label2.setFixedWidth(30)
        cna_l.addWidget(label2)

        self.cna_path = QLabel()
        self.cna_path.setMinimumWidth(200)
        self.cna_path.setText(self.appdata.cna_path)
        cna_l.addWidget(self.cna_path)
        self.layout.addItem(cna_l)

        h2 = QHBoxLayout()
        label = QLabel("Default color for values in a scenario:")
        h2.addWidget(label)
        self.scen_color_btn = QPushButton()
        self.scen_color_btn.setFixedWidth(100)
        palette = self.scen_color_btn.palette()
        palette.setColor(QPalette.Button, self.appdata.scen_color)
        self.scen_color_btn.setPalette(palette)
        h2.addWidget(self.scen_color_btn)
        self.layout.addItem(h2)

        h3 = QHBoxLayout()
        label = QLabel(
            "Default color for computed values not part of the scenario:")
        h3.addWidget(label)
        self.comp_color_btn = QPushButton()
        self.comp_color_btn.setFixedWidth(100)
        palette = self.comp_color_btn.palette()
        palette.setColor(QPalette.Button, self.appdata.comp_color)
        self.comp_color_btn.setPalette(palette)
        h3.addWidget(self.comp_color_btn)
        self.layout.addItem(h3)

        h4 = QHBoxLayout()
        label = QLabel("Special Color used for non equal flux bounds:")
        h4.addWidget(label)
        self.spec1_color_btn = QPushButton()
        self.spec1_color_btn.setFixedWidth(100)
        palette = self.spec1_color_btn.palette()
        palette.setColor(QPalette.Button, self.appdata.special_color_1)
        self.spec1_color_btn.setPalette(palette)
        h4.addWidget(self.spec1_color_btn)
        self.layout.addItem(h4)

        h5 = QHBoxLayout()
        label = QLabel(
            "Special Color 2 used for non equal flux bounds that exclude 0:")
        h5.addWidget(label)
        self.spec2_color_btn = QPushButton()
        self.spec2_color_btn.setFixedWidth(100)
        palette = self.spec2_color_btn.palette()
        palette.setColor(QPalette.Button, self.appdata.special_color_2)
        self.spec2_color_btn.setPalette(palette)
        h5.addWidget(self.spec2_color_btn)
        self.layout.addItem(h5)

        h6 = QHBoxLayout()
        label = QLabel("Color used for empty reaction boxes:")
        h6.addWidget(label)
        self.default_color_btn = QPushButton()
        self.default_color_btn.setFixedWidth(100)
        palette = self.default_color_btn.palette()
        palette.setColor(QPalette.Button, self.appdata.default_color)
        self.default_color_btn.setPalette(palette)
        h6.addWidget(self.default_color_btn)
        self.layout.addItem(h6)

        h = QHBoxLayout()
        label = QLabel("Work directory:")
        h.addWidget(label)
        self.work_directory = QPushButton()
        self.work_directory.setText(self.appdata.work_directory)
        h.addWidget(self.work_directory)
        self.layout.addItem(h)

        h7 = QHBoxLayout()
        label = QLabel("Shown number of digits after the decimal point:")
        h7.addWidget(label)
        self.rounding = QLineEdit()
        self.rounding.setFixedWidth(100)
        self.rounding.setText(str(self.appdata.rounding))
        validator = QIntValidator(0, 20, self)
        self.rounding.setValidator(validator)
        h7.addWidget(self.rounding)
        self.layout.addItem(h7)

        h8 = QHBoxLayout()
        label = QLabel(
            "Absolute tolerance used to compare float values in the UI:")
        h8.addWidget(label)
        self.abs_tol = QLineEdit()
        self.abs_tol.setFixedWidth(100)
        self.abs_tol.setText(str(self.appdata.abs_tol))
        validator = QDoubleValidator(self)
        validator.setTop(1)
        self.abs_tol.setValidator(validator)
        h8.addWidget(self.abs_tol)
        self.layout.addItem(h8)

        l2 = QHBoxLayout()
        self.button = QPushButton("Apply Changes")
        self.cancel = QPushButton("Close")
        l2.addWidget(self.button)
        l2.addWidget(self.cancel)
        self.layout.addItem(l2)
        self.setLayout(self.layout)

        # Connecting the signal
        self.choose_ml_path_btn.clicked.connect(self.choose_ml_path)
        self.choose_oc_exe_btn.clicked.connect(self.choose_oc_exe)
        self.choose_cna_path_btn.clicked.connect(self.choose_cna_path)
        self.work_directory.clicked.connect(self.choose_work_directory)
        self.scen_color_btn.clicked.connect(self.choose_scen_color)
        self.comp_color_btn.clicked.connect(self.choose_comp_color)
        self.spec1_color_btn.clicked.connect(self.choose_spec1_color)
        self.spec2_color_btn.clicked.connect(self.choose_spec2_color)
        self.default_color_btn.clicked.connect(self.choose_default_color)
        self.selected_engine.currentTextChanged.connect(self.update)
        self.cancel.clicked.connect(self.reject)
        self.button.clicked.connect(self.apply)

        self.check_all()

        if self.meng is not None:
            self.selected_engine.insertItem(1, "Matlab")
            if self.appdata.selected_engine == "matlab":
                self.selected_engine.setCurrentIndex(1)
        if self.oeng is not None:
            self.selected_engine.insertItem(1, "Octave")
            if self.appdata.selected_engine == "octave":
                self.selected_engine.setCurrentIndex(1)

        self.update()

    def update(self):
        cross_icon = QIcon(":/icons/cross.png")
        cross = cross_icon.pixmap(QSize(32, 32))
        check_icon = QIcon(":/icons/check.png")
        check = check_icon.pixmap(QSize(32, 32))

        selected_engine = self.selected_engine.currentText()

        if selected_engine == "None":
            qmark_icon = QIcon(":/icons/qmark.png")
            qmark = qmark_icon.pixmap(QSize(32, 32))
            self.cna_label.setPixmap(qmark)
        else:
            if self.cna_ok:
                self.cna_label.setPixmap(check)
            else:
                self.cna_label.setPixmap(cross)
        if self.oeng is not None:
            # disable button if octave is already working
            self.oc_label.setPixmap(check)
        else:
            self.oc_label.setPixmap(cross)

        if self.meng is not None:
            self.ml_label.setPixmap(check)
        else:
            self.ml_label.setPixmap(cross)

        self.selected_engine.currentTextChanged.disconnect(self.update)
        self.selected_engine.clear()
        self.selected_engine.addItem("None")
        if self.meng is not None:
            self.selected_engine.addItem("Matlab")
        if self.oeng is not None:
            self.selected_engine.addItem("Octave")
#
        if selected_engine is "None":
            self.selected_engine.setCurrentIndex(0)
        if selected_engine == "Matlab":
            self.selected_engine.setCurrentIndex(1)
        if selected_engine == "Octave":
            if self.selected_engine.count() == 2:
                self.selected_engine.setCurrentIndex(1)
            elif self.selected_engine.count() == 3:
                self.selected_engine.setCurrentIndex(2)

        self.selected_engine.currentTextChanged.connect(self.update)

    def choose_ml_path(self):
        dialog = QFileDialog(self, directory=self.matlab_path.text())
        dialog.setFileMode(QFileDialog.DirectoryOnly)
        directory: str = dialog.getExistingDirectory()
        if not directory or len(
                directory) == 0 or not os.path.exists(directory):
            return

        self.choose_ml_path_btn.setEnabled(False)
        self.matlab_path.setText(directory)
        self.try_install_matlab_engine(directory)
        self.check_matlab()
        self.choose_ml_path_btn.setEnabled(True)
        self.update()

    def try_install_matlab_engine(self, directory: str):
        try:
            path = os.path.join(directory, 'extern/engines/python')
            cwd = os.getcwd()
            os.chdir(path)
            temp_dir = TemporaryDirectory()
            os.system("python setup.py build --build-base=" + temp_dir.name +
                      ' install')
            os.chdir(cwd)
        except FileNotFoundError:
            # no Matlab engine found
            pass

    def choose_oc_exe(self):

        dialog = QFileDialog(self, directory=self.oc_exe.text())
        filename: str = dialog.getOpenFileName(directory=os.getcwd())[0]
        if not filename or len(filename) == 0 or not os.path.exists(filename):
            return

        self.oc_exe.setText(filename)
        if os.path.isfile(filename):
            os.environ['OCTAVE_EXECUTABLE'] = filename

        self.choose_oc_exe_btn.setEnabled(False)
        self.check_octave()
        self.choose_oc_exe_btn.setEnabled(True)
        self.update()

    def check_all(self):
        self.check_octave()
        self.check_matlab()
        self.check_cna()

    def check_octave(self):
        if self.oeng is None:
            self.oeng = try_octave_engine(self.oc_exe.text())

    def check_matlab(self):
        # only recheck matlab if necessary
        if self.meng is None:
            self.meng = try_matlab_engine()

    def choose_cna_path(self):
        dialog = QFileDialog(self, directory=self.cna_path.text())
        dialog.setFileMode(QFileDialog.DirectoryOnly)
        directory: str = dialog.getExistingDirectory()
        if not directory or len(
                directory) == 0 or not os.path.exists(directory):
            return

        self.cna_path.setText(directory)
        self.update()
        self.reset_engine()
        self.check_cna()
        self.update()

    def choose_work_directory(self):
        dialog = QFileDialog(self, directory=self.work_directory.text())
        directory: str = dialog.getExistingDirectory()
        if not directory or len(
                directory) == 0 or not os.path.exists(directory):
            return
        self.work_directory.setText(directory)

    def reset_engine(self):
        # This resets the engines
        if self.oeng is None:
            self.meng = try_matlab_engine()
        else:
            self.oeng = try_octave_engine(self.oc_exe.text())

    def check_cna(self):

        if self.oeng is not None:
            self.cna_ok = try_cna(self.oeng, self.cna_path.text())
        elif self.meng is not None:
            self.cna_ok = try_cna(self.meng, self.cna_path.text())

    def choose_scen_color(self):
        palette = self.scen_color_btn.palette()
        initial = palette.color(QPalette.Button)

        dialog = QColorDialog(self)
        color: str = dialog.getColor(initial)
        if color.isValid():
            palette.setColor(QPalette.Button, color)
            self.scen_color_btn.setPalette(palette)

    def choose_comp_color(self):
        palette = self.comp_color_btn.palette()
        initial = palette.color(QPalette.Button)

        dialog = QColorDialog(self)
        color: str = dialog.getColor(initial)
        if color.isValid():
            palette.setColor(QPalette.Button, color)
            self.comp_color_btn.setPalette(palette)

    def choose_spec1_color(self):
        palette = self.spec1_color_btn.palette()
        initial = palette.color(QPalette.Button)

        dialog = QColorDialog(self)
        color: str = dialog.getColor(initial)
        if color.isValid():
            palette.setColor(QPalette.Button, color)
            self.spec1_color_btn.setPalette(palette)

    def choose_spec2_color(self):
        palette = self.spec2_color_btn.palette()
        initial = palette.color(QPalette.Button)

        dialog = QColorDialog(self)
        color: str = dialog.getColor(initial)
        if color.isValid():
            palette.setColor(QPalette.Button, color)
            self.spec2_color_btn.setPalette(palette)

    def choose_default_color(self):
        palette = self.default_color_btn.palette()
        initial = palette.color(QPalette.Button)

        dialog = QColorDialog(self)
        color: str = dialog.getColor(initial)
        if color.isValid():
            palette.setColor(QPalette.Button, color)
            self.default_color_btn.setPalette(palette)

    def apply(self):
        self.appdata.matlab_path = self.matlab_path.text()
        self.appdata.octave_executable = self.oc_exe.text()
        self.appdata.cna_path = self.cna_path.text()
        self.appdata.matlab_engine = self.meng
        self.appdata.octave_engine = self.oeng

        if self.selected_engine.currentText() == "None":
            self.appdata.selected_engine = None
        elif self.selected_engine.currentText() == "Matlab":
            self.appdata.selected_engine = "matlab"
        elif self.selected_engine.currentText() == "Octave":
            self.appdata.selected_engine = "octave"

        self.appdata.select_engine()

        self.appdata.window.disable_enable_dependent_actions()

        self.appdata.work_directory = self.work_directory.text()

        palette = self.scen_color_btn.palette()
        self.appdata.scen_color = palette.color(QPalette.Button)

        palette = self.comp_color_btn.palette()
        self.appdata.comp_color = palette.color(QPalette.Button)

        palette = self.spec1_color_btn.palette()
        self.appdata.special_color_1 = palette.color(QPalette.Button)

        palette = self.spec2_color_btn.palette()
        self.appdata.special_color_2 = palette.color(QPalette.Button)

        palette = self.default_color_btn.palette()
        self.appdata.default_color = palette.color(QPalette.Button)

        self.appdata.rounding = int(self.rounding.text())
        self.appdata.abs_tol = float(self.abs_tol.text())

        parser = configparser.ConfigParser()
        parser.add_section('cnapy-config')
        parser.set('cnapy-config', 'version', self.appdata.version)
        parser.set('cnapy-config', 'matlab_path', self.appdata.matlab_path)
        parser.set('cnapy-config', 'OCTAVE_EXECUTABLE',
                   self.appdata.octave_executable)
        parser.set('cnapy-config', 'work_directory',
                   self.appdata.work_directory)
        parser.set('cnapy-config', 'cna_path', self.appdata.cna_path)
        parser.set('cnapy-config', 'scen_color',
                   str(self.appdata.scen_color.rgb()))
        parser.set('cnapy-config', 'comp_color',
                   str(self.appdata.comp_color.rgb()))
        parser.set('cnapy-config', 'spec1_color',
                   str(self.appdata.special_color_1.rgb()))
        parser.set('cnapy-config', 'spec2_color',
                   str(self.appdata.special_color_2.rgb()))
        parser.set('cnapy-config', 'default_color',
                   str(self.appdata.default_color.rgb()))
        parser.set('cnapy-config', 'rounding', str(self.appdata.rounding))
        parser.set('cnapy-config', 'abs_tol', str(self.appdata.abs_tol))
        parser.set('cnapy-config', 'selected_engine',
                   str(self.appdata.selected_engine))

        try:
            fp = open(self.appdata.conf_path, "w")
        except FileNotFoundError:
            os.makedirs(
                appdirs.user_config_dir("cnapy", roaming=True,
                                        appauthor=False))
            fp = open(self.appdata.conf_path, "w")

        parser.write(fp)
        fp.close()

        self.accept()
예제 #25
0
class PyChopGui(QMainWindow):
    """
    GUI Class using PyQT for PyChop to help users plan inelastic neutron experiments
    at spallation sources by calculating the resolution and flux at a given neutron energies.
    """

    instruments = {}
    choppers = {}
    minE = {}
    maxE = {}

    def __init__(self):
        super(PyChopGui, self).__init__()
        self.folder = os.path.dirname(sys.modules[self.__module__].__file__)
        for fname in os.listdir(self.folder):
            if fname.endswith('.yaml'):
                instobj = Instrument(os.path.join(self.folder, fname))
                self.instruments[instobj.name] = instobj
                self.choppers[instobj.name] = instobj.getChopperNames()
                self.minE[instobj.name] = max([instobj.emin, 0.01])
                self.maxE[instobj.name] = instobj.emax
        self.drawLayout()
        self.setInstrument(list(self.instruments.keys())[0])
        self.resaxes_xlim = 0
        self.qeaxes_xlim = 0
        self.isFramePlotted = 0

    def setInstrument(self, instname):
        """
        Defines the instrument parameters by the name of the instrument.
        """
        self.engine = self.instruments[str(instname)]
        self.tabs.setTabEnabled(self.tdtabID, False)
        self.widgets['ChopperCombo']['Combo'].clear()
        self.widgets['FrequencyCombo']['Combo'].clear()
        self.widgets['FrequencyCombo']['Label'].setText('Frequency')
        self.widgets['PulseRemoverCombo']['Combo'].clear()
        for item in self.choppers[str(instname)]:
            self.widgets['ChopperCombo']['Combo'].addItem(item)
        rep = self.engine.moderator.source_rep
        maxfreq = self.engine.chopper_system.max_frequencies
        # At the moment, the GUI only supports up to two independent frequencies
        if not hasattr(maxfreq, '__len__') or len(maxfreq) == 1:
            self.widgets['PulseRemoverCombo']['Combo'].hide()
            self.widgets['PulseRemoverCombo']['Label'].hide()
            for fq in range(rep, (maxfreq[0] if hasattr(maxfreq, '__len__') else maxfreq) + 1, rep):
                self.widgets['FrequencyCombo']['Combo'].addItem(str(fq))
            if hasattr(self.engine.chopper_system, 'frequency_names'):
                self.widgets['FrequencyCombo']['Label'].setText(self.engine.chopper_system.frequency_names[0])
        else:
            self.widgets['PulseRemoverCombo']['Combo'].show()
            self.widgets['PulseRemoverCombo']['Label'].show()
            if hasattr(self.engine.chopper_system, 'frequency_names'):
                for idx, chp in enumerate([self.widgets['FrequencyCombo']['Label'], self.widgets['PulseRemoverCombo']['Label']]):
                    chp.setText(self.engine.chopper_system.frequency_names[idx])
            for fq in range(rep, maxfreq[0] + 1, rep):
                self.widgets['FrequencyCombo']['Combo'].addItem(str(fq))
            for fq in range(rep, maxfreq[1] + 1, rep):
                self.widgets['PulseRemoverCombo']['Combo'].addItem(str(fq))
        if len(self.engine.chopper_system.choppers) > 1:
            self.widgets['MultiRepCheck'].setEnabled(True)
            self.tabs.setTabEnabled(self.tdtabID, True)
        else:
            self.widgets['MultiRepCheck'].setEnabled(False)
            self.widgets['MultiRepCheck'].setChecked(False)
        self.widgets['Chopper2Phase']['Edit'].hide()
        self.widgets['Chopper2Phase']['Label'].hide()
        if self.engine.chopper_system.isPhaseIndependent:
            self.widgets['Chopper2Phase']['Edit'].show()
            self.widgets['Chopper2Phase']['Label'].show()
            self.widgets['Chopper2Phase']['Edit'].setText(str(self.engine.chopper_system.defaultPhase[0]))
            self.widgets['Chopper2Phase']['Label'].setText(self.engine.chopper_system.phaseNames[0])
            # Special case for MERLIN - hide phase control from normal users
            if 'MERLIN' in str(instname) and not self.instSciAct.isChecked():
                self.widgets['Chopper2Phase']['Edit'].hide()
                self.widgets['Chopper2Phase']['Label'].hide()
        self.engine.setChopper(str(self.widgets['ChopperCombo']['Combo'].currentText()))
        self.engine.setFrequency(float(self.widgets['FrequencyCombo']['Combo'].currentText()))
        val = self.flxslder.val * self.maxE[self.engine.instname] / 100
        self.flxedt.setText('%3.2f' % (val))
        nframe = self.engine.moderator.n_frame if hasattr(self.engine.moderator, 'n_frame') else 1
        self.repfig_nframe_edit.setText(str(nframe))
        self.repfig_nframe_rep1only.setChecked(False)
        if hasattr(self.engine.chopper_system, 'default_frequencies'):
            cb = [self.widgets['FrequencyCombo']['Combo'], self.widgets['PulseRemoverCombo']['Combo']]
            for idx, freq in enumerate(self.engine.chopper_system.default_frequencies):
                cb[idx].setCurrentIndex([i for i in range(cb[idx].count()) if str(freq) in cb[idx].itemText(i)][0])
                if idx > 1:
                    break
        self.tabs.setTabEnabled(self.qetabID, False)
        if self.engine.has_detector and hasattr(self.engine.detector, 'tthlims'):
            self.tabs.setTabEnabled(self.qetabID, True)

    def setChopper(self, choppername):
        """
        Defines the Fermi chopper slit package type by name, or the disk chopper arrangement variant.
        """
        self.engine.setChopper(str(choppername))
        self.engine.setFrequency(float(self.widgets['FrequencyCombo']['Combo'].currentText()))
        # Special case for MERLIN - only enable multirep for 'G' chopper
        if 'MERLIN' in self.engine.instname:
            if 'G' in str(choppername):
                self.widgets['MultiRepCheck'].setEnabled(True)
                self.tabs.setTabEnabled(self.tdtabID, True)
                self.widgets['Chopper2Phase']['Edit'].setText('1500')
                self.widgets['Chopper2Phase']['Label'].setText('Disk chopper phase delay time')
                if self.instSciAct.isChecked():
                    self.widgets['Chopper2Phase']['Edit'].show()
                    self.widgets['Chopper2Phase']['Label'].show()
            else:
                self.widgets['MultiRepCheck'].setEnabled(False)
                self.widgets['MultiRepCheck'].setChecked(False)
                self.tabs.setTabEnabled(self.tdtabID, False)
                self.widgets['Chopper2Phase']['Edit'].hide()
                self.widgets['Chopper2Phase']['Label'].hide()

    def setFreq(self, freqtext=None, **kwargs):
        """
        Sets the chopper frequency(ies), in Hz.
        """
        freq_gui = float(self.widgets['FrequencyCombo']['Combo'].currentText())
        freq_in = kwargs['manual_freq'] if ('manual_freq' in kwargs.keys()) else freq_gui
        if len(self.engine.getFrequency()) > 1 and (not hasattr(freq_in, '__len__') or len(freq_in)==1):
            freqpr = float(self.widgets['PulseRemoverCombo']['Combo'].currentText())
            freq_in = [freq_in, freqpr]
        if not self.widgets['Chopper2Phase']['Label'].isHidden():
            chop2phase = self.widgets['Chopper2Phase']['Edit'].text()
            if isinstance(self.engine.chopper_system.defaultPhase[0], string_types):
                chop2phase = str(chop2phase)
            else:
                chop2phase = float(chop2phase) % (1e6 / self.engine.moderator.source_rep)
            self.engine.setFrequency(freq_in, phase=chop2phase)
        else:
            self.engine.setFrequency(freq_in)

    def setEi(self):
        """
        Sets the incident energy (or focused incident energy for multi-rep case).
        """
        try:
            eitxt = float(self.widgets['EiEdit']['Edit'].text())
            self.engine.setEi(eitxt)
            if self.eiPlots.isChecked():
                self.calc_callback()
        except ValueError:
            raise ValueError('No Ei specified, or Ei string not understood')

    def calc_callback(self):
        """
        Calls routines to calculate the resolution / flux and to update the Matplotlib graphs.
        """
        try:
            if self.engine.getChopper() is None:
                self.setChopper(self.widgets['ChopperCombo']['Combo'].currentText())
            self.setEi()
            self.setFreq()
            self.calculate()
            if self.errormess:
                idx = [i for i, ei in enumerate(self.eis) if np.abs(ei - self.engine.getEi()) < 1.e-4]
                if idx and self.flux[idx[0]] == 0:
                    raise ValueError(self.errormess)
                self.errormessage(self.errormess)
            self.plot_res()
            self.plot_frame()
            if self.instSciAct.isChecked():
                self.update_script()
        except ValueError as err:
            self.errormessage(err)
        self.plot_flux_ei()
        self.plot_flux_hz()

    def calculate(self):
        """
        Performs the resolution and flux calculations.
        """
        self.errormess = None
        if self.engine.getEi() is None:
            self.setEi()
        if self.widgets['MultiRepCheck'].isChecked():
            en = np.linspace(0, 0.95, 200)
            self.eis = self.engine.getAllowedEi()
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter('always', UserWarning)
                self.res = self.engine.getMultiRepResolution(en)
                self.flux = self.engine.getMultiRepFlux()
                if len(w) > 0:
                    mess = [str(w[i].message) for i in range(len(w))]
                    self.errormess = '\n'.join([m for m in mess if 'tchop' in m])
        else:
            en = np.linspace(0, 0.95*self.engine.getEi(), 200)
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter('always', UserWarning)
                self.res = self.engine.getResolution(en)
                self.flux = self.engine.getFlux()
                if len(w) > 0:
                    raise ValueError(w[0].message)

    def _set_overplot(self, overplot, axisname):
        axis = getattr(self, axisname)
        if overplot:
            if matplotlib.compare_versions('2.1.0',matplotlib.__version__):
                axis.hold(True)
        else:
            setattr(self, axisname+'_xlim', 0)
            axis.clear()
            axis.axhline(color='k')

    def plot_res(self):
        """
        Plots the resolution in the resolution tab
        """
        overplot = self.widgets['HoldCheck'].isChecked()
        multiplot = self.widgets['MultiRepCheck'].isChecked()
        self._set_overplot(overplot, 'resaxes')
        self._set_overplot(overplot, 'qeaxes')
        inst = self.engine.instname
        freq = self.engine.getFrequency()
        if hasattr(freq, '__len__'):
            freq = freq[0]
        if multiplot:
            if matplotlib.compare_versions('2.1.0',matplotlib.__version__):
                self.resaxes.hold(True)
            for ie, Ei in enumerate(self.eis):
                en = np.linspace(0, 0.95*Ei, 200)
                if any(self.res[ie]):
                    if not self.flux[ie]:
                        continue
                    line, = self.resaxes.plot(en, self.res[ie])
                    label_text = '%s_%3.2fmeV_%dHz_Flux=%fn/cm2/s' % (inst, Ei, freq, self.flux[ie])
                    line.set_label(label_text)
                    if self.tabs.isTabEnabled(self.qetabID):
                        self.plot_qe(Ei, label_text, hold=True)
                    self.resaxes_xlim = max(Ei, self.resaxes_xlim)
            if matplotlib.compare_versions('2.1.0',matplotlib.__version__):
                self.resaxes.hold(False)
        else:
            ei = self.engine.getEi()
            en = np.linspace(0, 0.95*ei, 200)
            line, = self.resaxes.plot(en, self.res)
            chopper = self.engine.getChopper()
            label_text = '%s_%s_%3.2fmeV_%dHz_Flux=%fn/cm2/s' % (inst, chopper, ei, freq, self.flux)
            line.set_label(label_text)
            if self.tabs.isTabEnabled(self.qetabID):
                self.plot_qe(ei, label_text, overplot)
            self.resaxes_xlim = max(ei, self.resaxes_xlim)
        self.resaxes.set_xlim([0, self.resaxes_xlim])
        self.resaxes.legend().draggable()
        self.resaxes.set_xlabel('Energy Transfer (meV)')
        self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)')
        self.rescanvas.draw()

    def plot_qe(self, Ei, label_text, hold=False):
        """ Plots the Q-E diagram """
        from scipy import constants
        E2q, meV2J = (2. * constants.m_n / (constants.hbar ** 2), constants.e / 1000.)
        en = np.linspace(-Ei / 5., Ei, 100)
        q2 = []
        for tth in self.engine.detector.tthlims:
            q = np.sqrt(E2q * (2 * Ei - en - 2 * np.sqrt(Ei * (Ei - en)) * np.cos(np.deg2rad(tth))) * meV2J) / 1e10
            q2.append(np.concatenate((np.flipud(q), q)))
        self._set_overplot(hold, 'qeaxes')
        self.qeaxes_xlim = max(np.max(q2), self.qeaxes_xlim)
        line, = self.qeaxes.plot(np.hstack(q2), np.concatenate((np.flipud(en), en)).tolist() * len(self.engine.detector.tthlims))
        line.set_label(label_text)
        self.qeaxes.set_xlim([0, self.qeaxes_xlim])
        self.qeaxes.legend().draggable()
        self.qeaxes.set_xlabel(r'$|Q| (\mathrm{\AA}^{-1})$')
        self.qeaxes.set_ylabel('Energy Transfer (meV)')
        self.qecanvas.draw()

    def plot_flux_ei(self, **kwargs):
        """
        Plots the flux vs Ei in the middle tab
        """
        inst = self.engine.instname
        chop = self.engine.getChopper()
        freq = self.engine.getFrequency()
        overplot = self.widgets['HoldCheck'].isChecked()
        if hasattr(freq, '__len__'):
            freq = freq[0]
        update = kwargs['update'] if 'update' in kwargs.keys() else False
        # Do not recalculate if all relevant parameters still the same.
        _, labels = self.flxaxes2.get_legend_handles_labels()
        searchStr = '([A-Z]+) "(.+)" ([0-9]+) Hz'
        tmpinst = []
        if (labels and (overplot or len(labels) == 1)) or update:
            for prevtitle in labels:
                prevInst, prevChop, prevFreq = re.search(searchStr, prevtitle).groups()
                if update:
                    tmpinst.append(copy.deepcopy(Instrument(self.instruments[prevInst], prevChop, float(prevFreq))))
                else:
                    if inst == prevInst and chop == prevChop and freq == float(prevFreq):
                        return
        ne = 25
        mn = self.minE[inst]
        mx = (self.flxslder.val/100)*self.maxE[inst]
        eis = np.linspace(mn, mx, ne)
        flux = eis*0
        elres = eis*0
        if update:
            self.flxaxes1.clear()
            self.flxaxes2.clear()
            if matplotlib.compare_versions('2.1.0',matplotlib.__version__):
                self.flxaxes1.hold(True)
                self.flxaxes2.hold(True)
            for ii, instrument in enumerate(tmpinst):
                for ie, ei in enumerate(eis):
                    with warnings.catch_warnings(record=True):
                        warnings.simplefilter('always', UserWarning)
                        flux[ie] = instrument.getFlux(ei)
                        elres[ie] = instrument.getResolution(0., ei)[0]
                self.flxaxes1.plot(eis, flux)
                line, = self.flxaxes2.plot(eis, elres)
                line.set_label(labels[ii])
        else:
            for ie, ei in enumerate(eis):
                with warnings.catch_warnings(record=True):
                    warnings.simplefilter('always', UserWarning)
                    flux[ie] = self.engine.getFlux(ei)
                    elres[ie] = self.engine.getResolution(0., ei)[0]
            if overplot:
                if matplotlib.compare_versions('2.1.0',matplotlib.__version__):
                    self.flxaxes1.hold(True)
                    self.flxaxes2.hold(True)
            else:
                self.flxaxes1.clear()
                self.flxaxes2.clear()
            self.flxaxes1.plot(eis, flux)
            line, = self.flxaxes2.plot(eis, elres)
            line.set_label('%s "%s" %d Hz' % (inst, chop, freq))
        self.flxaxes1.set_xlim([mn, mx])
        self.flxaxes2.set_xlim([mn, mx])
        self.flxaxes1.set_xlabel('Incident Energy (meV)')
        self.flxaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        self.flxaxes1.set_xlabel('Incident Energy (meV)')
        self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        lg = self.flxaxes2.legend()
        lg.draggable()
        self.flxcanvas.draw()

    def update_slider(self, val=None):
        """
        Callback function for the x-axis slider of the flux tab
        """
        if val is None:
            val = float(self.flxedt.text()) / self.maxE[self.engine.instname] * 100
            if val < self.minE[self.engine.instname]:
                self.errormessage("Max Ei must be greater than %2.1f" % (self.minE[self.engine.instname]))
                val = (self.minE[self.engine.instname]+0.1) / self.maxE[self.engine.instname] * 100
            self.flxslder.set_val(val)
        else:
            val = self.flxslder.val * self.maxE[self.engine.instname] / 100
            self.flxedt.setText('%3.2f' % (val))
        self.plot_flux_ei(update=True)
        self.flxcanvas.draw()

    def plot_flux_hz(self):
        """
        Plots the flux vs freq in the middle tab
        """
        inst = self.engine.instname
        chop = self.engine.getChopper()
        ei = float(self.widgets['EiEdit']['Edit'].text())
        overplot = self.widgets['HoldCheck'].isChecked()
        # Do not recalculate if one of the plots has the same parametersc
        _, labels = self.frqaxes2.get_legend_handles_labels()
        searchStr = '([A-Z]+) "(.+)" Ei = ([0-9.-]+) meV'
        if labels and (overplot or len(labels) == 1):
            for prevtitle in labels:
                prevInst, prevChop, prevEi = re.search(searchStr, prevtitle).groups()
                if inst == prevInst and chop == prevChop and abs(ei-float(prevEi)) < 0.01:
                    return
        freq0 = self.engine.getFrequency()
        rep = self.engine.moderator.source_rep
        maxfreq = self.engine.chopper_system.max_frequencies
        freqs = range(rep, (maxfreq[0] if hasattr(maxfreq, '__len__') else maxfreq) + 1, rep)
        flux = np.zeros(len(freqs))
        elres = np.zeros(len(freqs))
        for ie, freq in enumerate(freqs):
            if hasattr(freq0, '__len__'):
                self.setFreq(manual_freq=[freq] + freq0[1:])
            else:
                self.setFreq(manual_freq=freq)
            with warnings.catch_warnings(record=True):
                warnings.simplefilter('always', UserWarning)
                flux[ie] = self.engine.getFlux(ei)
                elres[ie] = self.engine.getResolution(0., ei)[0]
        if overplot:
            if matplotlib.compare_versions('2.1.0',matplotlib.__version__):
                self.frqaxes1.hold(True)
                self.frqaxes2.hold(True)
        else:
            self.frqaxes1.clear()
            self.frqaxes2.clear()
        self.setFreq(manual_freq=freq0)
        self.frqaxes1.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        line, = self.frqaxes1.plot(freqs, flux, 'o-')
        self.frqaxes1.set_xlim([0, np.max(freqs)])
        self.frqaxes2.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        line, = self.frqaxes2.plot(freqs, elres, 'o-')
        line.set_label('%s "%s" Ei = %5.3f meV' % (inst, chop, ei))
        lg = self.frqaxes2.legend()
        lg.draggable()
        self.frqaxes2.set_xlim([0, np.max(freqs)])
        self.frqcanvas.draw()

    def instSciCB(self):
        """
        Callback function for the "Instrument Scientist Mode" menu option
        """
        # MERLIN is a special case - want to hide ability to change phase from users
        if 'MERLIN' in self.engine.instname and 'G' in self.engine.getChopper():
            if self.instSciAct.isChecked():
                self.widgets['Chopper2Phase']['Edit'].show()
                self.widgets['Chopper2Phase']['Label'].show()
                self.widgets['Chopper2Phase']['Edit'].setText('1500')
                self.widgets['Chopper2Phase']['Label'].setText('Disk chopper phase delay time')
            else:
                self.widgets['Chopper2Phase']['Edit'].hide()
                self.widgets['Chopper2Phase']['Label'].hide()
        if self.instSciAct.isChecked():
            self.tabs.insertTab(self.scrtabID, self.scrtab, 'ScriptOutput')
            self.scrtab.show()
        else:
            self.tabs.removeTab(self.scrtabID)
            self.scrtab.hide()

    def errormessage(self, message):
        msg = QMessageBox()
        msg.setText(str(message))
        msg.setStandardButtons(QMessageBox.Ok)
        msg.exec_()

    def loadYaml(self):
        yaml_file = QFileDialog().getOpenFileName(self.mainWidget, 'Open Instrument YAML File', self.folder, 'Files (*.yaml)')
        if isinstance(yaml_file, tuple):
            yaml_file = yaml_file[0]
        yaml_file = str(yaml_file)
        new_folder = os.path.dirname(yaml_file)
        if new_folder != self.folder:
            self.folder = new_folder
        try:
            new_inst = Instrument(yaml_file)
        except (RuntimeError, AttributeError, ValueError) as err:
            self.errormessage(err)
        newname = new_inst.name
        if newname in self.instruments.keys() and not self.overwriteload.isChecked():
            overwrite, newname = self._ask_overwrite()
            if overwrite == 1:
                return
            elif overwrite == 0:
                newname = new_inst.name
        self.instruments[newname] = new_inst
        self.choppers[newname] = new_inst.getChopperNames()
        self.minE[newname] = max([new_inst.emin, 0.01])
        self.maxE[newname] = new_inst.emax
        self.updateInstrumentList()
        combo = self.widgets['InstrumentCombo']['Combo']
        idx = [i for i in range(combo.count()) if str(combo.itemText(i)) == newname]
        combo.setCurrentIndex(idx[0])
        self.setInstrument(newname)

    def _ask_overwrite(self):
        msg = QDialog()
        msg.setWindowTitle('Load overwrite')
        layout = QGridLayout()
        layout.addWidget(QLabel('Instrument %s already exists in memory. Overwrite this?'), 0, 0, 1, -1)
        buttons = [QPushButton(label) for label in ['Load and overwrite', 'Cancel Load', 'Load and rename to']]
        locations = [[1, 0], [1, 1], [2, 0]]
        self.overwrite_flag = 1

        def overwriteCB(idx):
            self.overwrite_flag = idx
            msg.accept()
        for idx, button in enumerate(buttons):
            button.clicked.connect(lambda _, idx=idx: overwriteCB(idx))
            layout.addWidget(button, locations[idx][0], locations[idx][1])
        newname = QLineEdit()
        newname.editingFinished.connect(lambda: overwriteCB(2))
        layout.addWidget(newname, 2, 1)
        msg.setLayout(layout)
        msg.exec_()
        newname = str(newname.text())
        if not newname or newname in self.instruments:
            self.errormessage('Invalid instrument name. Cancelling load.')
            self.overwrite_flag = 1
        return self.overwrite_flag, newname

    def updateInstrumentList(self):
        combo = self.widgets['InstrumentCombo']['Combo']
        old_instruments = [str(combo.itemText(i)) for i in range(combo.count())]
        new_instruments = [inst for inst in self.instruments if inst not in old_instruments]
        for inst in new_instruments:
            combo.addItem(inst)

    def plot_frame(self):
        """
        Plots the distance-time diagram in the right tab
        """
        if len(self.engine.chopper_system.choppers) > 1:
            self.engine.n_frame = int(self.repfig_nframe_edit.text())
            self.repaxes.clear()
            self.engine.plotMultiRepFrame(self.repaxes, first_rep=self.repfig_nframe_rep1only.isChecked())
            self.repcanvas.draw()

    def _gen_text_ei(self, ei, obj_in):
        obj = Instrument(obj_in)
        obj.setEi(ei)
        en = np.linspace(0, 0.95*ei, 10)
        try:
            flux = self.engine.getFlux()
            res = self.engine.getResolution(en)
        except ValueError as err:
            self.errormessage(err)
            raise ValueError(err)
        tsqvan, tsqdic, tsqmodchop = obj.getVanVar()
        v_mod, v_chop = tuple(np.sqrt(tsqmodchop[:2]) * 1e6)
        x0, _, x1, x2, _ = obj.chopper_system.getDistances()
        first_component = 'moderator'
        if x0 != tsqmodchop[2]:
            x0 = tsqmodchop[2]
            first_component = 'chopper 1'
        txt = '# ------------------------------------------------------------- #\n'
        txt += '# Ei = %8.2f meV\n' % (ei)
        txt += '# Flux = %8.2f n/cm2/s\n' % (flux)
        txt += '# Elastic resolution = %6.2f meV\n' % (res[0])
        txt += '# Time width at sample = %6.2f us, of which:\n' % (1e6*np.sqrt(tsqvan))
        for ky, val in list(tsqdic.items()):
            txt += '#     %20s : %6.2f us\n' % (ky, 1e6*np.sqrt(val))
        txt += '# %s distances:\n' % (obj.instname)
        txt += '#     x0 = %6.2f m (%s to Fermi)\n' % (x0, first_component)
        txt += '#     x1 = %6.2f m (Fermi to sample)\n' % (x1)
        txt += '#     x2 = %6.2f m (sample to detector)\n' % (x2)
        txt += '# Approximate inelastic resolution is given by:\n'
        txt += '#     dE = 2 * E2V * sqrt(ef**3 * t_van**2) / x2\n'
        txt += '#     where:  E2V = 4.373e-4 meV/(m/us) conversion from energy to speed\n'
        txt += '#             t_van**2 = (geom*t_mod)**2 + ((1+geom)*t_chop)**2\n'
        txt += '#             geom = (x1 + x2*(ei/ef)**1.5) / x0\n'
        txt += '#     and t_mod and t_chop are the moderator and chopper time widths at the\n'
        txt += '#     moderator and chopper positions (not at the sample as listed above).\n'
        txt += '# Which in this case is:\n'
        txt += '#     %.4e*sqrt(ef**3 * ( (%6.5f*(%.3f+%.3f*(ei/ef)**1.5))**2 \n' % (874.78672e-6/x2, v_mod, x1/x0, x2/x0)
        txt += '#                              + (%6.5f*(%.3f+%.3f*(ei/ef)**1.5))**2) )\n' % (v_chop, 1+x1/x0, x2/x0)
        txt += '#  EN (meV)   Full dE (meV)   Approx dE (meV)\n'
        for ii in range(len(res)):
            ef = ei-en[ii]
            approx = (874.78672e-6/x2)*np.sqrt(ef**3 * ((v_mod*((x1/x0)+(x2/x0)*(ei/ef)**1.5))**2
                                                        + (v_chop*(1+(x1/x0)+(x2/x0)*(ei/ef)**1.5))**2))
            txt += '%12.5f %12.5f %12.5f\n' % (en[ii], res[ii], approx)
        return txt

    def genText(self):
        """
        Generates text output of the resolution function versus energy transfer and other information.
        """
        multiplot = self.widgets['MultiRepCheck'].isChecked()
        obj = self.engine
        if obj.getChopper() is None:
            self.setChopper(self.widgets['ChopperCombo']['Combo'].currentText())
        if obj.getEi() is None:
            self.setEi()
        instname, chtyp, freqs, ei_in = tuple([obj.instname, obj.getChopper(), obj.getFrequency(), obj.getEi()])
        txt = '# ------------------------------------------------------------- #\n'
        txt += '# Chop calculation for instrument %s\n' % (instname)
        if obj.isFermi:
            txt += '#     with chopper %s at %3i Hz\n' % (chtyp, freqs[0])
        else:
            txt += '#     in %s mode with:\n' % (chtyp)
            freq_names = obj.chopper_system.frequency_names
            for idx in range(len(freq_names)):
                txt += '#     %s at %3i Hz\n' % (freq_names[idx], freqs[idx])
        txt += self._gen_text_ei(ei_in, obj)
        if multiplot:
            for ei in sorted(self.engine.getAllowedEi()):
                if np.abs(ei - ei_in) > 0.001:
                    txt += self._gen_text_ei(ei, obj)
        return txt

    def showText(self):
        """
        Creates a dialog to show the generated text output.
        """
        try:
            generatedText = self.genText()
        except ValueError:
            return
        self.txtwin = QDialog()
        self.txtedt = QTextEdit()
        self.txtbtn = QPushButton('OK')
        self.txtwin.layout = QVBoxLayout(self.txtwin)
        self.txtwin.layout.addWidget(self.txtedt)
        self.txtwin.layout.addWidget(self.txtbtn)
        self.txtbtn.clicked.connect(self.txtwin.deleteLater)
        self.txtedt.setText(generatedText)
        self.txtedt.setReadOnly(True)
        self.txtwin.setWindowTitle('Resolution information')
        self.txtwin.setWindowModality(Qt.ApplicationModal)
        self.txtwin.setAttribute(Qt.WA_DeleteOnClose)
        self.txtwin.setMinimumSize(400, 600)
        self.txtwin.resize(400, 600)
        self.txtwin.show()
        self.txtloop = QEventLoop()
        self.txtloop.exec_()

    def saveText(self):
        """
        Saves the generated text to a file (opens file dialog).
        """
        fname = QFileDialog.getSaveFileName(self, 'Open file', '')
        if isinstance(fname, tuple):
            fname = fname[0]
        fid = open(fname, 'w')
        fid.write(self.genText())
        fid.close()

    def update_script(self):
        """
        Updates the text window with information about the previous calculation.
        """
        if self.widgets['MultiRepCheck'].isChecked():
            out = self.engine.getMultiWidths()
            new_str = '\n'
            for ie, ee in enumerate(out['Eis']):
                res = out['Energy'][ie]
                percent = res / ee * 100
                chop_width = out['chopper'][ie]
                mod_width = out['moderator'][ie]
                new_str += 'Ei is %6.2f meV, resolution is %6.2f ueV, percentage resolution is %6.3f\n' % (ee, res * 1000, percent)
                new_str += 'FWHM at sample from chopper and moderator are %6.2f us, %6.2f us\n' % (chop_width, mod_width)
        else:
            ei =  self.engine.getEi()
            out = self.engine.getWidths()
            res = out['Energy']
            percent = res / ei * 100
            chop_width = out['chopper']
            mod_width = out['moderator']
            new_str = '\nEi is %6.2f meV, resolution is %6.2f ueV, percentage resolution is %6.3f\n' % (ei, res * 1000, percent)
            new_str += 'FWHM at sample from chopper and moderator are %6.2f us, %6.2f us\n' % (chop_width, mod_width)
        self.scredt.append(new_str)

    def onHelp(self):
        """
        Shows the help page
        """
        try:
            from pymantidplot.proxies import showCustomInterfaceHelp
            showCustomInterfaceHelp("PyChop")
        except ImportError:
            helpTxt = "PyChop is a tool to allow direct inelastic neutron\nscattering users to estimate the inelastic resolution\n"
            helpTxt += "and incident flux for a given spectrometer setting.\n\nFirst select the instrument, chopper settings and\n"
            helpTxt += "Ei, and then click 'Calculate and Plot'. Data for all\nthe graphs will be generated (may take 1-2s) and\n"
            helpTxt += "all graphs will be updated. If the 'Hold current plot'\ncheck box is ticked, additional settings will be\n"
            helpTxt += "overplotted on the existing graphs if they are\ndifferent from previous settings.\n\nMore in-depth help "
            helpTxt += "can be obtained from the\nMantid help pages."
            self.hlpwin = QDialog()
            self.hlpedt = QLabel(helpTxt)
            self.hlpbtn = QPushButton('OK')
            self.hlpwin.layout = QVBoxLayout(self.hlpwin)
            self.hlpwin.layout.addWidget(self.hlpedt)
            self.hlpwin.layout.addWidget(self.hlpbtn)
            self.hlpbtn.clicked.connect(self.hlpwin.deleteLater)
            self.hlpwin.setWindowTitle('Help')
            self.hlpwin.setWindowModality(Qt.ApplicationModal)
            self.hlpwin.setAttribute(Qt.WA_DeleteOnClose)
            self.hlpwin.setMinimumSize(370, 300)
            self.hlpwin.resize(370, 300)
            self.hlpwin.show()
            self.hlploop = QEventLoop()
            self.hlploop.exec_()

    def drawLayout(self):
        """
        Draws the GUI layout.
        """
        self.widgetslist = [
            ['pair', 'show', 'Instrument', 'combo', self.instruments, self.setInstrument, 'InstrumentCombo'],
            ['pair', 'show', 'Chopper', 'combo', '', self.setChopper, 'ChopperCombo'],
            ['pair', 'show', 'Frequency', 'combo', '', self.setFreq, 'FrequencyCombo'],
            ['pair', 'hide', 'Pulse remover chopper freq', 'combo', '', self.setFreq, 'PulseRemoverCombo'],
            ['pair', 'show', 'Ei', 'edit', '', self.setEi, 'EiEdit'],
            ['pair', 'hide', 'Chopper 2 phase delay time', 'edit', '5', self.setFreq, 'Chopper2Phase'],
            ['spacer'],
            ['single', 'show', 'Calculate and Plot', 'button', self.calc_callback, 'CalculateButton'],
            ['single', 'show', 'Hold current plot', 'check', lambda: None, 'HoldCheck'],
            ['single', 'show', 'Show multi-reps', 'check', lambda: None, 'MultiRepCheck'],
            ['spacer'],
            ['single', 'show', 'Show data ascii window', 'button', self.showText, 'ShowAsciiButton'],
            ['single', 'show', 'Save data as ascii', 'button', self.saveText, 'SaveAsciiButton']
        ]
        self.droplabels = []
        self.dropboxes = []
        self.singles = []
        self.widgets = {}

        self.leftPanel = QVBoxLayout()
        self.rightPanel = QVBoxLayout()
        self.tabs = QTabWidget(self)
        self.fullWindow = QGridLayout()
        for widget in self.widgetslist:
            if 'pair' in widget[0]:
                self.droplabels.append(QLabel(widget[2]))
                if 'combo' in widget[3]:
                    self.dropboxes.append(QComboBox(self))
                    self.dropboxes[-1].activated['QString'].connect(widget[5])
                    for item in widget[4]:
                        self.dropboxes[-1].addItem(item)
                    self.widgets[widget[-1]] = {'Combo':self.dropboxes[-1], 'Label':self.droplabels[-1]}
                elif 'edit' in widget[3]:
                    self.dropboxes.append(QLineEdit(self))
                    self.dropboxes[-1].returnPressed.connect(widget[5])
                    self.widgets[widget[-1]] = {'Edit':self.dropboxes[-1], 'Label':self.droplabels[-1]}
                else:
                    raise RuntimeError('Bug in code - widget %s is not recognised.' % (widget[3]))
                self.leftPanel.addWidget(self.droplabels[-1])
                self.leftPanel.addWidget(self.dropboxes[-1])
                if 'hide' in widget[1]:
                    self.droplabels[-1].hide()
                    self.dropboxes[-1].hide()
            elif 'single' in widget[0]:
                if 'check' in widget[3]:
                    self.singles.append(QCheckBox(widget[2], self))
                    self.singles[-1].stateChanged.connect(widget[4])
                elif 'button' in widget[3]:
                    self.singles.append(QPushButton(widget[2]))
                    self.singles[-1].clicked.connect(widget[4])
                else:
                    raise RuntimeError('Bug in code - widget %s is not recognised.' % (widget[3]))
                self.leftPanel.addWidget(self.singles[-1])
                if 'hide' in widget[1]:
                    self.singles[-1].hide()
                self.widgets[widget[-1]] = self.singles[-1]
            elif 'spacer' in widget[0]:
                self.leftPanel.addItem(QSpacerItem(0, 35))
            else:
                raise RuntimeError('Bug in code - widget class %s is not recognised.' % (widget[0]))

        # Right panel, matplotlib figures
        self.resfig = Figure()
        self.resfig.patch.set_facecolor('white')
        self.rescanvas = FigureCanvas(self.resfig)
        self.resaxes = self.resfig.add_subplot(111)
        self.resaxes.axhline(color='k')
        self.resaxes.set_xlabel('Energy Transfer (meV)')
        self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)')
        self.resfig_controls = NavigationToolbar(self.rescanvas, self)
        self.restab = QWidget(self.tabs)
        self.restabbox = QVBoxLayout()
        self.restabbox.addWidget(self.rescanvas)
        self.restabbox.addWidget(self.resfig_controls)
        self.restab.setLayout(self.restabbox)

        self.flxfig = Figure()
        self.flxfig.patch.set_facecolor('white')
        self.flxcanvas = FigureCanvas(self.flxfig)
        self.flxaxes1 = self.flxfig.add_subplot(121)
        self.flxaxes1.set_xlabel('Incident Energy (meV)')
        self.flxaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        self.flxaxes2 = self.flxfig.add_subplot(122)
        self.flxaxes2.set_xlabel('Incident Energy (meV)')
        self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        self.flxfig_controls = NavigationToolbar(self.flxcanvas, self)
        self.flxsldfg = Figure()
        self.flxsldfg.patch.set_facecolor('white')
        self.flxsldcv = FigureCanvas(self.flxsldfg)
        self.flxsldax = self.flxsldfg.add_subplot(111)
        self.flxslder = Slider(self.flxsldax, 'Ei (meV)', 0, 100, valinit=100)
        self.flxslder.valtext.set_visible(False)
        self.flxslder.on_changed(self.update_slider)
        self.flxedt = QLineEdit()
        self.flxedt.setText('1000')
        self.flxedt.returnPressed.connect(self.update_slider)
        self.flxtab = QWidget(self.tabs)
        self.flxsldbox = QHBoxLayout()
        self.flxsldbox.addWidget(self.flxsldcv)
        self.flxsldbox.addWidget(self.flxedt)
        self.flxsldwdg = QWidget()
        self.flxsldwdg.setLayout(self.flxsldbox)
        sz = self.flxsldwdg.maximumSize()
        sz.setHeight(50)
        self.flxsldwdg.setMaximumSize(sz)
        self.flxtabbox = QVBoxLayout()
        self.flxtabbox.addWidget(self.flxcanvas)
        self.flxtabbox.addWidget(self.flxsldwdg)
        self.flxtabbox.addWidget(self.flxfig_controls)
        self.flxtab.setLayout(self.flxtabbox)

        self.frqfig = Figure()
        self.frqfig.patch.set_facecolor('white')
        self.frqcanvas = FigureCanvas(self.frqfig)
        self.frqaxes1 = self.frqfig.add_subplot(121)
        self.frqaxes1.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes1.set_ylabel('Flux (n/cm$^2$/s)')
        self.frqaxes2 = self.frqfig.add_subplot(122)
        self.frqaxes1.set_xlabel('Chopper Frequency (Hz)')
        self.frqaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
        self.frqfig_controls = NavigationToolbar(self.frqcanvas, self)
        self.frqtab = QWidget(self.tabs)
        self.frqtabbox = QVBoxLayout()
        self.frqtabbox.addWidget(self.frqcanvas)
        self.frqtabbox.addWidget(self.frqfig_controls)
        self.frqtab.setLayout(self.frqtabbox)

        self.repfig = Figure()
        self.repfig.patch.set_facecolor('white')
        self.repcanvas = FigureCanvas(self.repfig)
        self.repaxes = self.repfig.add_subplot(111)
        self.repaxes.axhline(color='k')
        self.repaxes.set_xlabel(r'TOF ($\mu$sec)')
        self.repaxes.set_ylabel('Distance (m)')
        self.repfig_controls = NavigationToolbar(self.repcanvas, self)
        self.repfig_nframe_label = QLabel('Number of frames to plot')
        self.repfig_nframe_edit = QLineEdit('1')
        self.repfig_nframe_button = QPushButton('Replot')
        self.repfig_nframe_button.clicked.connect(lambda: self.plot_frame())
        self.repfig_nframe_rep1only = QCheckBox('First Rep Only')
        self.repfig_nframe_box = QHBoxLayout()
        self.repfig_nframe_box.addWidget(self.repfig_nframe_label)
        self.repfig_nframe_box.addWidget(self.repfig_nframe_edit)
        self.repfig_nframe_box.addWidget(self.repfig_nframe_button)
        self.repfig_nframe_box.addWidget(self.repfig_nframe_rep1only)
        self.reptab = QWidget(self.tabs)
        self.repfig_nframe = QWidget(self.reptab)
        self.repfig_nframe.setLayout(self.repfig_nframe_box)
        self.repfig_nframe.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed))
        self.reptabbox = QVBoxLayout()
        self.reptabbox.addWidget(self.repcanvas)
        self.reptabbox.addWidget(self.repfig_nframe)
        self.reptabbox.addWidget(self.repfig_controls)
        self.reptab.setLayout(self.reptabbox)

        self.qefig = Figure()
        self.qefig.patch.set_facecolor('white')
        self.qecanvas = FigureCanvas(self.qefig)
        self.qeaxes = self.qefig.add_subplot(111)
        self.qeaxes.axhline(color='k')
        self.qeaxes.set_xlabel(r'$|Q| (\mathrm{\AA}^{-1})$')
        self.qeaxes.set_ylabel('Energy Transfer (meV)')
        self.qefig_controls = NavigationToolbar(self.qecanvas, self)
        self.qetabbox = QVBoxLayout()
        self.qetabbox.addWidget(self.qecanvas)
        self.qetabbox.addWidget(self.qefig_controls)
        self.qetab = QWidget(self.tabs)
        self.qetab.setLayout(self.qetabbox)

        self.scrtab = QWidget(self.tabs)
        self.scredt = QTextEdit()
        self.scrcls = QPushButton("Clear")
        self.scrcls.clicked.connect(lambda: self.scredt.clear())
        self.scrbox = QVBoxLayout()
        self.scrbox.addWidget(self.scredt)
        self.scrbox.addWidget(self.scrcls)
        self.scrtab.setLayout(self.scrbox)
        self.scrtab.hide()

        self.tabs.addTab(self.restab, 'Resolution')
        self.tabs.addTab(self.flxtab, 'Flux-Ei')
        self.tabs.addTab(self.frqtab, 'Flux-Freq')
        self.tabs.addTab(self.reptab, 'Time-Distance')
        self.tdtabID = 3
        self.tabs.setTabEnabled(self.tdtabID, False)
        self.tabs.addTab(self.qetab, 'Q-E')
        self.qetabID = 4
        self.tabs.setTabEnabled(self.qetabID, False)
        self.scrtabID = 5
        self.rightPanel.addWidget(self.tabs)

        self.menuLoad = QMenu('Load')
        self.loadAct = QAction('Load YAML', self.menuLoad)
        self.loadAct.triggered.connect(self.loadYaml)
        self.menuLoad.addAction(self.loadAct)
        self.menuOptions = QMenu('Options')
        self.instSciAct = QAction('Instrument Scientist Mode', self.menuOptions, checkable=True)
        self.instSciAct.triggered.connect(self.instSciCB)
        self.menuOptions.addAction(self.instSciAct)
        self.eiPlots = QAction('Press Enter in Ei box updates plots', self.menuOptions, checkable=True)
        self.menuOptions.addAction(self.eiPlots)
        self.overwriteload = QAction('Always overwrite instruments in memory', self.menuOptions, checkable=True)
        self.menuOptions.addAction(self.overwriteload)
        self.menuBar().addMenu(self.menuLoad)
        self.menuBar().addMenu(self.menuOptions)

        self.leftPanelWidget = QWidget()
        self.leftPanelWidget.setLayout(self.leftPanel)
        self.leftPanelWidget.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred))
        self.fullWindow.addWidget(self.leftPanelWidget, 0, 0)
        self.fullWindow.addLayout(self.rightPanel, 0, 1)
        self.helpbtn = QPushButton("?", self)
        self.helpbtn.setMaximumWidth(30)
        self.helpbtn.clicked.connect(self.onHelp)
        self.fullWindow.addWidget(self.helpbtn, 1, 0, 1, -1)

        self.mainWidget = QWidget()
        self.mainWidget.setLayout(self.fullWindow)
        self.setCentralWidget(self.mainWidget)
        self.setWindowTitle('PyChopGUI')
        self.show()
예제 #26
0
    def __init__(self, parent: ReactionList):
        QWidget.__init__(self)

        self.parent = parent
        self.reaction = None
        self.is_valid = True
        self.changed = False
        self.setAcceptDrops(False)

        layout = QVBoxLayout()
        l = QHBoxLayout()
        self.delete_button = QPushButton("Delete reaction")
        self.delete_button.setIcon(QIcon.fromTheme("edit-delete"))
        policy = QSizePolicy()
        policy.ShrinkFlag = True
        self.delete_button.setSizePolicy(policy)
        l.addWidget(self.delete_button)
        layout.addItem(l)

        l = QHBoxLayout()
        label = QLabel("Id:")
        self.id = QLineEdit()
        l.addWidget(label)
        l.addWidget(self.id)
        layout.addItem(l)

        l = QHBoxLayout()
        label = QLabel("Name:")
        self.name = QLineEdit()
        l.addWidget(label)
        l.addWidget(self.name)
        layout.addItem(l)

        l = QHBoxLayout()
        label = QLabel("Equation:")
        self.equation = QLineEdit()
        l.addWidget(label)
        l.addWidget(self.equation)
        layout.addItem(l)

        l = QHBoxLayout()
        label = QLabel("Rate min:")
        self.lower_bound = QLineEdit()
        l.addWidget(label)
        l.addWidget(self.lower_bound)
        layout.addItem(l)

        l = QHBoxLayout()
        label = QLabel("Rate max:")
        self.upper_bound = QLineEdit()
        l.addWidget(label)
        l.addWidget(self.upper_bound)
        layout.addItem(l)

        l = QHBoxLayout()
        label = QLabel("Coefficient in obj. function:")
        self.coefficent = QLineEdit()
        l.addWidget(label)
        l.addWidget(self.coefficent)
        layout.addItem(l)

        l = QHBoxLayout()
        label = QLabel("Gene reaction rule:")
        self.gene_reaction_rule = QLineEdit()
        l.addWidget(label)
        l.addWidget(self.gene_reaction_rule)
        layout.addItem(l)

        l = QVBoxLayout()
        label = QLabel("Annotations:")
        l.addWidget(label)
        l2 = QHBoxLayout()
        self.annotation = QTableWidget(0, 2)
        self.annotation.setHorizontalHeaderLabels(
            ["key", "value"])
        self.annotation.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        l2.addWidget(self.annotation)

        self.add_anno = QPushButton("+")
        self.add_anno.clicked.connect(self.add_anno_row)
        l2.addWidget(self.add_anno)
        l.addItem(l2)
        layout.addItem(l)

        l = QVBoxLayout()
        label = QLabel("Metabolites involved in this reaction:")
        l.addWidget(label)
        l2 = QHBoxLayout()
        self.metabolites = QTreeWidget()
        self.metabolites.setHeaderLabels(["Id"])
        self.metabolites.setSortingEnabled(True)
        l2.addWidget(self.metabolites)
        l.addItem(l2)
        self.metabolites.itemDoubleClicked.connect(
            self.emit_jump_to_metabolite)
        layout.addItem(l)

        self.jump_list = JumpList(self)
        layout.addWidget(self.jump_list)

        self.setLayout(layout)

        self.delete_button.clicked.connect(self.delete_reaction)

        self.throttler = SignalThrottler(500)
        self.throttler.triggered.connect(self.reaction_data_changed)

        self.id.textEdited.connect(self.throttler.throttle)
        self.name.textEdited.connect(self.throttler.throttle)
        self.equation.textEdited.connect(self.throttler.throttle)
        self.lower_bound.textEdited.connect(self.throttler.throttle)
        self.upper_bound.textEdited.connect(self.throttler.throttle)
        self.coefficent.textEdited.connect(self.throttler.throttle)
        self.gene_reaction_rule.textEdited.connect(self.throttler.throttle)
        self.annotation.itemChanged.connect(self.throttler.throttle)

        self.validate_mask()
예제 #27
0
class ReactionList(QWidget):
    """A list of reaction"""

    def __init__(self, appdata: CnaData):
        QWidget.__init__(self)
        self.appdata = appdata
        self.last_selected = None
        self.reaction_counter = 1

        self.add_button = QPushButton("Add new reaction")
        self.add_button.setIcon(QIcon.fromTheme("list-add"))
        policy = QSizePolicy()
        policy.ShrinkFlag = True
        self.add_button.setSizePolicy(policy)

        self.reaction_list = DragableTreeWidget()
        self.reaction_list.setDragEnabled(True)
        self.reaction_list.setHeaderLabels(["Id", "Name", "Flux"])
        self.reaction_list.setSortingEnabled(True)

        for r in self.appdata.project.cobra_py_model.reactions:
            self.add_reaction(r)

        self.reaction_mask = ReactionMask(self)
        self.reaction_mask.hide()

        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        l = QHBoxLayout()
        l.setAlignment(Qt.AlignRight)
        l.addWidget(self.add_button)
        self.splitter = QSplitter()
        self.splitter.setOrientation(Qt.Vertical)
        self.splitter.addWidget(self.reaction_list)
        self.splitter.addWidget(self.reaction_mask)
        self.layout.addItem(l)
        self.layout.addWidget(self.splitter)
        self.setLayout(self.layout)

        self.reaction_list.currentItemChanged.connect(self.reaction_selected)
        self.reaction_mask.reactionChanged.connect(
            self.handle_changed_reaction)
        self.reaction_mask.reactionDeleted.connect(
            self.handle_deleted_reaction)
        self.reaction_mask.jumpToMap.connect(self.emit_jump_to_map)
        self.reaction_mask.jumpToMetabolite.connect(
            self.emit_jump_to_metabolite)

        self.add_button.clicked.connect(self.add_new_reaction)

    def clear(self):
        self.reaction_list.clear()
        self.reaction_mask.hide()

    def add_reaction(self, reaction: cobra.Reaction) -> QTreeWidgetItem:
        ''' create a new item in the reaction list'''
        self.reaction_list.clearSelection()
        item = QTreeWidgetItem(self.reaction_list)
        item.setText(0, reaction.id)
        item.setText(1, reaction.name)
        self.set_flux_value(item, reaction.id)

        text = "Id: " + reaction.id + "\nName: " + reaction.name \
            + "\nEquation: " + reaction.build_reaction_string()\
            + "\nLowerbound: " + str(reaction.lower_bound) \
            + "\nUpper bound: " + str(reaction.upper_bound) \
            + "\nObjective coefficient: " + str(reaction.objective_coefficient)

        item.setToolTip(1, text)
        item.setData(3, 0, reaction)

        return item

    def set_flux_value(self, item, key):
        if key in self.appdata.project.scen_values.keys():
            (vl, vu) = self.appdata.project.scen_values[key]
            if isclose(vl, vu, abs_tol=self.appdata.abs_tol):
                item.setData(2, 0, round(vl, self.appdata.rounding))
            else:
                item.setData(
                    2, 0, str((round(vl, self.appdata.rounding), round(vu, self.appdata.rounding))))
            item.setBackground(2, self.appdata.scen_color)
            item.setForeground(2, Qt.black)
        elif key in self.appdata.project.comp_values.keys():
            (vl, vu) = self.appdata.project.comp_values[key]

            # We differentiate special cases like (vl==vu)
            if isclose(vl, vu, abs_tol=self.appdata.abs_tol):
                if self.appdata.modes_coloring:
                    if vl == 0:
                        item.setBackground(2, Qt.red)
                    else:
                        item.setBackground(2, Qt.green)
                else:
                    item.setBackground(2, self.appdata.comp_color)

                item.setData(2, 0, round(vl, self.appdata.rounding))
            else:
                if isclose(vl, 0.0, abs_tol=self.appdata.abs_tol):
                    item.setBackground(2, self.appdata.special_color_1)
                elif isclose(vu, 0.0, abs_tol=self.appdata.abs_tol):
                    item.setBackground(2, self.appdata.special_color_1)
                elif vl <= 0 and vu >= 0:
                    item.setBackground(2, self.appdata.special_color_1)
                else:
                    item.setBackground(2, self.appdata.special_color_2)
                item.setData(
                    2, 0, str((round(vl, self.appdata.rounding), round(vu, self.appdata.rounding))))

            item.setForeground(2, Qt.black)

    def add_new_reaction(self):
        self.reaction_mask.show()
        while True:
            name = "rxn_"+str(self.reaction_counter)
            self.reaction_counter += 1
            if name not in self.appdata.project.cobra_py_model.reactions:
                break
        reaction = cobra.Reaction(name)
        self.appdata.project.cobra_py_model.add_reactions([reaction])
        item = self.add_reaction(reaction)
        self.reaction_selected(item, 1)
        self.appdata.window.unsaved_changes()

    def update_annotations(self, annotation):

        self.reaction_mask.annotation.itemChanged.disconnect(
            self.reaction_mask.throttler.throttle)
        c = self.reaction_mask.annotation.rowCount()
        for i in range(0, c):
            self.reaction_mask.annotation.removeRow(0)
        i = 0
        for key in annotation:
            self.reaction_mask.annotation.insertRow(i)
            keyl = QTableWidgetItem(key)
            iteml = QTableWidgetItem(str(annotation[key]))
            self.reaction_mask.annotation.setItem(i, 0, keyl)
            self.reaction_mask.annotation.setItem(i, 1, iteml)
            i += 1

        self.reaction_mask.annotation.itemChanged.connect(
            self.reaction_mask.throttler.throttle)

    def reaction_selected(self, item: QTreeWidgetItem, _column):
        if item is None:
            self.reaction_mask.hide()
        else:
            item.setSelected(True)
            self.reaction_mask.show()
            reaction: cobra.Reaction = item.data(3, 0)

            self.last_selected = reaction.id
            self.reaction_mask.reaction = reaction

            self.reaction_mask.id.setText(reaction.id)
            self.reaction_mask.name.setText(reaction.name)
            self.reaction_mask.equation.setText(
                reaction.build_reaction_string())
            self.reaction_mask.lower_bound.setText(str(reaction.lower_bound))
            self.reaction_mask.upper_bound.setText(str(reaction.upper_bound))
            self.reaction_mask.coefficent.setText(
                str(reaction.objective_coefficient))
            self.reaction_mask.gene_reaction_rule.setText(
                str(reaction.gene_reaction_rule))
            self.update_annotations(reaction.annotation)

            self.reaction_mask.changed = False

            turn_white(self.reaction_mask.id)
            turn_white(self.reaction_mask.name)
            turn_white(self.reaction_mask.name)
            turn_white(self.reaction_mask.equation)
            turn_white(self.reaction_mask.lower_bound)
            turn_white(self.reaction_mask.upper_bound)
            turn_white(self.reaction_mask.coefficent)
            turn_white(self.reaction_mask.gene_reaction_rule)
            self.reaction_mask.is_valid = True
        (_, r) = self.splitter.getRange(1)
        self.splitter.moveSplitter(r/2, 1)
        self.reaction_mask.update_state()

    def handle_changed_reaction(self, reaction: cobra.Reaction):
        # Update reaction item in list
        root = self.reaction_list.invisibleRootItem()
        child_count = root.childCount()
        for i in range(child_count):
            item = root.child(i)
            if item.data(3, 0) == reaction:
                old_id = item.text(0)
                item.setText(0, reaction.id)
                item.setText(1, reaction.name)
                break

        self.last_selected = self.reaction_mask.id.text()
        self.reactionChanged.emit(old_id, reaction)

    def handle_deleted_reaction(self, reaction: cobra.Reaction):
        '''Remove reaction item from reaction list'''
        root = self.reaction_list.invisibleRootItem()
        child_count = root.childCount()
        for i in range(child_count):
            item = root.child(i)
            if item.data(3, 0) == reaction:
                # remove item
                self.reaction_list.takeTopLevelItem(
                    self.reaction_list.indexOfTopLevelItem(item))
                break

        self.last_selected = self.reaction_mask.id.text()
        self.reactionDeleted.emit(reaction)

    def update_selected(self, string):
        root = self.reaction_list.invisibleRootItem()
        child_count = root.childCount()
        for i in range(child_count):
            item = root.child(i)
            item.setHidden(True)

        for item in self.reaction_list.findItems(string, Qt.MatchContains, 0):
            item.setHidden(False)
        for item in self.reaction_list.findItems(string, Qt.MatchContains, 1):
            item.setHidden(False)

    def update(self):
        self.reaction_list.clear()
        for r in self.appdata.project.cobra_py_model.reactions:
            self.add_reaction(r)

        if self.last_selected is None:
            pass
        else:
            items = self.reaction_list.findItems(
                self.last_selected, Qt.MatchExactly)

            for i in items:
                self.reaction_list.setCurrentItem(i)
                break

        self.reaction_mask.update_state()

    def set_current_item(self, key):
        self.last_selected = key
        self.update()

    def emit_jump_to_map(self, idx: str, reaction: str):
        self.jumpToMap.emit(idx, reaction)

    def emit_jump_to_metabolite(self, metabolite):
        self.jumpToMetabolite.emit(metabolite)

    itemActivated = Signal(str)
    reactionChanged = Signal(str, cobra.Reaction)
    reactionDeleted = Signal(cobra.Reaction)
    jumpToMap = Signal(str, str)
    jumpToMetabolite = Signal(str)
예제 #28
0
    def _setupChromSettings(self):
        l_chromconfig = QLabel('<h3>Chromaticity Variation Config</h3>', self)
        l_chromconfig.setAlignment(Qt.AlignCenter)
        self.le_chromconfig = _ConfigLineEdit(
            parent=self, config_type='bo_chromcorr_params')
        self.le_chromconfig.textChanged.connect(self._showChromConfigData)

        l_chrommat = QLabel('<h4>Matrix</h4>', self)
        l_chrommat.setAlignment(Qt.AlignCenter)
        self.table_chrommat = QTableWidget(self)
        self.table_chrommat.setObjectName('chrommat')
        self.table_chrommat.setStyleSheet("""
            #chrommat{
                background-color: #efebe7;
                min-width: 22.14em;
                min-height: 6em; max-height: 6em;}""")
        self.table_chrommat.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.table_chrommat.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.table_chrommat.setRowCount(2)
        self.table_chrommat.setColumnCount(2)
        self.table_chrommat.setVerticalHeaderLabels(['  X', '  Y'])
        self.table_chrommat.setHorizontalHeaderLabels(['SF', 'SD'])
        self.table_chrommat.horizontalHeader().setStyleSheet("""
            min-height:1.55em; max-height:1.55em;""")
        self.table_chrommat.verticalHeader().setStyleSheet("""
            min-width:1.55em; max-width:1.55em;""")
        self.table_chrommat.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.table_chrommat.verticalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.table_chrommat.setSizePolicy(QSzPlcy.MinimumExpanding,
                                          QSzPlcy.Preferred)

        l_nomSL = QLabel('<h4>Nominal SL</h4>')
        l_nomSL.setAlignment(Qt.AlignCenter)
        self.table_nomSL = QTableWidget(self)
        self.table_nomSL.setObjectName('nomSL')
        self.table_nomSL.setStyleSheet("""
            #nomSL{
                background-color: #efebe7;
                min-width: 22.14em;
                min-height: 4em; max-height: 4em;}""")
        self.table_nomSL.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.table_nomSL.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.table_nomSL.setRowCount(1)
        self.table_nomSL.setColumnCount(2)
        self.table_nomSL.setVerticalHeaderLabels(['SL'])
        self.table_nomSL.setHorizontalHeaderLabels(['SF', 'SD'])
        self.table_nomSL.horizontalHeader().setStyleSheet("""
            min-height:1.55em; max-height:1.55em;""")
        self.table_nomSL.verticalHeader().setStyleSheet("""
            min-width:1.55em; max-width:1.55em;""")
        self.table_nomSL.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.table_nomSL.verticalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.table_nomSL.setSizePolicy(QSzPlcy.MinimumExpanding,
                                       QSzPlcy.Preferred)

        l_nomchrom = QLabel('<h4>Nominal Chrom</h4>')
        l_nomchrom.setAlignment(Qt.AlignCenter)
        self.label_nomchrom = QLabel()
        self.label_nomchrom.setAlignment(Qt.AlignCenter)

        lay = QVBoxLayout()
        lay.addWidget(l_chromconfig)
        lay.addWidget(self.le_chromconfig)
        lay.addItem(QSpacerItem(20, 10, QSzPlcy.Expanding, QSzPlcy.Expanding))
        lay.addWidget(l_chrommat)
        lay.addWidget(self.table_chrommat)
        lay.addItem(QSpacerItem(20, 10, QSzPlcy.Expanding, QSzPlcy.Expanding))
        lay.addWidget(l_nomSL)
        lay.addWidget(self.table_nomSL)
        lay.addItem(QSpacerItem(20, 10, QSzPlcy.Expanding, QSzPlcy.Expanding))
        lay.addWidget(l_nomchrom)
        lay.addWidget(self.label_nomchrom)

        return lay
예제 #29
0
class C2ConfigFrame(ConfigBaseFrame):
    def __init__(self, parent):
        super(C2ConfigFrame, self).__init__(parent)

        self.widget_layout = QVBoxLayout(self)
        self.setLayout(self.widget_layout)

        self.c2_conf_label = QLabel(self)
        self.c2_conf_label.setText("C2 server configuration")
        self.widget_layout.addWidget(self.c2_conf_label)

        self.desc_label = QLabel(self)
        self.desc_label.setWordWrap(True)
        self.desc_label.setText(
            "Make sure that port you use is opened and IP address is "
            "valid. To balance server load, select amount of threads to use. Set 0 to use all "
            "available system cores.")
        self.widget_layout.addWidget(self.desc_label)

        self.ip_config = QWidget(self)
        self.ip_config_layout = QFormLayout(self.ip_config)
        self.widget_layout.addWidget(self.ip_config)

        self.c2_ip_label = QLabel(self.ip_config)
        self.c2_ip_label.setText("C2 IP Address")

        self.c2_ip_combo = QComboBox(self.ip_config)
        self.ip_config_layout.addRow(self.c2_ip_label, self.c2_ip_combo)

        self.port_label = QLabel(self.ip_config)
        self.port_label.setText("C2 Port")

        self.port_edit = QSpinBox(self.ip_config)
        self.port_edit.setRange(1024, 65535)
        self.port_edit.setValue(8192)
        self.ip_config_layout.addRow(self.port_label, self.port_edit)

        self.threads_label = QLabel(self.ip_config)
        self.threads_label.setText("C2 handler threads")

        self.threads_edit = QSpinBox(self.ip_config)
        self.threads_edit.setRange(0, 256)
        self.ip_config_layout.addRow(self.threads_label, self.threads_edit)

        self.c2_key_label = QLabel(self.ip_config)
        self.c2_key_label.setText("C2 encryption key")

        self.c2_key_edit = QLineEdit(self.ip_config)
        self.c2_key_edit.setReadOnly(True)
        self.c2_key_edit.setText(generate_key().decode())
        self.ip_config_layout.addRow(self.c2_key_label, self.c2_key_edit)

        spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum,
                                 QSizePolicy.Expanding)
        self.widget_layout.addItem(spacerItem)

    @Slot(dict)
    def set_options(self, options):
        self.c2_ip_combo.addItems(
            [iface.get("ip") for iface in options.get("interfaces")])
        self.c2_key_edit.setText(options.get("c2_key"))

    @Slot()
    def collect_info(self):
        return {
            "c2_ip": self.c2_ip_combo.currentText(),
            "c2_port": int(self.port_edit.text()),
            "c2_threads": int(self.threads_edit.value()),
            "c2_key": str(self.c2_key_edit.text())
        }
예제 #30
0
    def __init__(self, appdata):
        QWidget.__init__(self)
        self.appdata = appdata
        self.metabolite = None
        self.is_valid = True
        self.changed = False
        self.setAcceptDrops(False)

        layout = QVBoxLayout()
        l = QHBoxLayout()
        label = QLabel("Id:")
        self.id = QLineEdit()
        l.addWidget(label)
        l.addWidget(self.id)
        layout.addItem(l)

        l = QHBoxLayout()
        label = QLabel("Name:")
        self.name = QLineEdit()
        l.addWidget(label)
        l.addWidget(self.name)
        layout.addItem(l)

        l = QHBoxLayout()
        label = QLabel("Formula:")
        self.formula = QLineEdit()
        l.addWidget(label)
        l.addWidget(self.formula)
        layout.addItem(l)

        l = QHBoxLayout()
        label = QLabel("Charge:")
        self.charge = QLineEdit()
        l.addWidget(label)
        l.addWidget(self.charge)
        layout.addItem(l)

        l = QHBoxLayout()
        label = QLabel("Compartment:")
        self.compartment = QLineEdit()
        l.addWidget(label)
        l.addWidget(self.compartment)
        layout.addItem(l)

        l = QVBoxLayout()
        label = QLabel("Annotations:")
        l.addWidget(label)
        l2 = QHBoxLayout()
        self.annotation = QTableWidget(0, 2)
        self.annotation.setHorizontalHeaderLabels(
            ["key", "value"])
        self.annotation.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        l2.addWidget(self.annotation)

        self.add_anno = QPushButton("+")
        self.add_anno.clicked.connect(self.add_anno_row)
        l2.addWidget(self.add_anno)
        l.addItem(l2)
        layout.addItem(l)

        l = QVBoxLayout()
        label = QLabel("Reactions using this metabolite:")
        l.addWidget(label)
        l2 = QHBoxLayout()
        self.reactions = QTreeWidget()
        self.reactions.setHeaderLabels(["Id"])
        self.reactions.setSortingEnabled(True)
        l2.addWidget(self.reactions)
        l.addItem(l2)
        self.reactions.itemDoubleClicked.connect(self.emit_jump_to_reaction)
        layout.addItem(l)

        self.setLayout(layout)

        self.throttler = SignalThrottler(500)
        self.throttler.triggered.connect(self.metabolites_data_changed)

        self.id.textEdited.connect(self.throttler.throttle)
        self.name.textEdited.connect(self.throttler.throttle)
        self.formula.textEdited.connect(self.throttler.throttle)
        self.charge.textEdited.connect(self.throttler.throttle)
        self.compartment.textEdited.connect(self.throttler.throttle)
        self.annotation.itemChanged.connect(self.throttler.throttle)
        self.validate_mask()
예제 #31
0
class ConfigCobrapyDialog(QDialog):
    """A dialog to set values in cobrapy-config.txt"""

    def __init__(self, appdata: CnaData):
        QDialog.__init__(self)
        self.setWindowTitle("Configure COBRApy")

        self.appdata = appdata
        self.layout = QVBoxLayout()

        # allow MILP solvers only?
        avail_solvers = list(set(solvers.keys()) - {'scipy'}) # SCIPY currently not even usable for FBA

        h2 = QHBoxLayout()
        label = QLabel("Default solver:\n(set when loading a model)")
        h2.addWidget(label)
        self.default_solver = QComboBox()
        self.default_solver.addItems(avail_solvers)
        self.default_solver.setCurrentIndex(avail_solvers.index(interface_to_str(cobra.Configuration().solver)))
        h2.addWidget(self.default_solver)
        self.layout.addItem(h2)

        h9 = QHBoxLayout()
        label = QLabel("Solver for current model:")
        h9.addWidget(label)
        self.current_solver = QComboBox()
        self.current_solver.addItems(avail_solvers)
        self.current_solver.setCurrentIndex(avail_solvers.index(interface_to_str(appdata.project.cobra_py_model.problem)))
        h9.addWidget(self.current_solver)
        self.layout.addItem(h9)

        h7 = QHBoxLayout()
        label = QLabel(
            "Number of processes for multiprocessing (e.g. FVA):")
        h7.addWidget(label)
        self.num_processes = QLineEdit()
        self.num_processes.setFixedWidth(100)
        self.num_processes.setText(str(cobra.Configuration().processes))
        validator = QIntValidator(1, cpu_count(), self)
        self.num_processes.setValidator(validator)
        h7.addWidget(self.num_processes)
        self.layout.addItem(h7)

        h8 = QHBoxLayout()
        label = QLabel(
            "Default tolerance:\n(set when loading a model)")
        h8.addWidget(label)
        self.default_tolerance = QLineEdit()
        self.default_tolerance.setFixedWidth(100)
        self.default_tolerance.setText(str(cobra.Configuration().tolerance))
        validator = QDoubleValidator(self)
        validator.setBottom(1e-9) # probably a reasonable consensus value
        self.default_tolerance.setValidator(validator)
        h8.addWidget(self.default_tolerance)
        self.layout.addItem(h8)

        h10 = QHBoxLayout()
        label = QLabel(
            "Tolerance for current model:")
        h10.addWidget(label)
        self.current_tolerance = QLineEdit()
        self.current_tolerance.setFixedWidth(100)
        self.current_tolerance.setText(str(self.appdata.project.cobra_py_model.tolerance))
        validator = QDoubleValidator(self)
        validator.setBottom(0)
        self.current_tolerance.setValidator(validator)
        h10.addWidget(self.current_tolerance)
        self.layout.addItem(h10)

        l2 = QHBoxLayout()
        self.button = QPushButton("Apply Changes")
        self.cancel = QPushButton("Close")
        l2.addWidget(self.button)
        l2.addWidget(self.cancel)
        self.layout.addItem(l2)
        self.setLayout(self.layout)

        self.cancel.clicked.connect(self.reject)
        self.button.clicked.connect(self.apply)


    def apply(self):
        cobra.Configuration().solver = self.default_solver.currentText()
        cobra.Configuration().processes = int(self.num_processes.text())
        try:
            val = float(self.default_tolerance.text())
            if 1e-9 <= val <= 0.1:
                cobra.Configuration().tolerance = val
            else:
                raise ValueError
        except:
            QMessageBox.critical(self, "Cannot set default tolerance", "Choose a value between 0.1 and 1e-9 as default tolerance.")
            return
        try:
            self.appdata.project.cobra_py_model.solver = self.current_solver.currentText()
            self.appdata.project.cobra_py_model.tolerance = float(self.current_tolerance.text())
        except Exception as e:
            QMessageBox.critical(self, "Cannot set current solver/tolerance", str(e))
            return

        parser = configparser.ConfigParser()
        parser.add_section('cobrapy-config')
        parser.set('cobrapy-config', 'solver', interface_to_str(cobra.Configuration().solver))
        parser.set('cobrapy-config', 'processes', str(cobra.Configuration().processes))
        parser.set('cobrapy-config', 'tolerance', str(cobra.Configuration().tolerance))
        
        try:
            fp = open(self.appdata.cobrapy_conf_path, "w")
        except FileNotFoundError:
            os.makedirs(appdirs.user_config_dir(
                "cnapy", roaming=True, appauthor=False))
            fp = open(self.appdata.cobrapy_conf_path, "w")

        parser.write(fp)
        fp.close()

        self.accept()
예제 #32
0
class EFMDialog(QDialog):
    """A dialog to set up EFM calculation"""
    def __init__(self, appdata: CnaData, centralwidget):
        QDialog.__init__(self)
        self.setWindowTitle("Elementary Flux Mode Computation")

        self.appdata = appdata
        self.centralwidget = centralwidget
        self.eng = appdata.engine
        self.out = io.StringIO()
        self.err = io.StringIO()

        self.layout = QVBoxLayout()

        l1 = QHBoxLayout()
        self.constraints = QCheckBox("consider 0 in current scenario as off")
        self.constraints.setCheckState(Qt.Checked)
        l1.addWidget(self.constraints)
        self.layout.addItem(l1)

        l2 = QHBoxLayout()
        self.flux_bounds = QGroupBox(
            "use flux bounds to calculate elementary flux vectors")
        self.flux_bounds.setCheckable(True)
        self.flux_bounds.setChecked(False)

        vbox = QVBoxLayout()
        label = QLabel("Threshold for bounds to be unconstrained")
        vbox.addWidget(label)
        self.threshold = QLineEdit("100")
        validator = QIntValidator()
        validator.setBottom(0)
        self.threshold.setValidator(validator)
        vbox.addWidget(self.threshold)
        self.flux_bounds.setLayout(vbox)
        l2.addWidget(self.flux_bounds)
        self.layout.addItem(l2)

        l3 = QHBoxLayout()
        self.check_reversibility = QCheckBox("check reversibility")
        self.check_reversibility.setCheckState(Qt.Checked)
        l3.addWidget(self.check_reversibility)
        self.layout.addItem(l3)

        l4 = QHBoxLayout()
        self.convex_basis = QCheckBox("only convex basis")
        l4.addWidget(self.convex_basis)
        self.layout.addItem(l4)

        l5 = QHBoxLayout()
        self.isozymes = QCheckBox("consider isozymes only once")
        l5.addWidget(self.isozymes)
        self.layout.addItem(l5)

        # TODO: choose solver

        l7 = QHBoxLayout()
        self.rational_numbers = QCheckBox("use rational numbers")
        l7.addWidget(self.rational_numbers)
        self.layout.addItem(l7)

        lx = QHBoxLayout()
        self.button = QPushButton("Compute")
        self.cancel = QPushButton("Close")
        lx.addWidget(self.button)
        lx.addWidget(self.cancel)
        self.layout.addItem(lx)

        self.setLayout(self.layout)

        # Connecting the signal
        self.cancel.clicked.connect(self.reject)
        self.button.clicked.connect(self.compute)

    def compute(self):

        # create CobraModel for matlab
        self.appdata.create_cobra_model()
        legacy.read_cnapy_model(self.eng)

        # get some data
        reac_id = self.eng.get_reacID()

        # setting parameters
        a = self.eng.eval("constraints = {};",
                          nargout=0,
                          stdout=self.out,
                          stderr=self.err)
        scenario = {}
        if self.constraints.checkState(
        ) == Qt.Checked or self.flux_bounds.isChecked():
            onoff_str = ""
            for r in reac_id:
                if r in self.appdata.project.scen_values.keys():
                    (vl, vu) = self.appdata.project.scen_values[r]
                    if vl == vu:
                        if vl > 0:
                            onoff_str = onoff_str + " NaN"  # efmtool does not support 1
                        elif vl == 0:
                            scenario[r] = (0, 0)
                            onoff_str = onoff_str + " 0"
                        else:
                            onoff_str = onoff_str + " NaN"
                            print("WARN: negative value in scenario")
                    else:
                        onoff_str = onoff_str + " NaN"
                        print("WARN: not fixed value in scenario")
                else:
                    onoff_str = onoff_str + " NaN"

            onoff_str = "reaconoff = [" + onoff_str + "];"
            a = self.eng.eval(onoff_str,
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

            a = self.eng.eval("constraints.reaconoff = reaconoff;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

        if self.flux_bounds.isChecked():
            threshold = float(self.threshold.text())
            lb_str = ""
            ub_str = ""
            for r in reac_id:
                c_reaction = self.appdata.project.cobra_py_model.reactions.get_by_id(
                    r)
                if r in self.appdata.project.scen_values:
                    (vl, vu) = self.appdata.project.scen_values[r]
                else:
                    vl = c_reaction.lower_bound
                    vu = c_reaction.upper_bound
                if vl <= -threshold:
                    vl = "NaN"
                if vu >= threshold:
                    vu = "NaN"
                if vl == 0 and vu == 0:  # already in reaconoff, can be skipped here
                    vl = "NaN"
                    vu = "NaN"
                lb_str = lb_str + " " + str(vl)
                ub_str = ub_str + " " + str(vu)

            lb_str = "lb = [" + lb_str + "];"
            a = self.eng.eval(lb_str,
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)
            a = self.eng.eval("constraints.lb = lb;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

            ub_str = "ub = [" + ub_str + "];"
            a = self.eng.eval(ub_str,
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)
            a = self.eng.eval("constraints.ub = ub;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

        # TODO set solver 4 = EFMTool 3 = MetaTool, 1 = cna Mex file, 0 = cna functions
        a = self.eng.eval("solver = 4;",
                          nargout=0,
                          stdout=self.out,
                          stderr=self.err)

        if self.check_reversibility.checkState() == Qt.Checked:
            a = self.eng.eval("irrev_flag = 1;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)
        else:
            a = self.eng.eval("irrev_flag = 0;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

        # convex basis computation is only possible with METATOOL solver=3
        if self.convex_basis.checkState() == Qt.Checked:
            a = self.eng.eval("conv_basis_flag = 1; solver = 3;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)
        else:
            a = self.eng.eval("conv_basis_flag = 0;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

        if self.isozymes.checkState() == Qt.Checked:
            a = self.eng.eval("iso_flag = 1;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)
        else:
            a = self.eng.eval("iso_flag = 0;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

        # default we have no macromolecules and display is et to ALL
        a = self.eng.eval("c_macro=[]; display= 'ALL';",
                          nargout=0,
                          stdout=self.out,
                          stderr=self.err)

        if self.rational_numbers.checkState() == Qt.Checked:
            a = self.eng.eval(
                "efmtool_options = {'arithmetic', 'fractional'};",
                nargout=0,
                stdout=self.out,
                stderr=self.err)
        else:
            a = self.eng.eval("efmtool_options = {};",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

        if self.appdata.is_matlab_set():
            try:
                a = self.eng.eval(
                    "[ems, irrev_ems, ems_idx, ray] = CNAcomputeEFM(cnap, constraints,solver,irrev_flag,conv_basis_flag,iso_flag,c_macro,display,efmtool_options);",
                    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:
                ems = self.eng.workspace['ems']
                idx = self.eng.workspace['ems_idx']
                irreversible = numpy.squeeze(self.eng.workspace['irrev_ems'])
                unbounded = numpy.squeeze(self.eng.workspace['ray'])
                ems = numpy.array(ems)
                self.result2ui(ems, idx, reac_id, irreversible, unbounded,
                               scenario)

                self.accept()
        elif self.appdata.is_octave_ready():
            a = self.eng.eval(
                "[ems, irrev_ems, ems_idx, ray] = CNAcomputeEFM(cnap, constraints,solver,irrev_flag,conv_basis_flag,iso_flag,c_macro,display,efmtool_options);",
                nargout=0)

            ems = self.eng.pull('ems')
            idx = self.eng.pull('ems_idx')
            irreversible = numpy.squeeze(self.eng.pull('irrev_ems'))
            unbounded = numpy.squeeze(self.eng.pull('ray'))

            self.result2ui(ems, idx, reac_id, irreversible, unbounded,
                           scenario)

    def result2ui(self, ems, idx, reac_id, irreversible, unbounded, scenario):
        if len(ems) == 0:
            QMessageBox.information(
                self, 'No modes',
                'Modes have not been calculated or do not exist.')
        else:
            self.appdata.project.modes = FluxVectorContainer(
                ems, [reac_id[int(i) - 1] for i in idx[0]], irreversible,
                unbounded)
            self.centralwidget.mode_navigator.current = 0
            self.centralwidget.mode_navigator.scenario = scenario
            self.centralwidget.mode_navigator.title.setText("Mode Navigation")
            self.centralwidget.update_mode()