コード例 #1
0
class JSInterface(QtCore.QObject):
    """
    Interface between python and a webview's javascript.

    All methods decorated with "pyqtSlot" on this class can be called
    from the JS side.
    """

    registered = QtCore.pyqtSignal(str, str)
    visibility = QtCore.pyqtSignal(str, bool)
    rightclicked = QtCore.pyqtSignal(str, str)
    leftclicked = QtCore.pyqtSignal(str, str)
    evaljs = QtCore.pyqtSignal(str)
    lock = Lock()

    def __init__(self, frame, parent=None):

        self.frame = frame

        super(JSInterface, self).__init__(parent)
        self.evaljs.connect(self.evaluate_js)  # thread safety

    def evaluate_js(self, js):
        #print "JS", js
        with self.lock:
            self.frame.evaluateJavaScript(js)

    @QtCore.pyqtSlot(str, str)
    def left_click(self, kind, name):
        self.leftclicked.emit(kind, name)

    @QtCore.pyqtSlot(str, str)
    def right_click(self, kind, name):
        self.rightclicked.emit(kind, name)

    @QtCore.pyqtSlot(str, str)
    def register(self, kind, name):
        "inform the widget about an item"
        self.registered.emit(kind, name)

    def select(self, kind, names):
        "set an item as selected"
        self.evaljs.emit("Synoptic.unselectAll()")
        for name in names:
            self.evaljs.emit("Synoptic.select(%r, %r)" %
                             (str(kind), str(name)))

    @QtCore.pyqtSlot(str, bool)
    def visible(self, name, value=True):
        "Update the visibility of something"
        self.visibility.emit(name, value)

    def load(self, svg, section=None):
        "Load an SVG file"
        if section:
            self.evaljs.emit("Synoptic.load(%r, %r)" % (svg, section))
        else:
            self.evaljs.emit("Synoptic.load(%r)" % svg)
コード例 #2
0
    def __init__(self, parent=None, designMode=False):
        TaurusWidget.__init__(self, parent, designMode=designMode)
        self._setup_ui()

        self._throttle_timer = QtCore.QTimer()
        self._throttle_timer.setInterval(200)
        self._throttle_timer.setSingleShot(True)
        self.connect(self._throttle_timer, QtCore.SIGNAL("timeout()"),
                     self._writeValue)

        self._value = None

        self._acc_value = 0  # accumulate fast wheel events
        self._last_wheel = 0  # time of last wheel event
コード例 #3
0
class StatusArea(TaurusWidget):

    """A (scrolling) text area that displays device status, or any other
    string attribute."""

    statusTrigger = QtCore.pyqtSignal(str)

    def __init__(self, parent=None, down_command=None, up_command=None, state=None):
        TaurusWidget.__init__(self, parent)
        self._setup_ui()

    def _setup_ui(self):

        hbox = QtGui.QVBoxLayout(self)
        self.setLayout(hbox)

        self.status_label = QtGui.QLabel("(No status has been read.)")
        self.status_label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
        self.status_label.setWordWrap(True)
        self.status_label.setAlignment(QtCore.Qt.AlignTop)
        status_scroll_area = QtGui.QScrollArea()
        status_scroll_area.setMaximumSize(QtCore.QSize(100000, 100))
        status_scroll_area.setWidgetResizable(True)
        status_scroll_area.setWidget(self.status_label)
        hbox.addWidget(status_scroll_area)
        self.status = None

        self.statusTrigger.connect(self.updateStatus)

    def setModel(self, model):
        if model:
            split_model = model.split("/")
            if len(split_model) < 4:
                self.status = Attribute("%s/Status" % model)
            else:
                self.status = Attribute(model)
            self.status.addListener(self.onStatusChange)
        else:
            self.status and self.status.removeListener(self.onStatusChange)

    def onStatusChange(self, evt_src, evt_type, evt_value):
        if evt_type in [PyTango.EventType.CHANGE_EVENT,
                        PyTango.EventType.PERIODIC_EVENT]:
            self.statusTrigger.emit(evt_value.value)

    def updateStatus(self, status):
        self.status_label.setText(status)
