Пример #1
0
class ChoiceElement(MeasElement):
    """Base for elements that allow an arbitrary choice."""

    CACHE_KEY = ''
    SORT_KEY = lambda self, x: None  # keep previous ordering
    VALUES = []

    def createWidget(self, parent, client):
        if self.CACHE_KEY:
            values = client.getDeviceParam(*self.CACHE_KEY.split('/'))
            try:
                values = sorted(values or [], key=self.SORT_KEY)
            except TypeError:  # unsortable?
                pass
        else:
            values = self.VALUES
        self._values = values
        self._widget = QComboBox(parent)
        self._widget.addItems(self._values)
        if self.value is not None and self.value in self._values:
            self._widget.setCurrentIndex(self._values.index(self.value))
        elif self.value is None and self._values:
            self.value = self._values[0]
        self._widget.currentIndexChanged.connect(self._updateValue)
        return self._widget

    def _updateValue(self, index):
        self.value = self._values[index]
        self.changed.emit(self.value)
Пример #2
0
 def __init__(self, container=None, box_data=None, init_state=None):
     QComboBox.__init__(self, parent=container)
     self.current = init_state
     for item in box_data:
         self.addItem('%s' % item)
     self.setCurrentIndex(self.current)
     self.currentIndexChanged[int].connect(self.index_changed)
Пример #3
0
 def setPanelToolbar(self):
     bar = QToolBar('History viewer')
     bar.addAction(self.actionNew)
     bar.addAction(self.actionEditView)
     bar.addSeparator()
     bar.addAction(self.actionSavePlot)
     bar.addAction(self.actionPrint)
     bar.addAction(self.actionSaveData)
     bar.addSeparator()
     bar.addAction(self.actionUnzoom)
     bar.addAction(self.actionLogScale)
     bar.addSeparator()
     bar.addAction(self.actionAutoScale)
     bar.addAction(self.actionScaleX)
     bar.addAction(self.actionScaleY)
     bar.addSeparator()
     bar.addAction(self.actionResetView)
     bar.addAction(self.actionDeleteView)
     bar.addSeparator()
     bar.addAction(self.actionFitPeak)
     wa = QWidgetAction(bar)
     self.fitPickCheckbox = QCheckBox(bar)
     self.fitPickCheckbox.setText('Pick')
     self.fitPickCheckbox.setChecked(True)
     self.actionPickInitial.setChecked(True)
     self.fitPickCheckbox.toggled.connect(self.actionPickInitial.setChecked)
     self.actionPickInitial.toggled.connect(self.fitPickCheckbox.setChecked)
     layout = QHBoxLayout()
     layout.setContentsMargins(10, 0, 10, 0)
     layout.addWidget(self.fitPickCheckbox)
     frame = QFrame(bar)
     frame.setLayout(layout)
     wa.setDefaultWidget(frame)
     bar.addAction(wa)
     ag = QActionGroup(bar)
     ag.addAction(self.actionFitPeakGaussian)
     ag.addAction(self.actionFitPeakLorentzian)
     ag.addAction(self.actionFitPeakPV)
     ag.addAction(self.actionFitPeakPVII)
     ag.addAction(self.actionFitTc)
     ag.addAction(self.actionFitCosine)
     ag.addAction(self.actionFitSigmoid)
     ag.addAction(self.actionFitLinear)
     ag.addAction(self.actionFitExponential)
     wa = QWidgetAction(bar)
     self.fitComboBox = QComboBox(bar)
     for a in ag.actions():
         itemtext = a.text().replace('&', '')
         self.fitComboBox.addItem(itemtext)
         self.fitfuncmap[itemtext] = a
     self.fitComboBox.currentIndexChanged.connect(
         self.on__fitComboBox_currentIndexChanged)
     wa.setDefaultWidget(self.fitComboBox)
     bar.addAction(wa)
     bar.addSeparator()
     bar.addAction(self.actionFitArby)
     self.bar = bar
     self.actionFitLinear.trigger()
     return bar
Пример #4
0
 def createWidget(self, parent, client):
     resos = client.getDeviceParam(*self.CACHE_KEY.split('/'))
     self._values = ['off'] + ['%.1f%%' % v
                               for v in (resos or [])] + ['manual']
     self._widget = QComboBox(parent)
     self._widget.addItems(self._values)
     if self.value is not None and self.value in self._values:
         self._widget.setCurrentIndex(self._values.index(self.value))
     elif self.value is None and self._values:
         self.value = self._values[0]
     self._widget.currentIndexChanged.connect(self._updateValue)
     return self._widget
Пример #5
0
    def __init__(self, parent):
        QMainWindow.__init__(self, parent)
        DlgUtils.__init__(self, 'Live data')
        self.panel = parent
        layout1 = QVBoxLayout()
        self.plot = QwtPlot(self)
        layout1.addWidget(self.plot)
        self.curve = QwtPlotCurve()
        self.curve.setRenderHint(QwtPlotCurve.RenderAntialiased)
        self.curve.attach(self.plot)
        self.marker = QwtPlotMarker()
        self.marker.attach(self.plot)
        self.markerpen = QPen(Qt.red)
        self.marker.setSymbol(
            QwtSymbol(QwtSymbol.Ellipse, QBrush(), self.markerpen, QSize(7,
                                                                         7)))
        self.zoomer = QwtPlotZoomer(self.plot.canvas())
        self.zoomer.setMousePattern(QwtPlotZoomer.MouseSelect3, Qt.NoButton)
        self.picker = QwtPlotPicker(self.plot.canvas())
        self.picker.setSelectionFlags(QwtPlotPicker.PointSelection
                                      | QwtPlotPicker.ClickSelection)
        self.picker.setMousePattern(QwtPlotPicker.MouseSelect1, Qt.MidButton)
        self.picker.selected.connect(self.pickerSelected)
        layout2 = QHBoxLayout()
        layout2.addWidget(QLabel('Scale:', self))
        self.scale = QComboBox(self)
        self.scale.addItems([
            'Single detectors, sorted by angle',
            'Scattering angle 2theta (deg)', 'Q value (A-1)'
        ])
        self.scale.currentIndexChanged[int].connect(self.scaleChanged)
        layout2.addWidget(self.scale)
        layout2.addStretch()
        self.scaleframe = QFrame(self)
        self.scaleframe.setLayout(layout2)
        self.scaleframe.setVisible(False)
        layout1.addWidget(self.scaleframe)
        mainframe = QFrame(self)
        mainframe.setLayout(layout1)
        self.setCentralWidget(mainframe)
        self.setContentsMargins(6, 6, 6, 6)
        plotfont = scaledFont(self.font(), 0.7)
        self.plot.setAxisFont(QwtPlot.xBottom, plotfont)
        self.plot.setAxisFont(QwtPlot.yLeft, plotfont)
        self.plot.setCanvasBackground(Qt.white)
        self.resize(800, 200)

        self._detinfo = None
        self._anglemap = None
        self._infowindow = None
        self._infolabel = None
        self._xs = self._ys = None
        self._type = None
Пример #6
0
 def createWidget(self, parent, client):
     if self.CACHE_KEY:
         values = client.getDeviceParam(*self.CACHE_KEY.split('/'))
         values = sorted(values or [], key=self.SORT_KEY)
     else:
         values = self.VALUES
     self._values = values
     self._widget = QComboBox(parent)
     self._widget.addItems(self._values)
     if self.value is not None and self.value in self._values:
         self._widget.setCurrentIndex(self._values.index(self.value))
     elif self.value is None and self._values:
         self.value = self._values[0]
     self._widget.currentIndexChanged.connect(self._updateValue)
     return self._widget
Пример #7
0
 def __init__(self, parent, name, selections, preselect):
     QFrame.__init__(self, parent)
     self.name = name
     self.selections = selections
     layout = QHBoxLayout()
     layout.addWidget(QLabel(name, self))
     self.combo = QComboBox(self)
     self.combo.addItems(selections)
     if preselect in selections:
         self.combo.setCurrentIndex(selections.index(preselect))
     else:
         self.combo.setCurrentIndex(0)
     layout.addWidget(self.combo)
     layout.setContentsMargins(0, 0, 0, 0)
     self.setLayout(layout)
Пример #8
0
 def __init__(self, parent=None):
     QWidget.__init__(self, parent)
     self.setLayout(QHBoxLayout())
     self.layout().setContentsMargins(0.1, 0.1, 0.1, 0.1)
     self.layout().setSpacing(1)
     self.val = QLineEdit()
     self.val.setValidator(QDoubleValidator(0, 1000000, 2))
     self.layout().addWidget(self.val)
     self.unit = QComboBox()
     self.unit.insertItems(0, self.units)
     self.layout().addWidget(self.unit)
     self.setValue(1)
     self.val.returnPressed.connect(self.on_returnPressed)
     self.val.editingFinished.connect(self.on_returnPressed)
     self.unit.currentIndexChanged.connect(self.recalcValue)
Пример #9
0
 def __init__(self, parent, values, curvalue, add_other=False):
     QComboBox.__init__(self, parent)
     self._values = sorted(values, key=str)
     self._textvals = list(map(str, self._values))
     self._add_other = add_other
     if add_other:
         self._values.append(Ellipsis)
         self._textvals.append('<other value>')
     self.addItems(self._textvals)
     if curvalue in self._values:
         self.setCurrentIndex(self._values.index(curvalue))
     elif add_other:
         self.setCurrentIndex(len(self._values) - 1)
     self.currentIndexChanged['int'].connect(
         lambda idx: self.valueModified.emit())