コード例 #4
0
    def _setup_ui(self):

        hbox = QtGui.QVBoxLayout(self)
        self.setLayout(hbox)

        self.status_label = QtGui.QLabel("(No status has been read.)")
        self.status_label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
        self.status_label.setWordWrap(True)
        self.status_label.setAlignment(QtCore.Qt.AlignTop)
        status_scroll_area = QtGui.QScrollArea()
        status_scroll_area.setMaximumSize(QtCore.QSize(100000, 100))
        status_scroll_area.setWidgetResizable(True)
        status_scroll_area.setWidget(self.status_label)
        hbox.addWidget(status_scroll_area)
        self.status = None

        self.statusTrigger.connect(self.updateStatus)
コード例 #5
0
class AttributeColumnsTable(TaurusWidget):

    """Display several 1D spectrum attributes belonging to the same
    device as columns in a table."""

    trigger = QtCore.pyqtSignal((int, int))

    def __init__(self, parent=None):
        TaurusWidget.__init__(self, parent)
        self._setup_ui()

    def _setup_ui(self):
        hbox = QtGui.QHBoxLayout(self)
        self.setLayout(hbox)

        self.table = QtGui.QTableWidget()
        self.table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)

        hbox.addWidget(self.table)

        self.trigger.connect(self.updateColumn)
        self.attributes = []
        self._columns = []
        self._values = {}
        self._config = {}

    def setModel(self, attrs):
        if not attrs:
            for att, col in zip(self.attributes, self._columns):
                att and att.removeListener(col.event_received)
        else:
            try:
                TaurusWidget.setModel(self, attrs[0].rsplit("/", 1)[0])
                self.attributes = [Attribute(a) for a in attrs]

                self.table.setColumnCount(len(attrs))
                fmt = "%s [%s]"
                labels = []
                for a in self.attributes:
                    config = a.getConfig()
                    label = fmt % (config.getLabel(), config.getUnit())
                    labels.append(label)

                self.table.setHorizontalHeaderLabels(labels)
                header = self.table.horizontalHeader()
                header.setResizeMode(QtGui.QHeaderView.Stretch)

                # Check if there are any columns at all
                row_lengths = [len(a.read().value) for a in self.attributes
                               if a.read().quality == PyTango.AttrQuality.ATTR_VALID]
                if not any(row_lengths):
                    return None
                self.table.setRowCount(max(row_lengths))

                self._values = {}
                self._config = {}
                self._columns = []

                for i, att in enumerate(self.attributes):
                    # JFF: this is a workaround for a behavior in Taurus. Just
                    # adding a new listener to each attribute does not work, the
                    # previous ones get removed for some reason.
                    col = AttributeColumn(self, i)
                    self._columns.append(col)  # keep reference to prevent GC
                    att.addListener(col.event_received)
            except PyTango.DevFailed:
                pass

    def keyPressEvent(self, e):
        """Copy selected cells to the clipboard on Ctrl+C"""
        if (e.modifiers() & QtCore.Qt.ControlModifier):
            selected = self.table.selectedRanges()
            if e.key() == QtCore.Qt.Key_C:
                s = ""
                for r in xrange(selected[0].topRow(), selected[0].bottomRow() + 1):
                    for c in xrange(selected[0].leftColumn(), selected[0].rightColumn() + 1):
                        try:
                            s += str(self.table.item(r, c).text()) + "\t"
                        except AttributeError:
                            s += "\t"
                    s = s[:-1] + "\n"  # eliminate last '\t'
                clipboard = QtGui.QApplication.clipboard()
                clipboard.setText(s)

    def onEvent(self, column, evt_src, evt_type, evt_value):
        if evt_type in [PyTango.EventType.CHANGE_EVENT,
                        PyTango.EventType.PERIODIC_EVENT]:
            self._values[column] = evt_value
            self.trigger.emit(column)
        if isinstance(evt_value, PyTango.DeviceAttributeConfig):
            self._config[column] = evt_value

    def updateColumn(self, column):
        if not self._values:
            return  # when does this happen?
        data = self._values[column]
        for row, value in enumerate(data.value):
            if not isnan(value):
                cfg = self._config.get(column)
                if cfg and cfg.format != "Not specified":
                    item = QtGui.QTableWidgetItem(cfg.format % value)
                else:
                    item = QtGui.QTableWidgetItem(str(value))
                item.setFlags(QtCore.Qt.ItemIsSelectable |
                              QtCore.Qt.ItemIsEnabled)
            else:
                item = QtGui.QTableWidgetItem("NaN")
                item.setFlags(QtCore.Qt.ItemIsSelectable |
                              QtCore.Qt.ItemIsEnabled)
                item.setBackgroundColor(QtGui.QColor(220, 220, 220))
            item.setTextAlignment(QtCore.Qt.AlignRight |
                                  QtCore.Qt.AlignVCenter)
            self.table.setItem(row, column, item)
コード例 #6
0
class ToggleButton(TaurusWidget):

    """A button that has two states, pressed and unpressed. When pressing
    it, the 'down' command is run, and when unpressing it the 'up' command
    is run. The 'pressedness' of the button is connected to a given
    Tango state, e.g. ON."""

    state_trigger = QtCore.pyqtSignal(bool)

    def __init__(self, parent=None, down_command=None, up_command=None, state=None):
        TaurusWidget.__init__(self, parent)
        self._down_command = down_command
        self._up_command = up_command
        self._state = state
        self._setup_ui()

    def _setup_ui(self):
        hbox = QtGui.QHBoxLayout(self)
        self.setLayout(hbox)

        self.button = QtGui.QPushButton()
        # self.button.setText()
        self.button.setCheckable(True)
        hbox.addWidget(self.button)

        self.button.clicked.connect(self.onClick)
        self.state_trigger.connect(self.change_state)

    def setModel(self, model):
        TaurusWidget.setModel(self, model)
        if model:
            m = self.getModelObj()
            self.down_command = getattr(m, self._down_command)
            self.up_command = getattr(m, self._up_command)

            self.state = m.getAttribute("State")
            self.state.addListener(self.handle_state_event)
        else:
            if self.state:
                self.state.removeListener(self.handle_state_event)

    def onClick(self):
        print "state is", self.state.read()
        pressed = self.state.read().value == self._state
        print "pressed", pressed
        if pressed:
            print "running up_command", self._up_command
            self.up_command()
        else:
            print "running down_command", self._down_command
            self.down_command()

    def change_state(self, new_state):
        print "change_state", new_state
        self.button.setChecked(new_state)
        self.button.setText((self._down_command, self._up_command)[new_state])

    def handle_state_event(self, evt_src, evt_type, evt_value):
        if evt_type in [PyTango.EventType.CHANGE_EVENT,
                        PyTango.EventType.PERIODIC_EVENT]:
            print "state", self._state
            self.state_trigger.emit(evt_value.value == self._state)