Пример #10
0
 def createWidget(self, parent, client):
     if self.value is None:
         self.value = 30 * 60
     self._widget = QWidget(parent)
     layout = QHBoxLayout()
     self._widget.number = QSpinBox(self._widget)
     self._widget.number.setValue(30)
     self._widget.number.setMinimum(1)
     self._widget.number.setMaximum(10000)
     self._widget.unit = QComboBox(self._widget)
     self._widget.unit.addItems(['sec', 'min', 'hr'])
     self._widget.unit.setCurrentIndex(1)
     layout.addWidget(self._widget.number)
     layout.addWidget(self._widget.unit)
     layout.setContentsMargins(0, 0, 0, 0)
     self._widget.setLayout(layout)
     self._widget.number.valueChanged.connect(self._updateValue)
     self._widget.unit.currentIndexChanged.connect(self._updateValue)
     self._widget.setMinimumWidth(120)
     if self.value is not None:
         if self.value % 3600 == 0:
             self._widget.number.setValue(self.value // 3600)
             self._widget.unit.setCurrentIndex(2)
         elif self.value % 60 == 0:
             self._widget.number.setValue(self.value // 60)
             self._widget.unit.setCurrentIndex(1)
         else:
             self._widget.number.setValue(self.value)
             self._widget.unit.setCurrentIndex(0)
     return self._widget
Пример #11
0
 def __init__(self, viewplot, curvenames, *args):
     QFileDialog.__init__(self, viewplot, *args)
     self.setOption(self.DontConfirmOverwrite, False)
     # allow adding some own widgets
     self.setOption(self.DontUseNativeDialog, True)
     self.setAcceptMode(QFileDialog.AcceptSave)
     layout = self.layout()
     layout.addWidget(QLabel('Curve:', self), 4, 0)
     self.curveCombo = QComboBox(self)
     if len(curvenames) > 1:
         self.curveCombo.addItem('all (in separate files)')
         self.curveCombo.addItem('all (in one file, multiple data columns)')
     self.curveCombo.addItems(curvenames)
     layout.addWidget(self.curveCombo, 4, 1)
     layout.addWidget(QLabel('Time format:', self), 5, 0)
     self.formatCombo = QComboBox(self)
     self.formatCombo.addItems([
         'Seconds since first datapoint', 'UNIX timestamp',
         'Text timestamp (YYYY-MM-dd.HH:MM:SS)'
     ])
     layout.addWidget(self.formatCombo, 5, 1)
Пример #12
0
 def __init__(self, parent, client, options):
     self.fileList = QListWidget()
     self.tb_fileList = QComboBox()
     self._detector_selected = options.get('default_detector', '')
     self._previews = {}
     self._previews_cache = {}
     LiveDataPanel.__init__(self, parent, client, options)
     self.layout().setMenuBar(self.toolbar)
     self.scroll.setWidgetResizable(True)
     self.scrollContent.setLayout(QVBoxLayout())
     client.disconnected.connect(self.on_client_disconnected)
     client.setup.connect(self.on_client_setup)
     client.cache.connect(self.on_client_cache)
Пример #13
0
class Chopper(MeasElement):
    """Element for selecting chopper TOF resolution."""

    CACHE_KEY = 'chopper/resolutions'
    SORT_KEY = lambda self, x: num_sort(x)
    LABEL = 'TOF dλ/λ'

    def createWidget(self, parent, client):
        resos = client.getDeviceParam(*self.CACHE_KEY.split('/'))
        self._values = ['off'] + ['%.1f%%' % v
                                  for v in (resos or [])] + ['manual']
        self._widget = QComboBox(parent)
        self._widget.addItems(self._values)
        if self.value is not None and self.value in self._values:
            self._widget.setCurrentIndex(self._values.index(self.value))
        elif self.value is None and self._values:
            self.value = self._values[0]
        self._widget.currentIndexChanged.connect(self._updateValue)
        return self._widget

    def _updateValue(self, index):
        self.value = self._values[index]
        self.changed.emit(self.value)
Пример #14
0
 def __init__(self,
              parent,
              curvalue,
              client,
              needs_class='nicos.core.device.Device',
              allow_enter=False):
     QComboBox.__init__(self, parent, editable=True)
     self.setSizePolicy(
         QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))
     if client:
         devs = client.getDeviceList(needs_class)
         self.addItems(devs)
         try:
             index = devs.index(curvalue)
             self.setCurrentIndex(index)
         except ValueError:
             self.setEditText(curvalue)
     else:
         self.setEditText(curvalue)
     self.editTextChanged.connect(lambda _: self.valueModified.emit())
     if allow_enter:
         self.lineEdit().returnPressed.connect(
             lambda: self.valueChosen.emit(self.currentText()))
Пример #15
0
class Detector(MeasElement):
    """Element for selecting detector distance, depending on selector."""

    CACHE_KEY = 'detector/presets'
    SORT_KEY = lambda self, x: num_sort(x)
    LABEL = 'Detector'

    _allvalues = None

    def clientUpdate(self, client):
        self._allvalues = client.getDeviceParam(*self.CACHE_KEY.split('/'))
        self._values = []

    def createWidget(self, parent, client):
        self.clientUpdate(client)
        self._widget = QComboBox(parent)
        self._updateWidget()
        self._widget.currentIndexChanged.connect(self._updateValue)
        return self._widget

    def _updateWidget(self):
        self._widget.clear()
        self._widget.addItems(self._values)
        if self.value in self._values:
            self._widget.setCurrentIndex(self._values.index(self.value))

    def otherChanged(self, eltype, value):
        if eltype == 'selector' and self._allvalues is not None:
            self._values = sorted(self._allvalues[value], key=self.SORT_KEY)
            if self.value not in self._values:
                if self._values:
                    self.value = self._values[0]
                else:
                    self.value = None
            if self._widget is not None:
                self._updateWidget()

    def _updateValue(self, index):
        self.value = self._values[index]
        self.changed.emit(self.value)
Пример #16
0
class ScansPanel(Panel):
    """Provides a display for the scans of the current experiment.

    Options:

    * ``fit_functions`` (default {}) -- dictionary for special fitting
      functions. The name of the fit function is followed by a set of
      a list of fit parameters and the string containing the fit function
      code::

          fit_functions = {
              'Resonance': (['Vmax = 0.1', 'R = 0.6', 'f = ', 'L = ', 'C = '],
                            'Vmax / sqrt(R**2 + (f*L-1/(f*C))**2)'),
          }

      The function can use the all mathematical functions of the
      `numpy <http://www.numpy.org/>`_ package.

    """
    panelName = 'Scans'

    def __init__(self, parent, client, options):
        Panel.__init__(self, parent, client, options)
        loadUi(self, 'panels/scans.ui')
        ArbitraryFitter.arby_functions.update(options.get('fit_functions', {}))

        self.statusBar = QStatusBar(self, sizeGripEnabled=False)
        policy = self.statusBar.sizePolicy()
        policy.setVerticalPolicy(QSizePolicy.Fixed)
        self.statusBar.setSizePolicy(policy)
        self.layout().addWidget(self.statusBar)

        self.x_menu = QMenu(self)
        self.x_menu.aboutToShow.connect(self.on_x_menu_aboutToShow)
        self.actionXAxis.setMenu(self.x_menu)

        self.y_menu = QMenu(self)
        self.y_menu.aboutToShow.connect(self.on_y_menu_aboutToShow)
        self.actionYAxis.setMenu(self.y_menu)

        self.actionAutoDisplay.setChecked(True)

        self.norm_menu = QMenu(self)
        self.norm_menu.aboutToShow.connect(self.on_norm_menu_aboutToShow)
        self.actionNormalized.setMenu(self.norm_menu)

        quickfit = QShortcut(QKeySequence("G"), self)
        quickfit.activated.connect(self.on_quickfit)

        self.user_color = Qt.white
        self.user_font = QFont('Monospace')

        self.bulk_adding = False
        self.no_openset = False
        self.last_norm_selection = None
        self.fitclass = GaussFitter
        self.fitfuncmap = {}

        self.menus = None
        self.bars = None

        self.data = self.mainwindow.data

        # maps set uid -> plot
        self.setplots = {}
        # maps set uid -> list item
        self.setitems = {}
        # current plot object
        self.currentPlot = None
        # stack of set uids
        self.setUidStack = []
        # uids of automatically combined datasets -> uid of combined one
        self.contSetUids = {}

        self.splitter.setSizes([20, 80])
        self.splitter.restoreState(self.splitterstate)
        if self.tablecolwidth0 > 0:
            self.metaTable.setColumnWidth(0, self.tablecolwidth0)
            self.metaTable.setColumnWidth(1, self.tablecolwidth1)

        self.data.datasetAdded.connect(self.on_data_datasetAdded)
        self.data.pointsAdded.connect(self.on_data_pointsAdded)
        self.data.fitAdded.connect(self.on_data_fitAdded)
        client.experiment.connect(self.on_client_experiment)

        self.setCurrentDataset(None)
        self.updateList()

    def loadSettings(self, settings):
        self.splitterstate = settings.value('splitter', '', QByteArray)
        self.tablecolwidth0 = settings.value('tablecolwidth0', 0, int)
        self.tablecolwidth1 = settings.value('tablecolwidth1', 0, int)

    def saveSettings(self, settings):
        settings.setValue('splitter', self.splitter.saveState())
        settings.setValue('tablecolwidth0', self.metaTable.columnWidth(0))
        settings.setValue('tablecolwidth1', self.metaTable.columnWidth(1))

    def setCustomStyle(self, font, back):
        self.user_font = font
        self.user_color = back

        for plot in self.setplots.values():
            plot.setBackgroundColor(back)
            plot.update()

        bold = QFont(font)
        bold.setBold(True)
        larger = scaledFont(font, 1.6)
        for plot in self.setplots.values():
            plot.setFonts(font, bold, larger)

    def requestClose(self):
        # Always succeeds, but break up circular references so that the panel
        # object can be deleted properly.
        self.currentPlot = None
        self.setplots.clear()
        return True

    def _autoscale(self, x=None, y=None):
        xflag = x if x is not None else self.actionScaleX.isChecked()
        yflag = y if y is not None else self.actionScaleY.isChecked()
        if self.currentPlot:
            self.currentPlot.setAutoScaleFlags(xflag, yflag)
            self.actionAutoScale.setChecked(xflag or yflag)
            self.actionScaleX.setChecked(xflag)
            self.actionScaleY.setChecked(yflag)
            self.currentPlot.update()

    def enablePlotActions(self, on):
        for action in [
            self.actionSavePlot, self.actionPrint, self.actionResetPlot,
            self.actionAttachElog, self.actionCombine, self.actionClosePlot,
            self.actionDeletePlot, self.actionLogXScale, self.actionLogScale,
            self.actionAutoScale, self.actionScaleX, self.actionScaleY,
            self.actionXAxis, self.actionYAxis, self.actionNormalized,
            self.actionUnzoom, self.actionLegend, self.actionModifyData,
            self.actionFitPeak, self.actionFitPeakPV, self.actionFitPeakPVII,
            self.actionFitTc, self.actionFitCosine, self.actionFitSigmoid,
            self.actionFitArby, self.actionErrors,
        ]:
            action.setEnabled(on)

    def enableAutoScaleActions(self, on):
        for action in [self.actionAutoScale, self.actionScaleX,
                       self.actionScaleY]:
            action.setEnabled(on)

    def getMenus(self):
        if not self.menus:
            menu1 = QMenu('&Data plot', self)
            menu1.addAction(self.actionSavePlot)
            menu1.addAction(self.actionPrint)
            menu1.addAction(self.actionAttachElog)
            menu1.addSeparator()
            menu1.addAction(self.actionResetPlot)
            menu1.addAction(self.actionAutoDisplay)
            menu1.addAction(self.actionCombine)
            menu1.addAction(self.actionClosePlot)
            menu1.addAction(self.actionDeletePlot)
            menu1.addSeparator()
            menu1.addAction(self.actionXAxis)
            menu1.addAction(self.actionYAxis)
            menu1.addAction(self.actionNormalized)
            menu1.addSeparator()
            menu1.addAction(self.actionUnzoom)
            menu1.addAction(self.actionLogXScale)
            menu1.addAction(self.actionLogScale)
            menu1.addAction(self.actionAutoScale)
            menu1.addAction(self.actionScaleX)
            menu1.addAction(self.actionScaleY)
            menu1.addAction(self.actionLegend)
            menu1.addAction(self.actionErrors)
            menu1.addSeparator()

            menu2 = QMenu('Data &manipulation', self)
            menu2.addAction(self.actionModifyData)
            menu2.addSeparator()
            ag = QActionGroup(menu2)
            ag.addAction(self.actionFitPeakGaussian)
            ag.addAction(self.actionFitPeakLorentzian)
            ag.addAction(self.actionFitPeakPV)
            ag.addAction(self.actionFitPeakPVII)
            ag.addAction(self.actionFitTc)
            ag.addAction(self.actionFitCosine)
            ag.addAction(self.actionFitSigmoid)
            ag.addAction(self.actionFitLinear)
            ag.addAction(self.actionFitExponential)
            menu2.addAction(self.actionFitPeak)
            menu2.addAction(self.actionPickInitial)
            menu2.addAction(self.actionFitPeakGaussian)
            menu2.addAction(self.actionFitPeakLorentzian)
            menu2.addAction(self.actionFitPeakPV)
            menu2.addAction(self.actionFitPeakPVII)
            menu2.addAction(self.actionFitTc)
            menu2.addAction(self.actionFitCosine)
            menu2.addAction(self.actionFitSigmoid)
            menu2.addAction(self.actionFitLinear)
            menu2.addAction(self.actionFitExponential)
            menu2.addSeparator()
            menu2.addAction(self.actionFitArby)

            self.menus = [menu1, menu2]

        return self.menus

    def getToolbars(self):
        if not self.bars:
            bar = QToolBar('Scans')
            bar.addAction(self.actionSavePlot)
            bar.addAction(self.actionPrint)
            bar.addSeparator()
            bar.addAction(self.actionXAxis)
            bar.addAction(self.actionYAxis)
            bar.addAction(self.actionNormalized)
            bar.addSeparator()
            bar.addAction(self.actionLogXScale)
            bar.addAction(self.actionLogScale)
            bar.addAction(self.actionUnzoom)
            bar.addSeparator()
            bar.addAction(self.actionAutoScale)
            bar.addAction(self.actionScaleX)
            bar.addAction(self.actionScaleY)
            bar.addAction(self.actionLegend)
            bar.addAction(self.actionErrors)
            bar.addAction(self.actionResetPlot)
            bar.addAction(self.actionDeletePlot)
            bar.addSeparator()
            bar.addAction(self.actionAutoDisplay)
            bar.addAction(self.actionCombine)

            fitbar = QToolBar('Scan fitting')
            fitbar.addAction(self.actionFitPeak)
            wa = QWidgetAction(fitbar)
            self.fitPickCheckbox = QCheckBox(fitbar)
            self.fitPickCheckbox.setText('Pick')
            self.fitPickCheckbox.setChecked(True)
            self.actionPickInitial.setChecked(True)
            self.fitPickCheckbox.toggled.connect(self.actionPickInitial.setChecked)
            self.actionPickInitial.toggled.connect(self.fitPickCheckbox.setChecked)
            layout = QHBoxLayout()
            layout.setContentsMargins(10, 0, 10, 0)
            layout.addWidget(self.fitPickCheckbox)
            frame = QFrame(fitbar)
            frame.setLayout(layout)
            wa.setDefaultWidget(frame)
            fitbar.addAction(wa)
            ag = QActionGroup(fitbar)
            ag.addAction(self.actionFitPeakGaussian)
            ag.addAction(self.actionFitPeakLorentzian)
            ag.addAction(self.actionFitPeakPV)
            ag.addAction(self.actionFitPeakPVII)
            ag.addAction(self.actionFitTc)
            ag.addAction(self.actionFitCosine)
            ag.addAction(self.actionFitSigmoid)
            ag.addAction(self.actionFitLinear)
            ag.addAction(self.actionFitExponential)
            wa = QWidgetAction(fitbar)
            self.fitComboBox = QComboBox(fitbar)
            for a in ag.actions():
                itemtext = a.text().replace('&', '')
                self.fitComboBox.addItem(itemtext)
                self.fitfuncmap[itemtext] = a
            self.fitComboBox.currentIndexChanged.connect(
                self.on_fitComboBox_currentIndexChanged)
            wa.setDefaultWidget(self.fitComboBox)
            fitbar.addAction(wa)
            fitbar.addSeparator()
            fitbar.addAction(self.actionFitArby)

            self.bars = [bar, fitbar]

        return self.bars

    def updateList(self):
        self.datasetList.clear()
        for dataset in self.data.sets:
            if dataset.invisible:
                continue
            shortname = '%s - %s' % (dataset.name, dataset.default_xname)
            item = QListWidgetItem(shortname, self.datasetList)
            item.setData(32, dataset.uid)
            self.setitems[dataset.uid] = item

    def on_logYinDomain(self, flag):
        if not flag:
            self.actionLogScale.setChecked(flag)

    def on_logXinDomain(self, flag):
        if not flag:
            self.actionLogXScale.setChecked(flag)

    def on_datasetList_currentItemChanged(self, item, previous):
        if self.no_openset or item is None:
            return
        self.openDataset(itemuid(item))

    def on_datasetList_itemClicked(self, item):
        # this handler is needed in addition to currentItemChanged
        # since one can't change the current item if it's the only one
        if self.no_openset or item is None:
            return
        self.openDataset(itemuid(item))

    def openDataset(self, uid):
        dataset = self.data.uid2set[uid]
        newplot = None
        if dataset.uid not in self.setplots:
            newplot = DataSetPlot(self.plotFrame, self, dataset)
            if self.currentPlot:
                newplot.enableCurvesFrom(self.currentPlot)
            self.setplots[dataset.uid] = newplot
        self.datasetList.setCurrentItem(self.setitems[uid])
        plot = self.setplots[dataset.uid]
        self.enableAutoScaleActions(plot.HAS_AUTOSCALE)
        if newplot and plot.HAS_AUTOSCALE:
            from gr.pygr import PlotAxes
            plot.plot.autoscale = PlotAxes.SCALE_X | PlotAxes.SCALE_Y
        self.setCurrentDataset(plot)

    def setCurrentDataset(self, plot):
        if self.currentPlot:
            self.plotLayout.removeWidget(self.currentPlot)
            self.currentPlot.hide()
            self.metaTable.clearContents()
        self.currentPlot = plot
        if plot is None:
            self.enablePlotActions(False)
        else:
            try:
                self.setUidStack.remove(plot.dataset.uid)
            except ValueError:
                pass
            self.setUidStack.append(plot.dataset.uid)

            num_items = 0
            for catname in INTERESTING_CATS:
                if catname in plot.dataset.headerinfo:
                    num_items += 2 + len(plot.dataset.headerinfo[catname])
            num_items -= 1  # remove last empty row
            self.metaTable.setRowCount(num_items)

            i = 0
            for catname in INTERESTING_CATS:
                if catname in plot.dataset.headerinfo:
                    values = plot.dataset.headerinfo[catname]
                    catdesc = catname
                    for name_desc in INFO_CATEGORIES:
                        if name_desc[0] == catname:
                            catdesc = name_desc[1]
                    catitem = QTableWidgetItem(catdesc)
                    font = catitem.font()
                    font.setBold(True)
                    catitem.setFont(font)
                    self.metaTable.setItem(i, 0, catitem)
                    self.metaTable.setSpan(i, 0, 1, 2)
                    i += 1
                    for dev, name, value in sorted(values):
                        key = '%s_%s' % (dev, name) if name != 'value' else dev
                        self.metaTable.setItem(i, 0, QTableWidgetItem(key))
                        self.metaTable.setItem(i, 1, QTableWidgetItem(value))
                        if self.metaTable.columnSpan(i, 0) == 2:
                            self.metaTable.setSpan(i, 0, 1, 1)
                        i += 1
                    i += 1
            self.metaTable.resizeRowsToContents()

            self.enablePlotActions(True)
            self.enableAutoScaleActions(self.currentPlot.HAS_AUTOSCALE)
            self.datasetList.setCurrentItem(self.setitems[plot.dataset.uid])

            self.actionXAxis.setText('X axis: %s' % plot.current_xname)
            self.actionNormalized.setChecked(bool(plot.normalized))

            self.actionLogScale.setChecked(plot.isLogScaling())
            self.actionLogXScale.setChecked(plot.isLogXScaling())
            self.actionLegend.setChecked(plot.isLegendEnabled())
            self.actionErrors.setChecked(plot.isErrorBarEnabled())
            if plot.HAS_AUTOSCALE:
                from gr.pygr import PlotAxes
                mask = plot.plot.autoscale
                self._autoscale(x=mask & PlotAxes.SCALE_X,
                                y=mask & PlotAxes.SCALE_Y)
                plot.logYinDomain.connect(self.on_logYinDomain)
                plot.logXinDomain.connect(self.on_logXinDomain)
            self.plotLayout.addWidget(plot)
            plot.show()

    def on_data_datasetAdded(self, dataset):
        shortname = '%s - %s' % (dataset.name, dataset.default_xname)
        if dataset.uid in self.setitems:
            self.setitems[dataset.uid].setText(shortname)
            if dataset.uid in self.setplots:
                self.setplots[dataset.uid].updateDisplay()
        else:
            self.no_openset = True
            item = QListWidgetItem(shortname, self.datasetList)
            item.setData(32, dataset.uid)
            self.setitems[dataset.uid] = item
            if self.actionAutoDisplay.isChecked() and not self.data.bulk_adding:
                self.openDataset(dataset.uid)
            self.no_openset = False
        # If the dataset is a continuation of another dataset, automatically
        # create a combined dataset.
        contuids = dataset.continuation
        if contuids:
            alluids = tuple(contuids.split(',')) + (dataset.uid,)
            # Did we already create this set?  Then don't create it again.
            if self.contSetUids.get(alluids) in self.setitems:
                return
            allsets = list(map(self.data.uid2set.get, alluids))
            newuid = self._combine(COMBINE, allsets)
            if newuid:
                self.contSetUids[alluids] = newuid

    def on_data_pointsAdded(self, dataset):
        if dataset.uid in self.setplots:
            self.setplots[dataset.uid].pointsAdded()

    def on_data_fitAdded(self, dataset, res):
        if dataset.uid in self.setplots:
            self.setplots[dataset.uid]._plotFit(res)

    def on_client_experiment(self, data):
        self.datasetList.clear()

        # hide plot
        self.setCurrentDataset(None)

        # back to the beginning
        self.setplots = {}
        self.setitems = {}
        self.currentPlot = None
        self.setUidStack = []

    @pyqtSlot()
    def on_actionClosePlot_triggered(self):
        current_set = self.setUidStack.pop()
        if self.setUidStack:
            self.setCurrentDataset(self.setplots[self.setUidStack[-1]])
        else:
            self.setCurrentDataset(None)
        del self.setplots[current_set]

    @pyqtSlot()
    def on_actionResetPlot_triggered(self):
        current_set = self.setUidStack.pop()
        del self.setplots[current_set]
        self.openDataset(current_set)

    @pyqtSlot()
    def on_actionDeletePlot_triggered(self):
        if self.currentPlot.dataset.scaninfo != 'combined set':
            if not self.askQuestion('This is not a combined set: still '
                                    'delete it from the list?'):
                return
        current_set = self.setUidStack.pop()
        self.data.uid2set[current_set].invisible = True
        if self.setUidStack:
            self.setCurrentDataset(self.setplots[self.setUidStack[-1]])
        else:
            self.setCurrentDataset(None)
        del self.setplots[current_set]
        for i in range(self.datasetList.count()):
            if itemuid(self.datasetList.item(i)) == current_set:
                self.datasetList.takeItem(i)
                break

    @pyqtSlot()
    def on_actionSavePlot_triggered(self):
        filename = self.currentPlot.savePlot()
        if filename:
            self.statusBar.showMessage('Plot successfully saved to %s.' %
                                       filename)

    @pyqtSlot()
    def on_actionPrint_triggered(self):
        if self.currentPlot.printPlot():
            self.statusBar.showMessage('Plot successfully printed.')

    @pyqtSlot()
    def on_actionAttachElog_triggered(self):
        newdlg = dialogFromUi(self, 'panels/plot_attach.ui')
        suffix = self.currentPlot.SAVE_EXT
        newdlg.filename.setText(
            safeName('data_%s' % self.currentPlot.dataset.name + suffix))
        ret = newdlg.exec_()
        if ret != QDialog.Accepted:
            return
        descr = newdlg.description.text()
        fname = newdlg.filename.text()
        pathname = self.currentPlot.saveQuietly()
        with open(pathname, 'rb') as fp:
            remotefn = self.client.ask('transfer', fp.read())
        if remotefn is not None:
            self.client.eval('_LogAttach(%r, [%r], [%r])' %
                             (descr, remotefn, fname))
        os.unlink(pathname)

    @pyqtSlot()
    def on_actionUnzoom_triggered(self):
        self.currentPlot.unzoom()

    @pyqtSlot(bool)
    def on_actionLogScale_toggled(self, on):
        self.currentPlot.setLogScale(on)

    @pyqtSlot(bool)
    def on_actionLogXScale_toggled(self, on):
        self.currentPlot.setLogXScale(on)

    @pyqtSlot(bool)
    def on_actionAutoScale_toggled(self, on):
        self._autoscale(on, on)

    @pyqtSlot(bool)
    def on_actionScaleX_toggled(self, on):
        self._autoscale(x=on)

    @pyqtSlot(bool)
    def on_actionScaleY_toggled(self, on):
        self._autoscale(y=on)

    def on_x_menu_aboutToShow(self):
        self.x_menu.clear()
        if not self.currentPlot:
            return
        done = set()
        for name in self.currentPlot.dataset.xnameunits:
            if name in done:
                continue
            done.add(name)
            action = self.x_menu.addAction(name)
            action.setData(name)
            action.setCheckable(True)
            if name == self.currentPlot.current_xname:
                action.setChecked(True)
            action.triggered.connect(self.on_x_action_triggered)

    @pyqtSlot()
    def on_x_action_triggered(self, text=None):
        if text is None:
            text = self.sender().data()
        self.actionXAxis.setText('X axis: %s' % text)
        self.currentPlot.current_xname = text
        self.currentPlot.updateDisplay()
        self.on_actionUnzoom_triggered()

    @pyqtSlot()
    def on_actionXAxis_triggered(self):
        self.bars[0].widgetForAction(self.actionXAxis).showMenu()

    def on_y_menu_aboutToShow(self):
        self.y_menu.clear()
        if not self.currentPlot:
            return
        for curve in self.currentPlot.dataset.curves:
            action = self.y_menu.addAction(curve.full_description)
            action.setData(curve.full_description)
            action.setCheckable(True)
            if not curve.hidden:
                action.setChecked(True)
            action.triggered.connect(self.on_y_action_triggered)

    @pyqtSlot()
    def on_y_action_triggered(self, text=None):
        if text is None:
            text = self.sender().data()
        if not self.currentPlot:
            return
        for curve in self.currentPlot.dataset.curves:
            if curve.full_description == text:
                curve.hidden = not curve.hidden
        self.currentPlot.updateDisplay()
        self.on_actionUnzoom_triggered()

    @pyqtSlot()
    def on_actionYAxis_triggered(self):
        self.bars[0].widgetForAction(self.actionYAxis).showMenu()

    @pyqtSlot()
    def on_actionNormalized_triggered(self):
        if not self.currentPlot:
            return
        if self.currentPlot.normalized is not None:
            self.on_norm_action_triggered('None')
        else:
            all_normnames = [name for (_, name)
                             in self.currentPlot.dataset.normindices]
            if self.last_norm_selection and \
               self.last_norm_selection in all_normnames:
                use = self.last_norm_selection
            else:
                use = all_normnames[0] if all_normnames else 'None'
            self.on_norm_action_triggered(use)

    def on_norm_menu_aboutToShow(self):
        self.norm_menu.clear()
        if self.currentPlot:
            none_action = self.norm_menu.addAction('None')
            none_action.setData('None')
            none_action.setCheckable(True)
            none_action.setChecked(True)
            none_action.triggered.connect(self.on_norm_action_triggered)
            max_action = self.norm_menu.addAction('Maximum')
            max_action.setData('Maximum')
            max_action.setCheckable(True)
            if self.currentPlot.normalized == 'Maximum':
                max_action.setChecked(True)
                none_action.setChecked(False)
            max_action.triggered.connect(self.on_norm_action_triggered)
            for _, name in self.currentPlot.dataset.normindices:
                action = self.norm_menu.addAction(name)
                action.setData(name)
                action.setCheckable(True)
                if name == self.currentPlot.normalized:
                    action.setChecked(True)
                    none_action.setChecked(False)
                action.triggered.connect(self.on_norm_action_triggered)

    @pyqtSlot()
    def on_norm_action_triggered(self, text=None):
        if text is None:
            text = self.sender().data()
        if text == 'None':
            self.currentPlot.normalized = None
            self.actionNormalized.setChecked(False)
        else:
            self.last_norm_selection = text
            self.currentPlot.normalized = text
            self.actionNormalized.setChecked(True)
        self.currentPlot.updateDisplay()
        self.on_actionUnzoom_triggered()

    @pyqtSlot(bool)
    def on_actionLegend_toggled(self, on):
        self.currentPlot.setLegend(on)

    @pyqtSlot(bool)
    def on_actionErrors_toggled(self, on):
        self.currentPlot.setErrorBarEnabled(on)

    @pyqtSlot()
    def on_actionModifyData_triggered(self):
        self.currentPlot.modifyData()

    @pyqtSlot()
    def on_actionFitPeak_triggered(self):
        self.currentPlot.beginFit(self.fitclass, self.actionFitPeak,
                                  pickmode=self.fitPickCheckbox.isChecked())

    @pyqtSlot(int)
    def on_fitComboBox_currentIndexChanged(self, index):
        self.fitfuncmap[self.fitComboBox.currentText()].trigger()

    @pyqtSlot()
    def on_actionFitPeakGaussian_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitPeakGaussian.text().replace('&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = GaussFitter

    @pyqtSlot()
    def on_actionFitPeakLorentzian_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitPeakLorentzian.text().replace('&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = LorentzFitter

    @pyqtSlot()
    def on_actionFitPeakPV_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitPeakPV.text().replace('&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = PseudoVoigtFitter

    @pyqtSlot()
    def on_actionFitPeakPVII_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitPeakPVII.text().replace('&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = PearsonVIIFitter

    @pyqtSlot()
    def on_actionFitTc_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitTc.text().replace('&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = TcFitter

    @pyqtSlot()
    def on_actionFitCosine_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitCosine.text().replace('&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = CosineFitter

    @pyqtSlot()
    def on_actionFitSigmoid_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitSigmoid.text().replace('&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = SigmoidFitter

    @pyqtSlot()
    def on_actionFitLinear_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitLinear.text().replace('&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = LinearFitter

    @pyqtSlot()
    def on_actionFitExponential_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitExponential.text().replace('&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = ExponentialFitter

    @pyqtSlot()
    def on_actionFitArby_triggered(self):
        # no second argument: the "arbitrary" action is not checkable
        self.currentPlot.beginFit(ArbitraryFitter, None,
                                  pickmode=self.fitPickCheckbox.isChecked())

    @pyqtSlot()
    def on_quickfit(self):
        if not self.currentPlot or not self.currentPlot.underMouse():
            return
        self.currentPlot.fitQuick()

    @pyqtSlot()
    def on_actionCombine_triggered(self):
        current = self.currentPlot.dataset.uid
        dlg = dialogFromUi(self, 'panels/dataops.ui')
        for i in range(self.datasetList.count()):
            item = self.datasetList.item(i)
            newitem = QListWidgetItem(item.text(), dlg.otherList)
            newitem.setData(32, item.data(32))
            if itemuid(item) == current:
                dlg.otherList.setCurrentItem(newitem)
                # paint the current set in grey to indicate it's not allowed
                # to be selected
                newitem.setBackground(self.palette().brush(QPalette.Mid))
                newitem.setFlags(Qt.NoItemFlags)
        if dlg.exec_() != QDialog.Accepted:
            return
        items = dlg.otherList.selectedItems()
        sets = [self.data.uid2set[current]]
        for item in items:
            if itemuid(item) == current:
                return self.showError('Cannot combine set with itself.')
            sets.append(self.data.uid2set[itemuid(item)])
        for rop, rb in [(TOGETHER, dlg.opTogether),
                        (COMBINE, dlg.opCombine),
                        (ADD, dlg.opAdd),
                        (SUBTRACT, dlg.opSubtract),
                        (DIVIDE, dlg.opDivide)]:
            if rb.isChecked():
                op = rop
                break
        self._combine(op, sets)

    def _combine(self, op, sets):
        if op == TOGETHER:
            newset = ScanData()
            newset.name = combineattr(sets, 'name', sep=', ')
            newset.invisible = False
            newset.curves = []
            newset.scaninfo = 'combined set'
            # combine xnameunits from those that are in all sets
            all_xnu = set(sets[0].xnameunits)
            for dset in sets[1:]:
                all_xnu &= set(dset.xnameunits)
            newset.xnameunits = ['Default'] + [xnu for xnu in sets[0].xnameunits
                                               if xnu in all_xnu]
            newset.default_xname = 'Default'
            newset.normindices = sets[0].normindices
            # for together only, the number of curves and their columns
            # are irrelevant, just put all together
            for dataset in sets:
                for curve in dataset.curves:
                    newcurve = curve.copy()
                    if not newcurve.source:
                        newcurve.source = dataset.name
                    newset.curves.append(newcurve)
            self.data.add_existing_dataset(newset, [dataset.uid for dataset in sets])
            return newset.uid
        # else, need same axes, and same number and types of curves

        firstset = sets[0]
        nameprops = [firstset.xnames, firstset.xunits]
        curveprops = [(curve.description, curve.yindex)
                      for curve in firstset.curves]
        for dataset in sets[1:]:
            if [dataset.xnames, dataset.xunits] != nameprops:
                self.showError('Sets have different axes.')
                return
            if [(curve.description, curve.yindex)
                    for curve in dataset.curves] != curveprops:
                self.showError('Sets have different curves.')
                return
        if op == COMBINE:
            newset = ScanData()
            newset.name = combineattr(sets, 'name', sep=', ')
            newset.invisible = False
            newset.curves = []
            newset.scaninfo = 'combined set'
            newset.xnameunits = firstset.xnameunits
            newset.default_xname = firstset.default_xname
            newset.normindices = firstset.normindices
            for curves in zip(*(dataset.curves for dataset in sets)):
                newcurve = curves[0].copy()
                newcurve.datay = DataProxy(c.datay for c in curves)
                newcurve.datady = DataProxy(c.datady for c in curves)
                newcurve.datax = {xnu: DataProxy(c.datax[xnu] for c in curves)
                                  for xnu in newset.xnameunits}
                newcurve.datanorm = {nn: DataProxy(c.datanorm[nn] for c in curves)
                                     for i, nn in newset.normindices}
                newset.curves.append(newcurve)
            self.data.add_existing_dataset(newset,
                                           [dataset.uid for dataset in sets])
            return newset.uid

        if op == ADD:
            sep = ' + '
        elif op == SUBTRACT:
            sep = ' - '
        elif op == DIVIDE:
            sep = ' / '

        newset = ScanData()
        newset.name = combineattr(sets, 'name', sep=sep)
        newset.invisible = False
        newset.scaninfo = 'combined set'
        newset.curves = []
        newset.xnameunits = firstset.xnameunits
        newset.default_xname = firstset.default_xname
        if op in (SUBTRACT, DIVIDE):
            # remove information about normalization -- doesn't make sense
            newset.normindices = []
        else:
            newset.normindices = firstset.normindices

        for curves in zip(*(dataset.curves for dataset in sets)):
            newcurve = curves[0].deepcopy()
            # CRUDE HACK: don't care about the x values, operate by index
            removepoints = set()
            for curve in curves[1:]:
                for i in range(len(newcurve.datay)):
                    y1, y2 = float(newcurve.datay[i]), float(curve.datay[i])
                    if newcurve.dyindex != -1:
                        dy1 = newcurve.datady[i]
                        dy2 = curve.datady[i]
                    else:
                        dy1 = dy2 = 1.
                    if op == ADD:
                        newcurve.datay[i] = y1 + y2
                        newcurve.datady[i] = sqrt(dy1**2 + dy2**2)
                        for name in newcurve.datanorm:
                            newcurve.datanorm[name][i] += curve.datanorm[name][i]
                    elif op == SUBTRACT:
                        newcurve.datay[i] = y1 - y2
                        newcurve.datady[i] = sqrt(dy1**2 + dy2**2)
                    elif op == DIVIDE:
                        if y2 == 0:
                            y2 = 1.  # generate a value for now
                            removepoints.add(i)
                        newcurve.datay[i] = y1 / y2
                        newcurve.datady[i] = sqrt((dy1/y2)**2 +
                                                  (dy2*y1 / y2**2)**2)
            # remove points where we would have divided by zero
            if removepoints:
                newcurve.datay = [v for (i, v) in enumerate(newcurve.datay)
                                  if i not in removepoints]
                newcurve.datady = [v for (i, v) in enumerate(newcurve.datady)
                                   if i not in removepoints]
                for name in newcurve.datax:
                    newcurve.datax[name] = \
                        [v for (i, v) in enumerate(newcurve.datax[name])
                         if i not in removepoints]
                for name in newcurve.datanorm:
                    newcurve.datanorm[name] = \
                        [v for (i, v) in enumerate(newcurve.datanorm[name])
                         if i not in removepoints]
            newset.curves.append(newcurve)
        self.data.add_existing_dataset(newset)
        return newset.uid
Пример #17
0
    def getToolbars(self):
        if not self.bars:
            bar = QToolBar('Scans')
            bar.addAction(self.actionSavePlot)
            bar.addAction(self.actionPrint)
            bar.addSeparator()
            bar.addAction(self.actionXAxis)
            bar.addAction(self.actionYAxis)
            bar.addAction(self.actionNormalized)
            bar.addSeparator()
            bar.addAction(self.actionLogXScale)
            bar.addAction(self.actionLogScale)
            bar.addAction(self.actionUnzoom)
            bar.addSeparator()
            bar.addAction(self.actionAutoScale)
            bar.addAction(self.actionScaleX)
            bar.addAction(self.actionScaleY)
            bar.addAction(self.actionLegend)
            bar.addAction(self.actionErrors)
            bar.addAction(self.actionResetPlot)
            bar.addAction(self.actionDeletePlot)
            bar.addSeparator()
            bar.addAction(self.actionAutoDisplay)
            bar.addAction(self.actionCombine)

            fitbar = QToolBar('Scan fitting')
            fitbar.addAction(self.actionFitPeak)
            wa = QWidgetAction(fitbar)
            self.fitPickCheckbox = QCheckBox(fitbar)
            self.fitPickCheckbox.setText('Pick')
            self.fitPickCheckbox.setChecked(True)
            self.actionPickInitial.setChecked(True)
            self.fitPickCheckbox.toggled.connect(self.actionPickInitial.setChecked)
            self.actionPickInitial.toggled.connect(self.fitPickCheckbox.setChecked)
            layout = QHBoxLayout()
            layout.setContentsMargins(10, 0, 10, 0)
            layout.addWidget(self.fitPickCheckbox)
            frame = QFrame(fitbar)
            frame.setLayout(layout)
            wa.setDefaultWidget(frame)
            fitbar.addAction(wa)
            ag = QActionGroup(fitbar)
            ag.addAction(self.actionFitPeakGaussian)
            ag.addAction(self.actionFitPeakLorentzian)
            ag.addAction(self.actionFitPeakPV)
            ag.addAction(self.actionFitPeakPVII)
            ag.addAction(self.actionFitTc)
            ag.addAction(self.actionFitCosine)
            ag.addAction(self.actionFitSigmoid)
            ag.addAction(self.actionFitLinear)
            ag.addAction(self.actionFitExponential)
            wa = QWidgetAction(fitbar)
            self.fitComboBox = QComboBox(fitbar)
            for a in ag.actions():
                itemtext = a.text().replace('&', '')
                self.fitComboBox.addItem(itemtext)
                self.fitfuncmap[itemtext] = a
            self.fitComboBox.currentIndexChanged.connect(
                self.on_fitComboBox_currentIndexChanged)
            wa.setDefaultWidget(self.fitComboBox)
            fitbar.addAction(wa)
            fitbar.addSeparator()
            fitbar.addAction(self.actionFitArby)

            self.bars = [bar, fitbar]

        return self.bars
Пример #18
0
class BaseHistoryWindow(object):

    client = None
    presetdict = None

    def __init__(self):
        loadUi(self, 'panels/history.ui')

        self.user_color = Qt.white
        self.user_font = QFont('Monospace')

        self.views = []
        # stack of views to display
        self.viewStack = []
        # maps watched keys to their views
        self.keyviews = {}
        # current plot object
        self.currentPlot = None
        self.fitclass = LinearFitter
        self.fitfuncmap = {}

        self.enablePlotActions(False)

        self.presetmenu = QMenu('&Presets', self)

        for (name, view) in self.last_views:
            item = QListWidgetItem(name, self.viewList)
            item.setForeground(QBrush(QColor('#aaaaaa')))
            item.setData(Qt.UserRole, view)

        self.menus = None
        self.bar = None

        # NOTE: for this class, automatic connections don't work on PyQt4 >=
        # 4.12 since this class is not derived from QObject. But on older PyQt4
        # and PyQt5, they do work, so we change use the usual naming scheme
        # slightly to avoid double connections.
        self.viewList.currentItemChanged.connect(
            self.on__viewList_currentItemChanged)
        self.viewList.itemClicked.connect(self.on__viewList_itemClicked)
        self.viewList.itemDoubleClicked.connect(
            self.on__viewList_itemDoubleClicked)
        self.actionNew.triggered.connect(self.on__actionNew_triggered)
        self.actionEditView.triggered.connect(
            self.on__actionEditView_triggered)
        self.actionCloseView.triggered.connect(
            self.on__actionCloseView_triggered)
        self.actionResetView.triggered.connect(
            self.on__actionResetView_triggered)
        self.actionDeleteView.triggered.connect(
            self.on__actionDeleteView_triggered)
        self.actionSavePlot.triggered.connect(
            self.on__actionSavePlot_triggered)
        self.actionPrint.triggered.connect(self.on__actionPrint_triggered)
        self.actionUnzoom.triggered.connect(self.on__actionUnzoom_triggered)
        self.actionLogScale.toggled.connect(self.on__actionLogScale_toggled)
        self.actionAutoScale.toggled.connect(self.on__actionAutoScale_toggled)
        self.actionScaleX.toggled.connect(self.on__actionScaleX_toggled)
        self.actionScaleY.toggled.connect(self.on__actionScaleY_toggled)
        self.actionLegend.toggled.connect(self.on__actionLegend_toggled)
        self.actionSymbols.toggled.connect(self.on__actionSymbols_toggled)
        self.actionLines.toggled.connect(self.on__actionLines_toggled)
        self.actionSaveData.triggered.connect(
            self.on__actionSaveData_triggered)
        self.actionFitPeak.triggered.connect(self.on__actionFitPeak_triggered)
        self.actionFitArby.triggered.connect(self.on__actionFitArby_triggered)
        self.actionFitPeakGaussian.triggered.connect(
            self.on__actionFitPeakGaussian_triggered)
        self.actionFitPeakLorentzian.triggered.connect(
            self.on__actionFitPeakLorentzian_triggered)
        self.actionFitPeakPV.triggered.connect(
            self.on__actionFitPeakPV_triggered)
        self.actionFitPeakPVII.triggered.connect(
            self.on__actionFitPeakPVII_triggered)
        self.actionFitTc.triggered.connect(self.on__actionFitTc_triggered)
        self.actionFitCosine.triggered.connect(
            self.on__actionFitCosine_triggered)
        self.actionFitSigmoid.triggered.connect(
            self.on__actionFitSigmoid_triggered)
        self.actionFitLinear.triggered.connect(
            self.on__actionFitLinear_triggered)
        self.actionFitExponential.triggered.connect(
            self.on__actionFitExponential_triggered)

    def getMenus(self):
        menu = QMenu('&History viewer', self)
        menu.addAction(self.actionNew)
        menu.addSeparator()
        menu.addAction(self.actionSavePlot)
        menu.addAction(self.actionPrint)
        menu.addAction(self.actionAttachElog)
        menu.addAction(self.actionSaveData)
        menu.addSeparator()
        menu.addAction(self.actionEditView)
        menu.addAction(self.actionCloseView)
        menu.addAction(self.actionDeleteView)
        menu.addAction(self.actionResetView)
        menu.addSeparator()
        menu.addAction(self.actionLogScale)
        menu.addAction(self.actionAutoScale)
        menu.addAction(self.actionScaleX)
        menu.addAction(self.actionScaleY)
        menu.addAction(self.actionUnzoom)
        menu.addAction(self.actionLegend)
        menu.addAction(self.actionSymbols)
        menu.addAction(self.actionLines)
        ag = QActionGroup(menu)
        ag.addAction(self.actionFitPeakGaussian)
        ag.addAction(self.actionFitPeakLorentzian)
        ag.addAction(self.actionFitPeakPV)
        ag.addAction(self.actionFitPeakPVII)
        ag.addAction(self.actionFitTc)
        ag.addAction(self.actionFitCosine)
        ag.addAction(self.actionFitSigmoid)
        ag.addAction(self.actionFitLinear)
        ag.addAction(self.actionFitExponential)
        menu.addAction(self.actionFitPeak)
        menu.addAction(self.actionPickInitial)
        menu.addAction(self.actionFitPeakGaussian)
        menu.addAction(self.actionFitPeakLorentzian)
        menu.addAction(self.actionFitPeakPV)
        menu.addAction(self.actionFitPeakPVII)
        menu.addAction(self.actionFitTc)
        menu.addAction(self.actionFitCosine)
        menu.addAction(self.actionFitSigmoid)
        menu.addAction(self.actionFitLinear)
        menu.addAction(self.actionFitExponential)
        menu.addSeparator()
        menu.addAction(self.actionFitArby)
        menu.addSeparator()
        menu.addAction(self.actionClose)
        self._refresh_presets()
        return [menu, self.presetmenu]

    def getToolbars(self):
        if not self.bar:
            bar = QToolBar('History viewer')
            bar.addAction(self.actionNew)
            bar.addAction(self.actionEditView)
            bar.addSeparator()
            bar.addAction(self.actionSavePlot)
            bar.addAction(self.actionPrint)
            bar.addAction(self.actionSaveData)
            bar.addSeparator()
            bar.addAction(self.actionUnzoom)
            bar.addAction(self.actionLogScale)
            bar.addSeparator()
            bar.addAction(self.actionAutoScale)
            bar.addAction(self.actionScaleX)
            bar.addAction(self.actionScaleY)
            bar.addSeparator()
            bar.addAction(self.actionResetView)
            bar.addAction(self.actionDeleteView)
            bar.addSeparator()
            bar.addAction(self.actionFitPeak)
            wa = QWidgetAction(bar)
            self.fitPickCheckbox = QCheckBox(bar)
            self.fitPickCheckbox.setText('Pick')
            self.fitPickCheckbox.setChecked(True)
            self.actionPickInitial.setChecked(True)
            self.fitPickCheckbox.toggled.connect(
                self.actionPickInitial.setChecked)
            self.actionPickInitial.toggled.connect(
                self.fitPickCheckbox.setChecked)
            layout = QHBoxLayout()
            layout.setContentsMargins(10, 0, 10, 0)
            layout.addWidget(self.fitPickCheckbox)
            frame = QFrame(bar)
            frame.setLayout(layout)
            wa.setDefaultWidget(frame)
            bar.addAction(wa)
            ag = QActionGroup(bar)
            ag.addAction(self.actionFitPeakGaussian)
            ag.addAction(self.actionFitPeakLorentzian)
            ag.addAction(self.actionFitPeakPV)
            ag.addAction(self.actionFitPeakPVII)
            ag.addAction(self.actionFitTc)
            ag.addAction(self.actionFitCosine)
            ag.addAction(self.actionFitSigmoid)
            ag.addAction(self.actionFitLinear)
            ag.addAction(self.actionFitExponential)
            wa = QWidgetAction(bar)
            self.fitComboBox = QComboBox(bar)
            for a in ag.actions():
                itemtext = a.text().replace('&', '')
                self.fitComboBox.addItem(itemtext)
                self.fitfuncmap[itemtext] = a
            self.fitComboBox.currentIndexChanged.connect(
                self.on__fitComboBox_currentIndexChanged)
            wa.setDefaultWidget(self.fitComboBox)
            bar.addAction(wa)
            bar.addSeparator()
            bar.addAction(self.actionFitArby)
            self.bar = bar
            self.actionFitLinear.trigger()

        return [self.bar]

    def loadSettings(self, settings):
        self.splitterstate = settings.value('splitter', '', QByteArray)
        self.presetdict = {}
        # read new format if present
        settings.beginGroup('presets_new')
        for key in settings.childKeys():
            self.presetdict[key] = json.loads(settings.value(key))
        settings.endGroup()
        # convert old format
        try:
            presetval = settings.value('presets')
            if presetval:
                for (name, value) in presetval.items():
                    if not isinstance(value, bytes):
                        value = value.encode('latin1')
                    self.presetdict[name] = pickle.loads(value)
        except Exception:
            pass
        settings.remove('presets')
        self.last_views = []
        settings.beginGroup('views_new')
        for key in settings.childKeys():
            try:
                info = json.loads(settings.value(key))
                self.last_views.append((key, info))
            except Exception:
                pass
        settings.endGroup()

    def saveSettings(self, settings):
        settings.setValue('splitter', self.splitter.saveState())
        settings.beginGroup('presets_new')
        for (key, info) in self.presetdict.items():
            settings.setValue(key, json.dumps(info))
        settings.endGroup()
        settings.beginGroup('views_new')
        for view in self.views:
            settings.setValue(view.name, json.dumps(view.dlginfo))
        settings.endGroup()

    def openViews(self, views):
        """Open some views given by the specs in *views*, a list of strings.

        Each string can be a comma-separated list of key names, and an optional
        simple time spec (like "1h") separated by a colon.

        If a view spec matches the name of a preset, it is used instead.
        """
        for viewspec in views:
            timespec = '1h'
            if ':' in viewspec:
                viewspec, timespec = viewspec.rsplit(':', 1)
            info = dict(
                name=viewspec,
                devices=viewspec,
                simpleTime=True,
                simpleTimeSpec=timespec,
                slidingWindow=True,
                frombox=False,
                tobox=False,
                fromdate=0,
                todate=0,
                interval='',
                customY=False,
                customYFrom='',
                customYTo='',
            )
            self._createViewFromDialog(info)

    def _refresh_presets(self):
        pmenu = self.presetmenu
        pmenu.clear()
        delmenu = QMenu('Delete', self)
        try:
            for preset, info in iteritems(self.presetdict):
                paction = QAction(preset, self)
                pdelaction = QAction(preset, self)
                info = info.copy()

                def launchpreset(on, info=info):
                    self._createViewFromDialog(info)

                def delpreset(on, name=preset, act=paction, delact=pdelaction):
                    pmenu.removeAction(act)
                    delmenu.removeAction(delact)
                    self.presetdict.pop(name, None)
                    self._refresh_presets()

                paction.triggered[bool].connect(launchpreset)
                pmenu.addAction(paction)
                pdelaction.triggered[bool].connect(delpreset)
                delmenu.addAction(pdelaction)
        except AttributeError:
            self.presetdict = {}
        if self.presetdict:
            pmenu.addSeparator()
            pmenu.addMenu(delmenu)
        else:
            pmenu.addAction('(no presets created)')

    def _add_preset(self, name, info):
        if name:
            self.presetdict[name] = info.copy()
            self._refresh_presets()

    def _autoscale(self, x=None, y=None):
        xflag = x if x is not None else self.actionScaleX.isChecked()
        yflag = y if y is not None else self.actionScaleY.isChecked()
        if self.currentPlot:
            self.currentPlot.setAutoScaleFlags(xflag, yflag)
            self.actionAutoScale.setChecked(xflag or yflag)
            self.actionScaleX.setChecked(xflag)
            self.actionScaleY.setChecked(yflag)
            self.currentPlot.update()

    def enablePlotActions(self, on):
        for action in [
                self.actionSavePlot,
                self.actionPrint,
                self.actionAttachElog,
                self.actionSaveData,
                self.actionAutoScale,
                self.actionScaleX,
                self.actionScaleY,
                self.actionEditView,
                self.actionCloseView,
                self.actionDeleteView,
                self.actionResetView,
                self.actionUnzoom,
                self.actionLogScale,
                self.actionLegend,
                self.actionSymbols,
                self.actionLines,
                self.actionFitPeak,
                self.actionFitArby,
        ]:
            action.setEnabled(on)

    def enableAutoScaleActions(self, on):
        for action in [
                self.actionAutoScale, self.actionScaleX, self.actionScaleY
        ]:
            action.setEnabled(on)

    def on__fitComboBox_currentIndexChanged(self, index):
        self.fitfuncmap[self.fitComboBox.currentText()].trigger()

    def on__viewList_currentItemChanged(self, item, previous):
        if item is None:
            return
        for view in self.views:
            if view.listitem == item:
                self.openView(view)

    def on__viewList_itemClicked(self, item):
        # this handler is needed in addition to currentItemChanged
        # since one can't change the current item if it's the only one
        self.on__viewList_currentItemChanged(item, None)
        # is it a "saved from last time" item?
        info = item.data(Qt.UserRole)
        if info is not None:
            row = self.viewList.row(item)

            do_restore = self.askQuestion('Restore this view from last time?')
            self.viewList.takeItem(row)
            if do_restore:
                self._createViewFromDialog(info, row)

    def on_logYinDomain(self, flag):
        if not flag:
            self.actionLogScale.setChecked(flag)

    def newvalue_callback(self, data):
        (vtime, key, op, value) = data
        if key not in self.keyviews:
            return
        if not value:
            return
        value = cache_load(value)
        for view in self.keyviews[key]:
            view.newValue(vtime, key, op, value)

    def _createViewFromDialog(self, info, row=None):
        if not info['devices'].strip():
            return
        keys_indices = [
            extractKeyAndIndex(d.strip()) for d in info['devices'].split(',')
        ]
        if self.client is not None:
            meta = self._getMetainfo(keys_indices)
        else:
            meta = ({}, {})
        name = info['name']
        if not name:
            name = info['devices']
        if info['simpleTime']:
            name += ' (%s)' % info['simpleTimeSpec']
        window = None
        if info['simpleTime']:
            try:
                itime, _ = get_time_and_interval(info['simpleTimeSpec'])
            except ValueError:
                return
            fromtime = currenttime() - itime
            totime = None
            if info['slidingWindow']:
                window = itime
        else:
            if info['frombox']:
                fromtime = mktime(localtime(info['fromdate']))
            else:
                fromtime = None
            if info['tobox']:
                totime = mktime(localtime(info['todate']))
            else:
                totime = None
        try:
            interval = float(info['interval'])
        except ValueError:
            interval = 5.0
        if info['customY']:
            try:
                yfrom = float(info['customYFrom'])
            except ValueError:
                return
            try:
                yto = float(info['customYTo'])
            except ValueError:
                return
        else:
            yfrom = yto = None
        view = View(self, name, keys_indices, interval, fromtime, totime,
                    yfrom, yto, window, meta, info, self.gethistory_callback)
        self.views.append(view)
        view.listitem = QListWidgetItem(view.name)
        if row is not None:
            self.viewList.insertItem(row, view.listitem)
        else:
            self.viewList.addItem(view.listitem)
        self.openView(view)
        if view.totime is None:
            for key in view.uniq_keys:
                self.keyviews.setdefault(key, []).append(view)
        return view

    def _getMetainfo(self, keys_indices):
        """Collect unit and string<->integer mapping for each key that
        refers to a device main value.
        """
        units = {}
        mappings = {}
        seen = set()
        for key, _, _, _ in keys_indices:
            if key in seen or not key.endswith('/value'):
                continue
            seen.add(key)
            devname = key[:-6]
            devunit = self.client.getDeviceParam(devname, 'unit')
            if devunit:
                units[key] = devunit
            devmapping = self.client.getDeviceParam(devname, 'mapping')
            if devmapping:
                mappings[key] = m = {}
                i = 0
                for k, v in sorted(devmapping.items()):
                    if isinstance(v, integer_types):
                        m[k] = v
                    else:
                        m[k] = i
                        i += 1
        return units, mappings

    def on__actionNew_triggered(self):
        self.showNewDialog()

    def showNewDialog(self, devices=''):
        newdlg = NewViewDialog(self, client=self.client)
        newdlg.devices.setText(devices)
        ret = newdlg.exec_()
        if ret != QDialog.Accepted:
            return
        info = newdlg.infoDict()
        self._createViewFromDialog(info)
        if newdlg.savePreset.isChecked():
            self._add_preset(info['name'], info)

    def newView(self, devices):
        newdlg = NewViewDialog(self)
        newdlg.devices.setText(devices)
        info = newdlg.infoDict()
        self._createViewFromDialog(info)

    def openView(self, view):
        if not view.plot:
            view.plot = ViewPlot(self.plotFrame, self, view)
        self.viewList.setCurrentItem(view.listitem)
        self.setCurrentView(view)

    def setCurrentView(self, view):
        newView = False
        if self.currentPlot:
            self.plotLayout.removeWidget(self.currentPlot)
            self.currentPlot.hide()
        if view is None:
            self.currentPlot = None
            self.enablePlotActions(False)
        else:
            self.currentPlot = view.plot
            try:
                self.viewStack.remove(view)
            except ValueError:
                newView = True
            self.viewStack.append(view)

            self.enablePlotActions(True)
            self.enableAutoScaleActions(view.plot.HAS_AUTOSCALE)
            self.viewList.setCurrentItem(view.listitem)
            self.actionLogScale.setChecked(view.plot.isLogScaling())
            self.actionLegend.setChecked(view.plot.isLegendEnabled())
            self.actionSymbols.setChecked(view.plot.hasSymbols)
            self.actionLines.setChecked(view.plot.hasLines)
            self.plotLayout.addWidget(view.plot)
            if view.plot.HAS_AUTOSCALE:
                from gr.pygr import PlotAxes
                if newView:
                    mask = PlotAxes.SCALE_X | PlotAxes.SCALE_Y
                else:
                    mask = view.plot.plot.autoscale
                if view.yfrom and view.yto:
                    mask &= ~PlotAxes.SCALE_Y
                self._autoscale(x=mask & PlotAxes.SCALE_X,
                                y=mask & PlotAxes.SCALE_Y)
                view.plot.logYinDomain.connect(self.on_logYinDomain)
            view.plot.setSlidingWindow(view.window)
            view.plot.show()

    def on__viewList_itemDoubleClicked(self, item):
        if item:
            self.on__actionEditView_triggered()

    def on__actionEditView_triggered(self):
        view = self.viewStack[-1]
        newdlg = NewViewDialog(self, view.dlginfo, client=self.client)
        newdlg.setWindowTitle('Edit history view')
        ret = newdlg.exec_()
        if ret != QDialog.Accepted:
            return
        info = newdlg.infoDict()
        if newdlg.savePreset.isChecked():
            self._add_preset(info['name'], info)
        self.viewStack.pop()
        row = self.clearView(view)
        new_view = self._createViewFromDialog(info, row)
        if new_view.plot.HAS_AUTOSCALE:
            self._autoscale(True, False)

    def on__actionCloseView_triggered(self):
        view = self.viewStack.pop()
        if self.viewStack:
            self.setCurrentView(self.viewStack[-1])
        else:
            self.setCurrentView(None)
        view.plot = None

    def on__actionResetView_triggered(self):
        view = self.viewStack.pop()
        hassym = view.plot.hasSymbols
        view.plot = None
        self.openView(view)
        self.actionSymbols.setChecked(hassym)
        view.plot.setSymbols(hassym)

    def on__actionDeleteView_triggered(self):
        view = self.viewStack.pop()
        self.clearView(view)
        if self.viewStack:
            self.setCurrentView(self.viewStack[-1])
        else:
            self.setCurrentView(None)

    def clearView(self, view):
        self.views.remove(view)
        row = self.viewList.row(view.listitem)
        self.viewList.takeItem(row)
        if view.totime is None:
            for key in view.uniq_keys:
                self.keyviews[key].remove(view)
        view.cleanup()
        return row

    def on__actionSavePlot_triggered(self):
        filename = self.currentPlot.savePlot()
        if filename:
            self.statusBar.showMessage('View successfully saved to %s.' %
                                       filename)

    def on__actionPrint_triggered(self):
        if self.currentPlot.printPlot():
            self.statusBar.showMessage('View successfully printed.')

    def on__actionUnzoom_triggered(self):
        self.currentPlot.unzoom()

    def on__actionLogScale_toggled(self, on):
        self.currentPlot.setLogScale(on)

    def on__actionAutoScale_toggled(self, on):
        self._autoscale(on, on)

    def on__actionScaleX_toggled(self, on):
        self._autoscale(x=on)

    def on__actionScaleY_toggled(self, on):
        self._autoscale(y=on)

    def on__actionLegend_toggled(self, on):
        self.currentPlot.setLegend(on)

    def on__actionSymbols_toggled(self, on):
        self.currentPlot.setSymbols(on)

    def on__actionLines_toggled(self, on):
        self.currentPlot.setLines(on)

    def on__actionSaveData_triggered(self):
        self.currentPlot.saveData()

    def on__actionFitPeak_triggered(self):
        self.currentPlot.beginFit(self.fitclass, self.actionFitPeak,
                                  self.fitPickCheckbox.isChecked())

    def on__actionFitLinear_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitLinear.text().replace(
            '&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = LinearFitter

    def on__actionFitExponential_triggered(self):
        cbi = self.fitComboBox.findText(
            self.actionFitExponential.text().replace('&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = ExponentialFitter

    def on__actionFitPeakGaussian_triggered(self):
        cbi = self.fitComboBox.findText(
            self.actionFitPeakGaussian.text().replace('&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = GaussFitter

    def on__actionFitPeakLorentzian_triggered(self):
        cbi = self.fitComboBox.findText(
            self.actionFitPeakLorentzian.text().replace('&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = LorentzFitter

    def on__actionFitPeakPV_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitPeakPV.text().replace(
            '&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = PseudoVoigtFitter

    def on__actionFitPeakPVII_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitPeakPVII.text().replace(
            '&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = PearsonVIIFitter

    def on__actionFitTc_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitTc.text().replace(
            '&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = TcFitter

    def on__actionFitCosine_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitCosine.text().replace(
            '&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = CosineFitter

    def on__actionFitSigmoid_triggered(self):
        cbi = self.fitComboBox.findText(self.actionFitSigmoid.text().replace(
            '&', ''))
        self.fitComboBox.setCurrentIndex(cbi)
        self.fitclass = SigmoidFitter

    def on__actionFitArby_triggered(self):
        # no second argument: the "arbitrary" action is not checkable
        self.currentPlot.beginFit(ArbitraryFitter, None,
                                  self.fitPickCheckbox.isChecked())
Пример #19
0
class AliasWidget(QFrame):
    def __init__(self, parent, name, selections, preselect):
        QFrame.__init__(self, parent)
        self.name = name
        self.selections = selections
        layout = QHBoxLayout()
        layout.addWidget(QLabel(name, self))
        self.combo = QComboBox(self)
        self.combo.addItems(selections)
        if preselect in selections:
            self.combo.setCurrentIndex(selections.index(preselect))
        else:
            self.combo.setCurrentIndex(0)
        layout.addWidget(self.combo)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

    def setSelections(self, selections, preselect):
        if selections != self.selections:
            self.selections = selections
            self.combo.clear()
            self.combo.addItems(selections)
            if preselect in selections:
                self.combo.setCurrentIndex(selections.index(preselect))
            else:
                self.combo.setCurrentIndex(0)

    def getSelection(self):
        return self.combo.currentText()
Пример #20
0
class ToftofProfileWindow(DlgUtils, QMainWindow):
    def __init__(self, parent):
        QMainWindow.__init__(self, parent)
        DlgUtils.__init__(self, 'Live data')
        self.panel = parent
        layout1 = QVBoxLayout()
        self.plot = QwtPlot(self)
        layout1.addWidget(self.plot)
        self.curve = QwtPlotCurve()
        self.curve.setRenderHint(QwtPlotCurve.RenderAntialiased)
        self.curve.attach(self.plot)
        self.marker = QwtPlotMarker()
        self.marker.attach(self.plot)
        self.markerpen = QPen(Qt.red)
        self.marker.setSymbol(
            QwtSymbol(QwtSymbol.Ellipse, QBrush(), self.markerpen, QSize(7,
                                                                         7)))
        self.zoomer = QwtPlotZoomer(self.plot.canvas())
        self.zoomer.setMousePattern(QwtPlotZoomer.MouseSelect3, Qt.NoButton)
        self.picker = QwtPlotPicker(self.plot.canvas())
        self.picker.setSelectionFlags(QwtPlotPicker.PointSelection
                                      | QwtPlotPicker.ClickSelection)
        self.picker.setMousePattern(QwtPlotPicker.MouseSelect1, Qt.MidButton)
        self.picker.selected.connect(self.pickerSelected)
        layout2 = QHBoxLayout()
        layout2.addWidget(QLabel('Scale:', self))
        self.scale = QComboBox(self)
        self.scale.addItems([
            'Single detectors, sorted by angle',
            'Scattering angle 2theta (deg)', 'Q value (A-1)'
        ])
        self.scale.currentIndexChanged[int].connect(self.scaleChanged)
        layout2.addWidget(self.scale)
        layout2.addStretch()
        self.scaleframe = QFrame(self)
        self.scaleframe.setLayout(layout2)
        self.scaleframe.setVisible(False)
        layout1.addWidget(self.scaleframe)
        mainframe = QFrame(self)
        mainframe.setLayout(layout1)
        self.setCentralWidget(mainframe)
        self.setContentsMargins(6, 6, 6, 6)
        plotfont = scaledFont(self.font(), 0.7)
        self.plot.setAxisFont(QwtPlot.xBottom, plotfont)
        self.plot.setAxisFont(QwtPlot.yLeft, plotfont)
        self.plot.setCanvasBackground(Qt.white)
        self.resize(800, 200)

        self._detinfo = None
        self._anglemap = None
        self._infowindow = None
        self._infolabel = None
        self._xs = self._ys = None
        self._type = None

    def _retrieve_detinfo(self):
        if self._detinfo is None:
            info = self.panel.client.eval(
                'det._detinfo_parsed, '
                'det._anglemap', None)
            if not info:
                return self.showError('Cannot retrieve detector info.')
            self._lambda = self.panel.client.eval('chWL()', None)
            if not self._lambda:
                return self.showError('Cannot retrieve wavelength.')
            self._detinfo, self._anglemap = info
            self._inverse_anglemap = 0
            self._infowindow = QMainWindow(self)
            self._infolabel = QLabel(self._infowindow)
            self._infolabel.setTextFormat(Qt.RichText)
            self._infowindow.setCentralWidget(self._infolabel)
            self._infowindow.setContentsMargins(10, 10, 10, 10)
            self._inv_anglemap = [[
                entry for entry in self._detinfo[1:]
                if entry[12] == self._anglemap[detnr] + 1
            ][0] for detnr in range(len(self._xs))]

    def scaleChanged(self, scale):
        self.update(self._type, self._orig_nbins, self._orig_x, self._orig_y)

    def update(self, proftype, nbins, x, y):
        self._orig_x = x
        self._orig_y = y
        self._orig_nbins = nbins
        x.setsize(8 * nbins)
        y.setsize(8 * nbins)
        xs = struct.unpack('d' * nbins, x)
        ys = struct.unpack('d' * nbins, y)
        if proftype == 0:
            if self.scale.currentIndex() == 0:
                xs = xs
            elif self.scale.currentIndex() == 1:
                self._retrieve_detinfo()
                xs = [self._inv_anglemap[int(xi)][5] for xi in xs]
            else:
                self._retrieve_detinfo()
                if self._lambda is None:
                    self.showError('Could not determine wavelength.')
                    self.scale.setCurrentIndex(1)
                    return
                xs = [
                    4 * pi / self._lambda *
                    sin(radians(self._inv_anglemap[int(xi)][5] / 2.))
                    for xi in xs
                ]
        self._xs = xs
        self._ys = ys
        self.curve.setData(xs, ys)
        self.plot.setAxisAutoScale(QwtPlot.xBottom)
        self.plot.setAxisAutoScale(QwtPlot.yLeft)
        self.marker.setVisible(False)
        self.zoomer.setZoomBase(True)
        self._type = proftype
        if proftype == 0:
            self.setWindowTitle(
                'Single detector view (time-channel integrated)')
            self.scaleframe.setVisible(True)
        elif proftype == 1:
            self.setWindowTitle('Time channel view (detector integrated)')
            self.scaleframe.setVisible(False)
        else:
            self.scaleframe.setVisible(False)

    def pickerSelected(self, point):
        if self._type != 0:
            return
        self._retrieve_detinfo()
        index = self.curve.closestPoint(self.picker.transform(point))[0]
        detentry = self._inv_anglemap[index][:]
        detentry.append(self._xs[index])
        detentry.append(self._ys[index])
        self.marker.setXValue(self._xs[index])
        self.marker.setYValue(self._ys[index])
        self.marker.setVisible(True)
        self.plot.replot()
        self._infowindow.show()
        entrynames = [
            'EntryNr', 'Rack', 'Plate', 'Pos', 'RPos', '2Theta', 'CableNr',
            'CableType', 'CableLen', 'CableEmpty', 'Card', 'Chan', 'Total',
            'DetName', 'BoxNr', 'BoxChan', 'XValue', 'Counts'
        ]
        formats = [
            '%s', '%d', '%d', '%d', '%d', '%.3f', '%d', '%d', '%.2f', '%d',
            '%d', '%d', '%d', '%r', '%d', '%d', '%s', '%d'
        ]
        empties = [1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0]
        self._infolabel.setText('Detector info:<br><table>' + ''.join(
            '<tr><td>%s</td><td></td><td>%s</td></tr>%s' %
            (name, format % value, '<tr></tr>' if empty else '')
            for (name, format, empty,
                 value) in zip(entrynames, formats, empties, detentry)) +
                                '</table>')

    def closeEvent(self, event):
        if self._infowindow:
            self._infowindow.close()
Пример #21
0
class TimeEditWidget(QWidget):
    units = ('s', 'm', 'h', 'd')

    returnPressed = pyqtSignal()

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setLayout(QHBoxLayout())
        self.layout().setContentsMargins(0.1, 0.1, 0.1, 0.1)
        self.layout().setSpacing(1)
        self.val = QLineEdit()
        self.val.setValidator(QDoubleValidator(0, 1000000, 2))
        self.layout().addWidget(self.val)
        self.unit = QComboBox()
        self.unit.insertItems(0, self.units)
        self.layout().addWidget(self.unit)
        self.setValue(1)
        self.val.returnPressed.connect(self.on_returnPressed)
        self.val.editingFinished.connect(self.on_returnPressed)
        self.unit.currentIndexChanged.connect(self.recalcValue)

    def value(self):
        val = float(self.val.text())
        current_unit_index = self.unit.currentIndex()
        if current_unit_index == 1:  # minutes
            val *= 60.
        elif current_unit_index == 2:  # hours
            val *= 3600.
        elif current_unit_index == 3:  # days
            val *= 86400.
        return int(val)

    def setValue(self, val):
        fmt = '%%.%df' % self.val.validator().decimals()
        self.val.setText(fmt % float(val))
        self.unit.setCurrentIndex(0)
        self.currentUnit = 0

    def recalcValue(self, idx):
        # adjust widget value to unit
        unit_index_current = self.currentUnit
        unit_index_next = idx
        if unit_index_next > unit_index_current:
            start = unit_index_current
            end = unit_index_next
        elif unit_index_next < unit_index_current:
            start = unit_index_next
            end = unit_index_current
        val = float(self.val.text())
        factor = 1.
        for i in range(start, end):
            if self.units[i + 1] == 'd':
                factor *= 24.
            else:
                factor *= 60.
        if unit_index_next - unit_index_current > 0:
            factor = 1. / factor
        next_value = val * factor
        self.val.setText('%s' % next_value)
        self.currentUnit = idx

    def on_returnPressed(self):
        self.returnPressed.emit()
Пример #22
0
 def mousePressEvent(self, e):
     if e.button() == Qt.RightButton:
         if self.parent():
             self.parent().mousePressEvent(e)
     else:
         QComboBox.mousePressEvent(self, e)
Пример #23
0
class ScansPanel(DefaultScansPanel):
    def __init__(self, parent, client, options):
        DefaultScansPanel.__init__(self, parent, client, options)
        self.bars = self.createPanelToolbar()
        for index, bar in enumerate(self.bars):
            self.layout().insertWidget(index, bar)
        self.set_icons()

    def set_icons(self):
        self.actionPrint.setIcon(get_icon('print-24px.svg'))
        self.actionSavePlot.setIcon(get_icon('save-24px.svg'))
        self.actionUnzoom.setIcon(get_icon('zoom_out-24px.svg'))
        self.actionClose.setIcon(get_icon('zoom_out-24px.svg'))

    def createPanelToolbar(self):
        bar = QToolBar('Scans')
        bar.addAction(self.actionSavePlot)
        bar.addAction(self.actionPrint)
        bar.addSeparator()
        bar.addAction(self.actionXAxis)
        bar.addAction(self.actionYAxis)
        bar.addAction(self.actionNormalized)
        bar.addSeparator()
        bar.addAction(self.actionLogXScale)
        bar.addAction(self.actionLogScale)
        bar.addAction(self.actionUnzoom)
        bar.addSeparator()
        bar.addAction(self.actionAutoScale)
        bar.addAction(self.actionScaleX)
        bar.addAction(self.actionScaleY)
        bar.addAction(self.actionLegend)
        bar.addAction(self.actionErrors)
        bar.addAction(self.actionResetPlot)
        bar.addAction(self.actionDeletePlot)
        bar.addSeparator()
        bar.addAction(self.actionAutoDisplay)
        bar.addAction(self.actionCombine)

        fitbar = QToolBar('Scan fitting')
        fitbar.addAction(self.actionFitPeak)
        wa = QWidgetAction(fitbar)
        self.fitPickCheckbox = QCheckBox(fitbar)
        self.fitPickCheckbox.setText('Pick')
        self.fitPickCheckbox.setChecked(True)
        self.actionPickInitial.setChecked(True)
        self.fitPickCheckbox.toggled.connect(self.actionPickInitial.setChecked)
        self.actionPickInitial.toggled.connect(self.fitPickCheckbox.setChecked)
        layout = QHBoxLayout()
        layout.setContentsMargins(10, 0, 10, 0)
        layout.addWidget(self.fitPickCheckbox)
        frame = QFrame(fitbar)
        frame.setLayout(layout)
        wa.setDefaultWidget(frame)
        fitbar.addAction(wa)
        ag = QActionGroup(fitbar)
        ag.addAction(self.actionFitPeakGaussian)
        ag.addAction(self.actionFitPeakLorentzian)
        ag.addAction(self.actionFitPeakPV)
        ag.addAction(self.actionFitPeakPVII)
        ag.addAction(self.actionFitTc)
        ag.addAction(self.actionFitCosine)
        ag.addAction(self.actionFitSigmoid)
        ag.addAction(self.actionFitLinear)
        ag.addAction(self.actionFitExponential)
        wa = QWidgetAction(fitbar)
        self.fitComboBox = QComboBox(fitbar)
        for a in ag.actions():
            itemtext = a.text().replace('&', '')
            self.fitComboBox.addItem(itemtext)
            self.fitfuncmap[itemtext] = a
        self.fitComboBox.currentIndexChanged.connect(
            self.on_fitComboBox_currentIndexChanged)
        wa.setDefaultWidget(self.fitComboBox)
        fitbar.addAction(wa)
        fitbar.addSeparator()
        fitbar.addAction(self.actionFitArby)

        bars = [bar, fitbar]

        return bars

    def getToolbars(self):
        return []
Пример #24
0
 def createWidget(self, parent, client):
     self.clientUpdate(client)
     self._widget = QComboBox(parent)
     self._updateWidget()
     self._widget.currentIndexChanged.connect(self._updateValue)
     return self._widget
Пример #25
0
    def _reinit(self):
        classes = self.devinfo.classes

        if sip.isdeleted(self.devitem):
            # The item we're controlling has been removed from the list (e.g.
            # due to client reconnect), get it again.
            self.devitem = self.device_panel._devitems.get(
                self.devname.lower())
            # No such device anymore...
            if self.devitem is None:
                self.close()
                return

        self.deviceName.setText('Device: %s' % self.devname)
        self.setWindowTitle('Control %s' % self.devname)

        self.settingsBtn = self.buttonBox.button(
            QDialogButtonBox.RestoreDefaults)
        self.settingsBtn.clicked.connect(self.on_settingsBtn_clicked)

        # trigger parameter poll
        self.client.eval('%s.pollParams()' % self.devname, None)

        # now get all cache keys pertaining to the device and set the
        # properties we want
        params = self.client.getDeviceParams(self.devname)
        self.paraminfo = self.client.getDeviceParamInfo(self.devname)
        self.paramvalues = dict(params)

        # put parameter values in the list widget
        self.paramItems.clear()
        self.paramList.clear()
        for key, value in sorted(iteritems(params)):
            if self.paraminfo.get(key):
                # normally, show only userparams, except in expert mode
                is_userparam = self.paraminfo[key]['userparam']
                if is_userparam or self.device_panel._show_lowlevel:
                    self.paramItems[key] = item = \
                        QTreeWidgetItem(self.paramList, [key, str(value)])
                    # display non-userparams in grey italics, like lowlevel
                    # devices in the device list
                    if not is_userparam:
                        item.setFont(0, lowlevelFont[True])
                        item.setForeground(0, lowlevelBrush[True])

        # set description label
        if params.get('description'):
            self.description.setText(params['description'])
        else:
            self.description.setVisible(False)

        # check how to refer to the device in commands: if it is lowlevel,
        # we need to use quotes
        self.devrepr = srepr(self.devname) if params.get('lowlevel', True) \
            else self.devname

        # show "Set alias" group box if it is an alias device
        if 'alias' in params:
            if params['alias']:
                self.deviceName.setText(self.deviceName.text() +
                                        ' (alias for %s)' % params['alias'])
            alias_config = self.client.eval('session.alias_config', {})
            self.aliasTarget = QComboBox(self)
            self.aliasTarget.setEditable(True)
            if self.devname in alias_config:
                items = [t[0] for t in alias_config[self.devname]]
                self.aliasTarget.addItems(items)
                if params['alias'] in items:
                    self.aliasTarget.setCurrentIndex(
                        items.index(params['alias']))
            self.targetLayoutAlias.takeAt(1).widget().deleteLater()
            self.targetLayoutAlias.insertWidget(1, self.aliasTarget)
            if self.client.viewonly:
                self.setAliasBtn.setEnabled(False)
        else:
            self.aliasGroup.setVisible(False)

        historyBtn = self.buttonBox.button(QDialogButtonBox.Reset)
        # show current value/status if it is readable
        if 'nicos.core.device.Readable' not in classes:
            self.valueFrame.setVisible(False)
            self.buttonBox.removeButton(historyBtn)
        else:
            self.valuelabel.setText(self.devitem.text(1))
            self.statuslabel.setText(self.devitem.text(2))
            self.statusimage.setPixmap(self.devitem.icon(0).pixmap(16, 16))
            setForegroundBrush(self.statuslabel, self.devitem.foreground(2))
            setBackgroundBrush(self.statuslabel, self.devitem.background(2))

            # modify history button: add icon and set text
            historyBtn.setIcon(QIcon(':/find'))
            historyBtn.setText('Plot history...')
            historyBtn.clicked.connect(self.on_historyBtn_clicked)

        if self.client.viewonly:
            self.limitFrame.setVisible(False)
            self.targetFrame.setVisible(False)
            return

        # add a menu for the "More" button
        self.moveBtns.clear()
        menu = QMenu(self)
        if 'nicos.core.mixins.HasLimits' in classes:
            menu.addAction(self.actionSetLimits)
        if 'nicos.core.mixins.HasOffset' in classes:
            menu.addAction(self.actionAdjustOffset)
        if 'nicos.devices.abstract.CanReference' in classes:
            menu.addAction(self.actionReference)
        if 'nicos.devices.abstract.Coder' in classes:
            menu.addAction(self.actionSetPosition)
        if 'nicos.core.device.Moveable' in classes:
            if not menu.isEmpty():
                menu.addSeparator()
            menu.addAction(self.actionFix)
            menu.addAction(self.actionRelease)
        if 'nicos.core.mixins.CanDisable' in classes:
            if not menu.isEmpty():
                menu.addSeparator()
            menu.addAction(self.actionEnable)
            menu.addAction(self.actionDisable)
        if not menu.isEmpty():
            menuBtn = QPushButton('More', self)
            menuBtn.setMenu(menu)
            self.moveBtns.addButton(menuBtn, QDialogButtonBox.ResetRole)

        def reset(checked):
            self.device_panel.exec_command('reset(%s)' % self.devrepr)

        def stop(checked):
            self.device_panel.exec_command('stop(%s)' % self.devrepr,
                                           immediate=True)

        self.moveBtns.addButton('Reset', QDialogButtonBox.ResetRole)\
                     .clicked.connect(reset)

        if 'nicos.core.device.Moveable' in classes or \
           'nicos.core.device.Measurable' in classes:
            self.moveBtns.addButton('Stop', QDialogButtonBox.ResetRole)\
                         .clicked.connect(stop)

        # show target and limits if the device is Moveable
        if 'nicos.core.device.Moveable' not in classes:
            self.limitFrame.setVisible(False)
            self.targetFrame.setVisible(False)
        else:
            if 'nicos.core.mixins.HasLimits' not in classes:
                self.limitFrame.setVisible(False)
            else:
                self.limitMin.setText(str(params['userlimits'][0]))
                self.limitMax.setText(str(params['userlimits'][1]))

            # insert a widget to enter a new device value
            # allowEnter=False because we catch pressing Enter ourselves
            self.target = DeviceValueEdit(self,
                                          dev=self.devname,
                                          useButtons=True,
                                          allowEnter=False)
            self.target.setClient(self.client)

            def btn_callback(target):
                self.device_panel.exec_command('move(%s, %s)' %
                                               (self.devrepr, srepr(target)))

            self.target.valueChosen.connect(btn_callback)
            self.targetFrame.layout().takeAt(1).widget().deleteLater()
            self.targetFrame.layout().insertWidget(1, self.target)

            def move(checked):
                try:
                    target = self.target.getValue()
                except ValueError:
                    return
                self.device_panel.exec_command('move(%s, %s)' %
                                               (self.devrepr, srepr(target)))

            if self.target.getValue() is not Ellipsis:  # (button widget)
                self.moveBtn = self.moveBtns.addButton(
                    'Move', QDialogButtonBox.AcceptRole)
                self.moveBtn.clicked.connect(move)
            else:
                self.moveBtn = None

            if params.get('fixed') and self.moveBtn:
                self.moveBtn.setEnabled(False)
                self.moveBtn.setText('(fixed)')
Пример #26
0
class ControlDialog(QDialog):
    """Dialog opened to control and view details for one device."""

    closed = pyqtSignal(object)

    def __init__(self, parent, devname, devinfo, devitem, log, expert):
        QDialog.__init__(self, parent)
        loadUi(self, 'panels/devices_one.ui')
        self.log = log

        self.device_panel = parent
        self.client = parent.client
        self.devname = devname
        self.devinfo = devinfo
        self.devitem = devitem
        self.paramItems = {}
        self.moveBtn = None
        self.target = None

        self._reinit()
        self._show_extension(expert)

        if self.target:
            self.target.setFocus()

    def _reinit(self):
        classes = self.devinfo.classes

        if sip.isdeleted(self.devitem):
            # The item we're controlling has been removed from the list (e.g.
            # due to client reconnect), get it again.
            self.devitem = self.device_panel._devitems.get(
                self.devname.lower())
            # No such device anymore...
            if self.devitem is None:
                self.close()
                return

        self.deviceName.setText('Device: %s' % self.devname)
        self.setWindowTitle('Control %s' % self.devname)

        self.settingsBtn = self.buttonBox.button(
            QDialogButtonBox.RestoreDefaults)
        self.settingsBtn.clicked.connect(self.on_settingsBtn_clicked)

        # trigger parameter poll
        self.client.eval('%s.pollParams()' % self.devname, None)

        # now get all cache keys pertaining to the device and set the
        # properties we want
        params = self.client.getDeviceParams(self.devname)
        self.paraminfo = self.client.getDeviceParamInfo(self.devname)
        self.paramvalues = dict(params)

        # put parameter values in the list widget
        self.paramItems.clear()
        self.paramList.clear()
        for key, value in sorted(iteritems(params)):
            if self.paraminfo.get(key):
                # normally, show only userparams, except in expert mode
                is_userparam = self.paraminfo[key]['userparam']
                if is_userparam or self.device_panel._show_lowlevel:
                    self.paramItems[key] = item = \
                        QTreeWidgetItem(self.paramList, [key, str(value)])
                    # display non-userparams in grey italics, like lowlevel
                    # devices in the device list
                    if not is_userparam:
                        item.setFont(0, lowlevelFont[True])
                        item.setForeground(0, lowlevelBrush[True])

        # set description label
        if params.get('description'):
            self.description.setText(params['description'])
        else:
            self.description.setVisible(False)

        # check how to refer to the device in commands: if it is lowlevel,
        # we need to use quotes
        self.devrepr = srepr(self.devname) if params.get('lowlevel', True) \
            else self.devname

        # show "Set alias" group box if it is an alias device
        if 'alias' in params:
            if params['alias']:
                self.deviceName.setText(self.deviceName.text() +
                                        ' (alias for %s)' % params['alias'])
            alias_config = self.client.eval('session.alias_config', {})
            self.aliasTarget = QComboBox(self)
            self.aliasTarget.setEditable(True)
            if self.devname in alias_config:
                items = [t[0] for t in alias_config[self.devname]]
                self.aliasTarget.addItems(items)
                if params['alias'] in items:
                    self.aliasTarget.setCurrentIndex(
                        items.index(params['alias']))
            self.targetLayoutAlias.takeAt(1).widget().deleteLater()
            self.targetLayoutAlias.insertWidget(1, self.aliasTarget)
            if self.client.viewonly:
                self.setAliasBtn.setEnabled(False)
        else:
            self.aliasGroup.setVisible(False)

        historyBtn = self.buttonBox.button(QDialogButtonBox.Reset)
        # show current value/status if it is readable
        if 'nicos.core.device.Readable' not in classes:
            self.valueFrame.setVisible(False)
            self.buttonBox.removeButton(historyBtn)
        else:
            self.valuelabel.setText(self.devitem.text(1))
            self.statuslabel.setText(self.devitem.text(2))
            self.statusimage.setPixmap(self.devitem.icon(0).pixmap(16, 16))
            setForegroundBrush(self.statuslabel, self.devitem.foreground(2))
            setBackgroundBrush(self.statuslabel, self.devitem.background(2))

            # modify history button: add icon and set text
            historyBtn.setIcon(QIcon(':/find'))
            historyBtn.setText('Plot history...')
            historyBtn.clicked.connect(self.on_historyBtn_clicked)

        if self.client.viewonly:
            self.limitFrame.setVisible(False)
            self.targetFrame.setVisible(False)
            return

        # add a menu for the "More" button
        self.moveBtns.clear()
        menu = QMenu(self)
        if 'nicos.core.mixins.HasLimits' in classes:
            menu.addAction(self.actionSetLimits)
        if 'nicos.core.mixins.HasOffset' in classes:
            menu.addAction(self.actionAdjustOffset)
        if 'nicos.devices.abstract.CanReference' in classes:
            menu.addAction(self.actionReference)
        if 'nicos.devices.abstract.Coder' in classes:
            menu.addAction(self.actionSetPosition)
        if 'nicos.core.device.Moveable' in classes:
            if not menu.isEmpty():
                menu.addSeparator()
            menu.addAction(self.actionFix)
            menu.addAction(self.actionRelease)
        if 'nicos.core.mixins.CanDisable' in classes:
            if not menu.isEmpty():
                menu.addSeparator()
            menu.addAction(self.actionEnable)
            menu.addAction(self.actionDisable)
        if not menu.isEmpty():
            menuBtn = QPushButton('More', self)
            menuBtn.setMenu(menu)
            self.moveBtns.addButton(menuBtn, QDialogButtonBox.ResetRole)

        def reset(checked):
            self.device_panel.exec_command('reset(%s)' % self.devrepr)

        def stop(checked):
            self.device_panel.exec_command('stop(%s)' % self.devrepr,
                                           immediate=True)

        self.moveBtns.addButton('Reset', QDialogButtonBox.ResetRole)\
                     .clicked.connect(reset)

        if 'nicos.core.device.Moveable' in classes or \
           'nicos.core.device.Measurable' in classes:
            self.moveBtns.addButton('Stop', QDialogButtonBox.ResetRole)\
                         .clicked.connect(stop)

        # show target and limits if the device is Moveable
        if 'nicos.core.device.Moveable' not in classes:
            self.limitFrame.setVisible(False)
            self.targetFrame.setVisible(False)
        else:
            if 'nicos.core.mixins.HasLimits' not in classes:
                self.limitFrame.setVisible(False)
            else:
                self.limitMin.setText(str(params['userlimits'][0]))
                self.limitMax.setText(str(params['userlimits'][1]))

            # insert a widget to enter a new device value
            # allowEnter=False because we catch pressing Enter ourselves
            self.target = DeviceValueEdit(self,
                                          dev=self.devname,
                                          useButtons=True,
                                          allowEnter=False)
            self.target.setClient(self.client)

            def btn_callback(target):
                self.device_panel.exec_command('move(%s, %s)' %
                                               (self.devrepr, srepr(target)))

            self.target.valueChosen.connect(btn_callback)
            self.targetFrame.layout().takeAt(1).widget().deleteLater()
            self.targetFrame.layout().insertWidget(1, self.target)

            def move(checked):
                try:
                    target = self.target.getValue()
                except ValueError:
                    return
                self.device_panel.exec_command('move(%s, %s)' %
                                               (self.devrepr, srepr(target)))

            if self.target.getValue() is not Ellipsis:  # (button widget)
                self.moveBtn = self.moveBtns.addButton(
                    'Move', QDialogButtonBox.AcceptRole)
                self.moveBtn.clicked.connect(move)
            else:
                self.moveBtn = None

            if params.get('fixed') and self.moveBtn:
                self.moveBtn.setEnabled(False)
                self.moveBtn.setText('(fixed)')

    def on_paramList_customContextMenuRequested(self, pos):
        item = self.paramList.itemAt(pos)
        if not item:
            return

        menu = QMenu(self)
        refreshAction = menu.addAction('Refresh')
        menu.addAction('Refresh all')

        # QCursor.pos is more reliable then the given pos
        action = menu.exec_(QCursor.pos())

        if action:
            cmd = 'session.getDevice(%r).pollParams(volatile_only=False%s)' \
                  % (self.devname, ', param_list=[%r]' % item.text(0)
                     if action == refreshAction else '')
            # poll even non volatile parameter as requested explicitely
            self.client.eval(cmd, None)

    @pyqtSlot()
    def on_settingsBtn_clicked(self):
        self._show_extension(self.extension.isHidden())

    def _show_extension(self, show):
        if show:
            # make "settings shown" permanent
            self.settingsBtn.hide()
        self.extension.setVisible(show)
        self.settingsBtn.setText('Settings %s' % ('<<<' if show else '>>>'))
        sz = self.size()
        sz.setHeight(self.sizeHint().height())
        self.resize(sz)

    @pyqtSlot()
    def on_actionSetLimits_triggered(self):
        dlg = dialogFromUi(self, 'panels/devices_limits.ui')
        dlg.descLabel.setText('Adjust user limits of %s:' % self.devname)
        dlg.limitMin.setText(self.limitMin.text())
        dlg.limitMax.setText(self.limitMax.text())
        abslimits = self.client.getDeviceParam(self.devname, 'abslimits')
        offset = self.client.getDeviceParam(self.devname, 'offset')
        if offset is not None:
            abslimits = abslimits[0] - offset, abslimits[1] - offset
        dlg.limitMinAbs.setText(str(abslimits[0]))
        dlg.limitMaxAbs.setText(str(abslimits[1]))
        target = DeviceParamEdit(dlg, dev=self.devname, param='userlimits')
        target.setClient(self.client)
        btn = dlg.buttonBox.addButton('Reset to maximum range',
                                      QDialogButtonBox.ResetRole)

        def callback():
            self.device_panel.exec_command('resetlimits(%s)' % self.devrepr)
            dlg.reject()

        btn.clicked.connect(callback)
        dlg.targetLayout.addWidget(target)
        res = dlg.exec_()
        if res != QDialog.Accepted:
            return
        newlimits = target.getValue()
        if newlimits[0] < abslimits[0] or newlimits[1] > abslimits[1]:
            QMessageBox.warning(
                self, 'Error', 'The entered limits are not '
                'within the absolute limits for the device.')
            # retry
            self.on_actionSetLimits_triggered()
            return
        self.device_panel.exec_command('set(%s, "userlimits", %s)' %
                                       (self.devrepr, newlimits))

    def _get_new_value(self, window_title, desc):
        dlg = dialogFromUi(self, 'panels/devices_newpos.ui')
        dlg.setWindowTitle(window_title)
        dlg.descLabel.setText(desc)
        dlg.oldValue.setText(self.valuelabel.text())
        target = DeviceValueEdit(dlg, dev=self.devname)
        target.setClient(self.client)
        dlg.targetLayout.addWidget(target)
        target.setFocus()
        res = dlg.exec_()
        if res != QDialog.Accepted:
            return None
        return target.getValue()

    @pyqtSlot()
    def on_actionAdjustOffset_triggered(self):
        val = self._get_new_value('Adjust NICOS offset',
                                  'Adjust NICOS offset of %s:' % self.devname)
        if val is not None:
            self.device_panel.exec_command('adjust(%s, %r)' %
                                           (self.devrepr, val))

    @pyqtSlot()
    def on_actionSetPosition_triggered(self):
        val = self._get_new_value(
            'Set hardware position',
            'Set hardware position of %s:' % self.devname)
        if val is not None:
            if self.devrepr != self.devname:
                cmd = 'CreateDevice(%s); %s.setPosition(%r)' % \
                      (self.devrepr, self.devname, val)
            else:
                cmd = '%s.setPosition(%r)' % (self.devname, val)
            self.device_panel.exec_command(cmd)

    @pyqtSlot()
    def on_actionReference_triggered(self):
        self.device_panel.exec_command('reference(%s)' % self.devrepr)

    @pyqtSlot()
    def on_actionFix_triggered(self):
        reason, ok = QInputDialog.getText(
            self, 'Fix',
            'Please enter the reason for fixing %s:' % self.devname)
        if not ok:
            return
        self.device_panel.exec_command('fix(%s, %r)' % (self.devrepr, reason))

    @pyqtSlot()
    def on_actionRelease_triggered(self):
        self.device_panel.exec_command('release(%s)' % self.devrepr)

    @pyqtSlot()
    def on_actionEnable_triggered(self):
        self.device_panel.exec_command('enable(%s)' % self.devrepr)

    @pyqtSlot()
    def on_actionDisable_triggered(self):
        self.device_panel.exec_command('disable(%s)' % self.devrepr)

    @pyqtSlot()
    def on_setAliasBtn_clicked(self):
        self.device_panel.exec_command(
            'set(%s, "alias", %s)' %
            (self.devrepr, srepr(self.aliasTarget.currentText())))

    def closeEvent(self, event):
        event.accept()
        self.closed.emit(self.devname.lower())

    def on_cache(self, subkey, value):
        if subkey not in self.paramItems:
            return
        if not value:
            return
        value = cache_load(value)
        self.paramvalues[subkey] = value
        self.paramItems[subkey].setText(1, str(value))

    def on_paramList_itemClicked(self, item):
        pname = item.text(0)
        self.editParam(pname)

    def editParam(self, pname):
        if not self.paraminfo[pname]['settable'] or self.client.viewonly:
            return
        mainunit = self.paramvalues.get('unit', 'main')
        punit = (self.paraminfo[pname]['unit'] or '').replace('main', mainunit)

        dlg = dialogFromUi(self, 'panels/devices_param.ui')
        dlg.target = DeviceParamEdit(self, dev=self.devname, param=pname)
        dlg.target.setClient(self.client)
        dlg.paramName.setText('Parameter: %s.%s' % (self.devname, pname))
        dlg.paramDesc.setText(self.paraminfo[pname]['description'])
        dlg.paramValue.setText(str(self.paramvalues[pname]) + ' ' + punit)
        dlg.targetLayout.addWidget(dlg.target)
        dlg.resize(dlg.sizeHint())
        dlg.target.setFocus()
        if dlg.exec_() != QDialog.Accepted:
            return
        try:
            new_value = dlg.target.getValue()
        except ValueError:
            self.log.exception('invalid value for typed value')
            # shouldn't happen, but if it does, at least give an indication that
            # something went wrong
            QMessageBox.warning(
                self, 'Error', 'The entered value is invalid '
                'for this parameter.')
            return
        if self.devrepr == self.devname:
            self.device_panel.exec_command('%s.%s = %r' %
                                           (self.devname, pname, new_value))
        else:
            self.device_panel.exec_command(
                'set(%s, %s, %r)' % (self.devrepr, srepr(pname), new_value))

    def on_historyBtn_clicked(self):
        self.device_panel.plot_history(self.devname)
Пример #27
0
class HistoryPanel(DefaultHistoryPanel):
    def __init__(self, parent, client, options):
        DefaultHistoryPanel.__init__(self, parent, client, options)
        self.layout().setMenuBar(self.setPanelToolbar())
        self.set_icons()

    def setPanelToolbar(self):
        bar = QToolBar('History viewer')
        bar.addAction(self.actionNew)
        bar.addAction(self.actionEditView)
        bar.addSeparator()
        bar.addAction(self.actionSavePlot)
        bar.addAction(self.actionPrint)
        bar.addAction(self.actionSaveData)
        bar.addSeparator()
        bar.addAction(self.actionUnzoom)
        bar.addAction(self.actionLogScale)
        bar.addSeparator()
        bar.addAction(self.actionAutoScale)
        bar.addAction(self.actionScaleX)
        bar.addAction(self.actionScaleY)
        bar.addSeparator()
        bar.addAction(self.actionResetView)
        bar.addAction(self.actionDeleteView)
        bar.addSeparator()
        bar.addAction(self.actionFitPeak)
        wa = QWidgetAction(bar)
        self.fitPickCheckbox = QCheckBox(bar)
        self.fitPickCheckbox.setText('Pick')
        self.fitPickCheckbox.setChecked(True)
        self.actionPickInitial.setChecked(True)
        self.fitPickCheckbox.toggled.connect(self.actionPickInitial.setChecked)
        self.actionPickInitial.toggled.connect(self.fitPickCheckbox.setChecked)
        layout = QHBoxLayout()
        layout.setContentsMargins(10, 0, 10, 0)
        layout.addWidget(self.fitPickCheckbox)
        frame = QFrame(bar)
        frame.setLayout(layout)
        wa.setDefaultWidget(frame)
        bar.addAction(wa)
        ag = QActionGroup(bar)
        ag.addAction(self.actionFitPeakGaussian)
        ag.addAction(self.actionFitPeakLorentzian)
        ag.addAction(self.actionFitPeakPV)
        ag.addAction(self.actionFitPeakPVII)
        ag.addAction(self.actionFitTc)
        ag.addAction(self.actionFitCosine)
        ag.addAction(self.actionFitSigmoid)
        ag.addAction(self.actionFitLinear)
        ag.addAction(self.actionFitExponential)
        wa = QWidgetAction(bar)
        self.fitComboBox = QComboBox(bar)
        for a in ag.actions():
            itemtext = a.text().replace('&', '')
            self.fitComboBox.addItem(itemtext)
            self.fitfuncmap[itemtext] = a
        self.fitComboBox.currentIndexChanged.connect(
            self.on__fitComboBox_currentIndexChanged)
        wa.setDefaultWidget(self.fitComboBox)
        bar.addAction(wa)
        bar.addSeparator()
        bar.addAction(self.actionFitArby)
        self.bar = bar
        self.actionFitLinear.trigger()
        return bar

    def set_icons(self):
        self.actionNew.setIcon(get_icon('add_circle_outline-24px.svg'))
        self.actionEditView.setIcon(get_icon('edit-24px.svg'))
        self.actionSavePlot.setIcon(get_icon('save-24px.svg'))
        self.actionPrint.setIcon(get_icon('print-24px.svg'))
        self.actionUnzoom.setIcon(get_icon('zoom_out-24px.svg'))
        self.actionSaveData.setIcon(get_icon('archive-24px.svg'))

    def getToolbars(self):
        return []