コード例 #7
0
class DeviceRowsTable(TaurusWidget):

    """A widget that displays a table where each row displays a device,
    and the values of selected attributes."""

    STATE_COLORS = {
        PyTango.DevState.ON: (0, 255, 0),
        PyTango.DevState.OFF: (255, 255, 255),
        PyTango.DevState.CLOSE: (255, 255, 255),
        PyTango.DevState.OPEN: (0, 255, 0),
        PyTango.DevState.INSERT: (255, 255, 255),
        PyTango.DevState.EXTRACT: (0, 255, 0),
        PyTango.DevState.MOVING: (128, 160, 255),
        PyTango.DevState.STANDBY: (255, 255, 0),
        PyTango.DevState.FAULT: (255, 0, 0),
        PyTango.DevState.INIT: (204, 204, 122),
        PyTango.DevState.RUNNING: (128, 160, 255),
        PyTango.DevState.ALARM:  (255, 140, 0),
        PyTango.DevState.DISABLE: (255, 0, 255),
        PyTango.DevState.UNKNOWN: (128, 128, 128),
        None: (128, 128, 128)
    }

    trigger = QtCore.pyqtSignal(int, int)

    def __init__(self, parent=None):
        TaurusWidget.__init__(self, parent)
        self._setup_ui()

    def _setup_ui(self):
        hbox = QtGui.QHBoxLayout(self)
        self.setLayout(hbox)

        self.table = QtGui.QTableWidget(parent=self)

        hbox.addWidget(self.table)

        self.trigger.connect(self.update_item)
        self.table.itemClicked.connect(self.on_item_clicked)

        self._items = {}
        self.attributes = {}

    def keyPressEvent(self, e):
        """Copy selected cells to the clipboard on Ctrl+C"""
        if (e.modifiers() & QtCore.Qt.ControlModifier):
            selected = self.table.selectedRanges()
            if e.key() == QtCore.Qt.Key_C:
                s = ""
                for r in xrange(selected[0].topRow(), selected[0].bottomRow() + 1):
                    for c in xrange(selected[0].leftColumn(), selected[0].rightColumn() + 1):
                        try:
                            s += str(self.table.item(r, c).text()) + "\t"
                        except AttributeError:
                            s += "\t"
                    s = s[:-1] + "\n"  # eliminate last '\t'
                clipboard = QtGui.QApplication.clipboard()
                clipboard.setText(s)

    def setModel(self, devices, attributes=[]):
        if not devices:
            for dev, attrs in self.attributes.items():
                for att in attrs:
                    att and att.removeListener(
                        self._items[dev][att.name].event_received)
        else:
            try:
                # TaurusWidget.setModel(self, attrs[0].rsplit("/", 1)[0])
                attrnames = [a[0] if isinstance(a, tuple) else a
                             for a in attributes]
                self.attributes = dict((dev, [Attribute("%s/%s" % (dev, a))
                                              for a in attrnames])
                                       for dev in devices)

                self.table.setColumnCount(len(attributes) + 1)
                colnames = [a[1] if isinstance(a, tuple) else a
                            for a in attributes]
                labels = ["Device"] + colnames
                self.table.setHorizontalHeaderLabels(labels)
                header = self.table.horizontalHeader()
                header.setResizeMode(QtGui.QHeaderView.Stretch)

                # Check if there are any columns at all
                self.table.setRowCount(len(devices))

                for r, (dev, attrs) in enumerate(self.attributes.items()):
                    item = QtGui.QTableWidgetItem(dev)
                    item.setFlags(QtCore.Qt.ItemIsSelectable |
                                  QtCore.Qt.ItemIsEnabled)
                    self.table.setItem(r, 0, item)
                    for c, att in enumerate(attrs, 1):
                        # JFF: this is a workaround for a behavior in Taurus. Just
                        # adding a new listener to each attribute does not work, the
                        # previous ones get removed for some reason.
                        item = TableItem(self.trigger)
                        self.table.setItem(r, c, item)
                        att.addListener(item.event_received)

            except PyTango.DevFailed:
                pass

    def update_item(self, row, column):
        item = self.table.item(row, column)
        value, config = item.value, item.config

        # Set text
        if (config and config.format_spec != "Not specified") and \
                (config.format_spec != "!s"):
            data_format = "%" + config.format_spec
            item.setText(data_format % value.rvalue)
        else:
            item.setText(str(value.rvalue))

        # Set flags and state
        if item.writable_boolean:
            item.setFlags(QtCore.Qt.ItemIsSelectable
                          | QtCore.Qt.ItemIsEnabled
                          | QtCore.Qt.ItemIsUserCheckable)
            state = QtCore.Qt.Checked if value.wvalue else QtCore.Qt.Unchecked
            item.setCheckState(state)
        else:
            item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)

        # Set background color
        if config.type == 5: # DevState
            if value.rvalue in self.STATE_COLORS:
                item.setBackgroundColor(QtGui.QColor(*self.STATE_COLORS[value.rvalue]))

    def on_item_clicked(self, item):
        # Not a writable item
        if not isinstance(item, TableItem) or not item.writable_boolean:
            return
        button_state = (item.checkState() == QtCore.Qt.Checked)
        value_state = item.value.w_value
        if button_state != value_state:
            item.source.write(button_state)
コード例 #8
0
 def scaleSize(self):
     size = self.form.scrollArea.widget().frameSize()
     return QtCore.QSize(size.width(), size.height() * self.scale_factor)
コード例 #9
0
class CyclePanel(TaurusWidget):
    "Panel for controlling the cycling functionality"

    trend_trigger = QtCore.pyqtSignal(bool)

    attrs = ["CyclingTimePlateau", "CyclingIterations", "CyclingSteps",
             "CyclingRampTime", "NominalSetPoint"]
    scale_factor = 1.1

    def __init__(self, parent=None):
        TaurusWidget.__init__(self, parent)
        self._setup_ui()

    def scaleSize(self):
        size = self.form.scrollArea.widget().frameSize()
        return QtCore.QSize(size.width(), size.height() * self.scale_factor)

    def _setup_ui(self):
        vbox = QtGui.QVBoxLayout(self)
        self.setLayout(vbox)

        grid = QtGui.QGridLayout()
        self.form = MAXForm(withButtons=False)

        grid.addWidget(self.form, 0, 0, 2, 1)
        # rescale taurus form methode
        self.form.scrollArea.sizeHint = self.scaleSize
        self.status_label = StatusArea()
        grid.addWidget(self.status_label, 0, 1, 1, 1)

        commandbox = QtGui.QVBoxLayout(self)
        self.start_button = TaurusCommandButton(command="StartCycle")
        self.start_button.setUseParentModel(True)
        self.stop_button = TaurusCommandButton(command="StopCycle")
        self.stop_button.setUseParentModel(True)
        commandbox.addWidget(self.start_button)
        commandbox.addWidget(self.stop_button)
        grid.addLayout(commandbox, 1, 1, 1, 1)

        vbox.addLayout(grid)

        self.trend = TaurusTrend()
        vbox.addWidget(self.trend, stretch=1)
        self.trend_trigger.connect(self.set_trend_paused)

        self.cyclingState = None

    def setModel(self, device):
        print self.__class__.__name__, "setModel", device
        TaurusWidget.setModel(self, device)
        # self.state_button.setModel(device)
        if device:
            self.form.setModel(["%s/%s" % (device, attribute)
                                for attribute in self.attrs])

            self.status_label.setModel("%s/cyclingStatus" % device)

            ps = str(PyTango.Database().get_device_property(
                device, "PowerSupplyProxy")["PowerSupplyProxy"][0])

            self.trend.setPaused()
            self.trend.setModel(["%s/Current" % ps])
            self.trend.setForcedReadingPeriod(0.2)
            self.trend.showLegend(True)

            # let's pause the trend when not cycling
            self.cyclingState = self.getModelObj().getAttribute("cyclingState")
            self.cyclingState.addListener(self.handle_cycling_state)
        else:
            if self.cyclingState:
                self.cyclingState.removeListener(self.handle_cycling_state)
            self.trend.setModel(None)
            self.status_label.setModel(None)

    def handle_cycling_state(self, evt_src, evt_type, evt_value):
        if evt_type in [PyTango.EventType.CHANGE_EVENT,
                        PyTango.EventType.PERIODIC_EVENT]:
            self.trend_trigger.emit(evt_value.value)

    def set_trend_paused(self, value):
        self.trend.setForcedReadingPeriod(0.2 if value else -1)
        self.trend.setPaused(not value)
コード例 #10
0
class MAXValueBar(TaurusWidget):

    value_trigger = QtCore.pyqtSignal(float, float)
    conf_trigger = QtCore.pyqtSignal()

    _delta = 1

    def __init__(self, parent=None, designMode=False):
        TaurusWidget.__init__(self, parent, designMode=designMode)
        self._setup_ui()

        self._throttle_timer = QtCore.QTimer()
        self._throttle_timer.setInterval(200)
        self._throttle_timer.setSingleShot(True)
        self.connect(self._throttle_timer, QtCore.SIGNAL("timeout()"),
                     self._writeValue)

        self._value = None

        self._acc_value = 0  # accumulate fast wheel events
        self._last_wheel = 0  # time of last wheel event

    @classmethod
    def getQtDesignerPluginInfo(cls):
        ret = TaurusWidget.getQtDesignerPluginInfo()
        ret['module'] = 'maxvaluebar'
        ret['group'] = 'MAX-lab Taurus Widgets'
        ret['container'] = ':/designer/frame.png'
        ret['container'] = False
        return ret

    def _setup_ui(self):
        vbox = QtGui.QHBoxLayout(self)
        self.valuebar = ValueBarWidget()
        vbox.addWidget(self.valuebar)
        self.setLayout(vbox)
        self.value_trigger.connect(self.valuebar.setValue)
        self.conf_trigger.connect(self.updateConfig)
        self.setFocusPolicy(QtCore.Qt.WheelFocus)

    def setModel(self, model):
        TaurusWidget.setModel(self, model)
        self.updateConfig()
        conf = Configuration("%s?configuration" % self.model)
        conf.addListener(lambda *args: self.conf_trigger.emit())

    def _decimalDigits(self, fmt):
        '''returns the number of decimal digits from a format string
        (or None if they are not defined)'''
        try:
            if fmt[-1].lower() in ['f', 'g'] and '.' in fmt:
                return int(fmt[:-1].split('.')[-1])
            else:
                return None
        except:
            return None

    def updateConfig(self):
        conf = Configuration("%s?configuration" % self.model)
        # Note: could be inefficient with lots of redraws?
        self.valuebar.setMaximum(float_or_none(conf.max_value))
        self.valuebar.setMinimum(float_or_none(conf.min_value))
        self.valuebar.setWarningHigh(float_or_none(conf.max_warning))
        self.valuebar.setWarningLow(float_or_none(conf.min_warning))
        self.valuebar.setAlarmHigh(float_or_none(conf.max_alarm))
        self.valuebar.setAlarmLow(float_or_none(conf.min_alarm))

        digits = self._decimalDigits(conf.format)
        if digits:
            self._delta = pow(10, -digits)

    @QtCore.pyqtSlot()
    def _writeValue(self):
        if self._value:
            self.getModelObj().write(self._value)

    def throttledWrite(self, value):
        """Intead of writing to Tango every time the value changes, we start a
        timer. Writes during the timer will be accumulated and when the timer
        runs out, the last value is written.
        """
        self._value = value
        if not self._throttle_timer.isActive():
            self._throttle_timer.start()

    def handleEvent(self, evt_src, evt_type, evt_value):
        if evt_type in (PyTango.EventType.PERIODIC_EVENT,
                        PyTango.EventType.CHANGE_EVENT):
            if evt_value.quality == PyTango.AttrQuality.ATTR_VALID:
                self.value_trigger.emit(evt_value.value, evt_value.w_value)
        elif evt_type in (PyTango.EventType.ATTR_CONF_EVENT,
                          PyTango.EventType.QUALITY_EVENT):
            # Note: why don't I get "ATTR_CONF" events when the attribute
            # config changes? Seems like I get QUALITY events instead..?
            self.conf_trigger.emit()

    def wheelEvent(self, evt):

        model = self.getModelObj()

        evt.accept()
        numDegrees = evt.delta() / 8
        numSteps = numDegrees / 15
        modifiers = evt.modifiers()
        if modifiers & Qt.Qt.ControlModifier:
            numSteps *= 10
        elif (modifiers & Qt.Qt.AltModifier) and model.isFloat():
            numSteps *= .1

        # We change the value by 1 in the least significant digit according
        # to the configured format.
        value = self.valuebar.write_value + numSteps * self._delta
        self.valuebar.setWriteValue(value)
        self.throttledWrite(value)