Esempio n. 1
0
    def __init__(self, parent, client, options):
        Panel.__init__(self, parent, client, options)

        # make a vertical layout for 'ourself'
        self.vBoxLayout = QVBoxLayout(self)

        # make a buttonBox
        self.buttonBox = QDialogButtonBox(self.buttons, parent=self)
        self.buttonBox.setObjectName('buttonBox')

        # put buttonBox below main content
        self.vBoxLayout.addWidget(self.buttonBox)

        allButtons = 'Ok Open Save Cancel Close Discard Apply Reset '\
                     'RestoreDefaults Help SaveAll Yes YesToAll No NoToAll '\
                     'Abort Retry Ignore'.split()
        for btn_name in allButtons:
            btn = self.buttonBox.button(getattr(QDialogButtonBox, btn_name))
            if btn:
                handler = getattr(self, 'on_buttonBox_%s_clicked' % btn_name,
                                  None)
                if not handler:
                    # pylint: disable=function-redefined
                    def handler(self=self, n=btn_name):
                        self.showError('on_buttonBox_%s_clicked not '
                                       'implemented!' % n)

                btn.clicked.connect(handler)
Esempio n. 2
0
 def __init__(self, parent, designMode=False, **kwds):
     QWidget.__init__(self, parent, **kwds)
     NicosWidget.__init__(self)
     self._entries = []
     self._vlayout = QVBoxLayout()
     self._vlayout.setContentsMargins(0, 0, 0, 0)
     self.setLayout(self._vlayout)
     self._add()
     self._designer = designMode
Esempio n. 3
0
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        self.canvas = FigureCanvas(Figure(figsize=(20, 16)))
        self._static_ax = self.canvas.figure.subplots()

        layout = QVBoxLayout()
        layout.addWidget(self.canvas)
        self.setLayout(layout)
Esempio n. 4
0
class CustomButtonPanel(Panel):
    """Base class for custom instrument specific panels

    with a QDialogButtonBox at the lower right and some glue magic for
    fancy stuff...
    """
    buttons = QDialogButtonBox.Close | QDialogButtonBox.Apply

    def __init__(self, parent, client, options):
        Panel.__init__(self, parent, client, options)

        # make a vertical layout for 'ourself'
        self.vBoxLayout = QVBoxLayout(self)

        # make a buttonBox
        self.buttonBox = QDialogButtonBox(self.buttons, parent=self)
        self.buttonBox.setObjectName('buttonBox')

        # put buttonBox below main content
        self.vBoxLayout.addWidget(self.buttonBox)

        allButtons = 'Ok Open Save Cancel Close Discard Apply Reset '\
                     'RestoreDefaults Help SaveAll Yes YesToAll No NoToAll '\
                     'Abort Retry Ignore'.split()
        for btn_name in allButtons:
            btn = self.buttonBox.button(getattr(QDialogButtonBox, btn_name))
            if btn:
                handler = getattr(self, 'on_buttonBox_%s_clicked' % btn_name,
                                  None)
                if not handler:
                    # pylint: disable=function-redefined
                    def handler(self=self, n=btn_name):
                        self.showError('on_buttonBox_%s_clicked not '
                                       'implemented!' % n)

                btn.clicked.connect(handler)

    def panelState(self):
        """returns current window state as obtained from the stack of parents"""
        obj = self
        while hasattr(obj, 'parent'):
            if isinstance(obj, AuxiliaryWindow):
                return "tab"
            elif isinstance(obj, DetachedWindow):
                return "detached"
            obj = obj.parent()
        return "main"

    def on_buttonBox_Close_clicked(self):
        self.closeWindow()

    def on_buttonBox_Ok_clicked(self):
        """OK = Apply + Close"""
        if hasattr(self, 'on_buttonBox_Apply_clicked'):
            self.on_buttonBox_Apply_clicked()
        self.on_buttonBox_Close_clicked()
Esempio n. 5
0
    def __init__(self, parent, client, options):
        LokiPanelBase.__init__(self, parent, client, options)
        loadUi(self, findResource('nicos_ess/loki/gui/ui_files/sampleconf.ui'))
        self.sampleGroup.setEnabled(False)
        self.frame.setLayout(QVBoxLayout())

        self.sample_frame = QFrame(self)
        loadUi(
            self.sample_frame,
            findResource('nicos_ess/loki/gui/ui_files/sampleconf_summary.ui'))

        layout = self.frame.layout()
        layout.addWidget(self.sample_frame)
        self.sample_frame.hide()

        self.sample_frame.posTbl.setEnabled(False)

        for box in self.sample_frame.findChildren(QLineEdit):
            box.setEnabled(False)

        menu = QMenu(self)
        menu.addAction(self.actionEmpty)
        menu.addAction(self.actionGenerate)
        self.createBtn.setMenu(menu)

        self.configs = []
        self.holder_info = options.get('holder_info', [])
        self.instrument = options.get('instrument', 'loki')
        self.unapplied_changes = False
        self.applyBtn.setEnabled(False)
        self.initialise_connection_status_listeners()
Esempio n. 6
0
    def __init__(self, parent, client, options):
        Panel.__init__(self, parent, client, options)
        loadUi(self, findResource('nicos_mlz/kws1/gui/sampleconf.ui'))
        self.sampleGroup.setEnabled(False)
        self.frame.setLayout(QVBoxLayout())

        menu = QMenu(self)
        menu.addAction(self.actionCopyAperture)
        menu.addAction(self.actionCopyDetOffset)
        menu.addAction(self.actionCopyThickness)
        menu.addAction(self.actionCopyTimeFactor)
        menu.addSeparator()
        menu.addAction(self.actionCopyAll)
        self.copyBtn.setMenu(menu)

        menu = QMenu(self)
        menu.addAction(self.actionEmpty)
        menu.addAction(self.actionGenerate)
        self.createBtn.setMenu(menu)

        self.configs = []
        self.dirty = False
        self.filename = None
        self.holder_info = options.get('holder_info', [])
        self.instrument = options.get('instrument', 'kws1')
Esempio n. 7
0
 def __init__(self, parent, nmin, allow_enter=False):
     QScrollArea.__init__(self, parent)
     self.setWidgetResizable(True)
     self.frame = QFrame(self)
     self.layout = QVBoxLayout()
     self.layout.setContentsMargins(2, 2, 2, 2)
     self.addBtn = QPushButton(QIcon(':/add'), '', self.frame)
     self.addBtn.clicked.connect(self.on_addBtn_clicked)
     self.addBtn.setSizePolicy(
         QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred))
     self.layout.addWidget(self.addBtn)
     self.layout.addStretch()
     self.frame.setLayout(self.layout)
     self.setWidget(self.frame)
     self.items = []
     self.nmin = nmin
     self.allow_enter = allow_enter
Esempio n. 8
0
    def __init__(self, item, window, menuwindow, parent):
        from nicos.clients.gui.panels.utils import createWindowItem
        QMainWindow.__init__(self, parent)
        self.user_color = window.user_color
        self.mainwindow = window.mainwindow
        self.log = NicosLogger('AuxiliarySubWindow')
        self.log.parent = self.mainwindow.log

        self.panels = []

        # we have to nest one step to get consistent layout spacing
        # around the central widget
        central = QWidget(self)
        layout = QVBoxLayout()
        # only keep margin at the top (below the tabs)
        layout.setContentsMargins(0, 6, 0, 0)
        if len(item) == 1:
            (subitem, setupSpec) = item + (None, )
        else:
            (subitem, setupSpec) = item
        it = createWindowItem(subitem, window, menuwindow, self, self.log)
        if it:
            if isinstance(it, (
                    Panel,
                    QSplitter,
            )):
                if isinstance(it, Panel):
                    it.hideTitle()
                # if tab has its own setups overwrite panels setups
                if setupSpec:
                    it.setSetups(setupSpec)
                it.setWidgetVisible.connect(parent.setWidgetVisibleSlot)
            layout.addWidget(it)
            central.setLayout(layout)
            self.setCentralWidget(central)
Esempio n. 9
0
 def __init__(self, parent, client, instrument, configs, config=None):
     QDialog.__init__(self, parent)
     self.instrument = instrument
     self.configs = configs
     self.client = client
     self.setWindowTitle('Sample configuration')
     layout = QVBoxLayout()
     self.frm = QFrame(self)
     loadUi(self.frm, findResource('nicos_mlz/kws1/gui/sampleconf_one.ui'))
     self.frm.addDevBtn.clicked.connect(self.on_addDevBtn_clicked)
     self.frm.delDevBtn.clicked.connect(self.on_delDevBtn_clicked)
     self.frm.readDevsBtn.clicked.connect(self.on_readDevsBtn_clicked)
     self.frm.readApBtn.clicked.connect(self.on_readApBtn_clicked)
     box = QDialogButtonBox(self)
     box.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
     box.accepted.connect(self.maybeAccept)
     box.rejected.connect(self.reject)
     layout.addWidget(self.frm)
     layout.addWidget(box)
     self.setLayout(layout)
     for box in [self.frm.offsetBox, self.frm.thickBox, self.frm.factorBox,
                 self.frm.apXBox, self.frm.apYBox, self.frm.apWBox,
                 self.frm.apHBox]:
         box.setValidator(DoubleValidator(self))
     if config is not None:
         configToFrame(self.frm, config)
Esempio n. 10
0
    def __init__(self, parent, client, instrument, configs, config=None):
        QDialog.__init__(self, parent)
        self.instrument = instrument
        self.configs = configs
        self.client = client
        self.setWindowTitle('Sample configuration')
        layout = QVBoxLayout()
        self.frm = QFrame(self)
        loadUi(self.frm,
               findResource('nicos_ess/loki/gui/ui_files/sampleconf_one.ui'))
        self.frm.addDevBtn.clicked.connect(self.on_addDevBtn_clicked)
        self.frm.delDevBtn.clicked.connect(self.on_delDevBtn_clicked)
        self.frm.readDevsBtn.clicked.connect(self.on_readDevsBtn_clicked)
        box = QDialogButtonBox(self)
        box.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        box.accepted.connect(self.maybeAccept)
        box.rejected.connect(self.reject)
        layout.addWidget(self.frm)
        layout.addWidget(box)
        self.setLayout(layout)
        self.frm.thickBox.setValidator(DoubleValidator(self))
        if config is not None:
            configToFrame(self.frm, config)

        # Apply local customisations to the stylesheet
        self.setStyleSheet(ConfigEditDialog_QSS)

        if not config:
            self.frm.whatLbl.setText('New sample configuration')
Esempio n. 11
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)
Esempio n. 12
0
 def __init__(self, title, xlabel, ylabel, name='unknown', parent=None,
              **kwds):
     QWidget.__init__(self, parent)
     self.name = name
     parent.setLayout(QVBoxLayout())
     self.plot = MiniPlot(xlabel, ylabel, self, color1=COLOR_BLACK,
                          color2=COLOR_RED)
     titleLabel = QLabel(title)
     titleLabel.setAlignment(Qt.AlignCenter)
     titleLabel.setStyleSheet('QLabel {font-weight: 600}')
     parent.layout().insertWidget(0, titleLabel)
     self.plot.setSizePolicy(QSizePolicy.MinimumExpanding,
                             QSizePolicy.MinimumExpanding)
     parent.layout().insertWidget(1, self.plot)
Esempio n. 13
0
 def __init__(self, parent, values, curvalue, client):
     QWidget.__init__(self, parent)
     layout = self._layout = QVBoxLayout()
     self.checkboxes = []
     self.values = []
     curvalue = curvalue or set()
     for value in values:
         checkbox = QCheckBox(str(value), self)
         if value in curvalue:
             checkbox.setCheckState(Qt.Checked)
         checkbox.stateChanged.connect(self.on_checkbox_stateChanged)
         layout.addWidget(checkbox)
         self.checkboxes.append(checkbox)
     layout.setContentsMargins(0, 0, 0, 0)
     self.setLayout(layout)
Esempio n. 14
0
    def __init__(self, parent, inner, selector, buttons):
        QWidget.__init__(self, parent)
        self._inner = inner
        self._selector = selector

        layout = self._layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        inner.valueModified.connect(self.valueModified)
        inner.valueChosen.connect(self.valueChosen)
        if not buttons:
            selector.valueModified.connect(self.on_selector_valueModified)
            inner.setVisible(selector.getValue() is Ellipsis)
        else:
            selector.valueChosen.connect(self.valueChosen)
        layout.addWidget(selector)
        layout.addWidget(inner)
        self.setLayout(layout)
Esempio n. 15
0
 def __init__(self, parent=None, designMode=False, **kwds):
     QWidget.__init__(self, parent, **kwds)
     NicosWidget.__init__(self)
     self._last_mtime = None
     self.namelabel = QLabel(self)
     self.namelabel.setAlignment(Qt.AlignHCenter)
     self.piclabel = QLabel(self)
     self.piclabel.setScaledContents(True)
     layout = QVBoxLayout()
     layout.setContentsMargins(0, 0, 0, 0)
     layout.addWidget(self.piclabel, 1)
     self.setLayout(layout)
Esempio n. 16
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
Esempio n. 17
0
    def __init__(self, main):
        QDialog.__init__(self, main)
        loadUi(self, 'dialogs/watchdog.ui')

        self.frame = QFrame(self)
        self.scrollArea.setWidget(self.frame)
        self.frame.setLayout(QVBoxLayout())
        self.frame.layout().setContentsMargins(0, 0, 10, 0)
        self.frame.layout().addStretch()

        def btn(button):
            if self.buttonBox.buttonRole(button) == QDialogButtonBox.ResetRole:
                for w in self.frame.children():
                    if isinstance(w, QWidget):
                        w.hide()
            else:
                self.close()

        self.buttonBox.clicked.connect(btn)
Esempio n. 18
0
 def propertyUpdated(self, pname, value):
     NicosWidget.propertyUpdated(self, pname, value)
     if pname == 'filepath':
         self._filePath = findResource(value)
         self.setPicture()
     elif pname == 'name':
         layout = QVBoxLayout()
         if value:
             layout.addWidget(self.namelabel)
             layout.addSpacing(5)
         layout.addWidget(self.piclabel, 1)
         sip.delete(self.layout())
         self.setLayout(layout)
         self.namelabel.setText(value)
     elif pname in ('width', 'height'):
         self.setPicture()
     elif pname == 'refresh':
         if value:
             self._refreshTimer = QTimer()
             self._refreshTimer.setInterval(value * 1000)
             self._refreshTimer.timeout.connect(self.updatePicture)
             self._refreshTimer.start()
Esempio n. 19
0
 def viewTextFile(self, fname):
     with open(fname) as f:
         contents = f.read()
     qd = QDialog(self, 'PreviewDlg', True)
     qd.setCaption('File preview')
     qd.resize(QSize(500, 500))
     lay = QVBoxLayout(qd, 11, 6, 'playout')
     lb = QLabel(qd, 'label')
     lb.setText('Viewing %s:' % fname)
     lay.addWidget(lb)
     tx = QTextEdit(qd, 'preview')
     tx.setReadOnly(1)
     tx.setText(contents)
     font = QFont(tx.font())
     font.setFamily('monospace')
     tx.setFont(font)
     lay.addWidget(tx)
     btn = QPushButton(qd, 'ok')
     btn.setAutoDefault(1)
     btn.setDefault(1)
     btn.setText('Close')
     btn.clicked.connect(qd.accept)
     lay.addWidget(btn, 0, QWidget.AlignRight)
     qd.show()
Esempio n. 20
0
 def reinitLayout(self):
     # reinitialize UI after switching horizontal/vertical layout
     if self.props['horizontal']:
         new_layout = QHBoxLayout()
         new_layout.addWidget(self.namelabel)
         new_layout.addStretch()
         new_layout.addWidget(self.valuelabel)
         self.namelabel.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
     else:
         new_layout = QVBoxLayout()
         new_layout.addWidget(self.namelabel)
         tmplayout = QHBoxLayout()
         if self.width >= 0:
             tmplayout.addStretch()
         tmplayout.addWidget(self.valuelabel)
         if self.width >= 0:
             tmplayout.addStretch()
         new_layout.addLayout(tmplayout)
         self.namelabel.setAlignment(Qt.AlignHCenter)
     if self.layout():
         sip.delete(self.layout())
     new_layout.setContentsMargins(1, 1, 1, 1)  # save space
     self.setLayout(new_layout)
Esempio n. 21
0
    def __init__(self, parent, measdef, client):
        self.measdef = measdef
        self.client = client
        QDialog.__init__(self, parent)
        loadUi(self, findResource('nicos_ess/loki/gui/ui_files/devices.ui'))

        self.frame = QFrame(self)
        self.scrollArea.setWidget(self.frame)
        self.frame.setLayout(QVBoxLayout())
        self.frame.layout().setContentsMargins(0, 0, 10, 0)
        self.frame.layout().addStretch()

        devlist = client.getDeviceList('nicos.core.device.Moveable')
        for dev in devlist:
            if dev not in DEV_NOT_ALLOWED:
                QListWidgetItem(dev, self.devList)

        self._widgets = []

        for group in measdef.devices:
            devs = group[0].keys()
            w = self._addWidget(devs)
            for entry in group:
                w.addRow([entry[x] for x in devs])
Esempio n. 22
0
    def __init__(self, parent, measdef, client):
        self._edit = None
        self.measdef = measdef
        self.client = client
        QDialog.__init__(self, parent)
        loadUi(self, findResource('nicos_ess/loki/gui/ui_files/detsets.ui'))
        self.table.setColumnCount(len(measdef.getElements()))

        # apply current settings
        self._rows = []
        if measdef.detsets:
            for row in measdef.detsets[0]:
                self.addRow(row)

        # create widgets for new setting
        self._new_elements = {}
        headers = []
        for i, (eltype, cls) in enumerate(measdef.getElements()):
            element = self._new_elements[eltype] = cls(eltype, self.client)
            w = element.createWidget(self, self.client)
            for other in self._new_elements.values():
                if other is not element:
                    element.otherChanged(other.eltype, other.getValue())

            def handler(new_value, eltype=eltype):
                for other in self._new_elements.values():
                    other.otherChanged(eltype, new_value)

            element.changed.connect(handler)
            headers.append(element.getLabel())
            layout = QVBoxLayout()
            layout.addWidget(QLabel(headers[-1], self))
            layout.addWidget(w)
            self.widgetFrame.layout().insertLayout(i, layout)

        # update table widget
        self.table.setHorizontalHeaderLabels(headers)
        self.table.resizeColumnsToContents()
        for i in range(len(measdef.getElements())):
            self.table.setColumnWidth(i,
                                      max(50, 1.5 * self.table.columnWidth(i)))
        self.table.resizeRowsToContents()
Esempio n. 23
0
    def __init__(self, parent=None, statusbar=None):
        super(StatusBarOverlay, self).__init__(parent)

        self.statusbar = statusbar

        print(parent.__class__.__name__)

        layout = QVBoxLayout()

        line = QHBoxLayout()
        self.graphicsButton = QPushButton('Graphics')
        self.logButton = QPushButton('Log')
        self.toggleButton = QPushButton('Show')
        self.graphicsButton.setCheckable(True)
        self.logButton.setCheckable(True)
        self.toggleButton.setCheckable(True)

        self.buttonsBox = QButtonGroup()
        self.buttonsBox.addButton(self.graphicsButton)
        self.buttonsBox.addButton(self.logButton)
        # self.buttonsBox.addButton(self.toggleButton)

        line.addStretch()
        line.addWidget(self.graphicsButton)
        line.addWidget(self.logButton)
        line.addWidget(self.toggleButton)
        line.addStretch()

        line.setObjectName('ButtonsLayout')
        layout.addLayout(line)

        del line
        line = QHBoxLayout()

        layout.addLayout(line)
        self.setLayout(layout)
        self.toggleButton.clicked.connect(self.on_toggle)

        self.set_hidden()
Esempio n. 24
0
    def __init__(self, model, name, addr):
        super(BaseDev, self).__init__()
        self.name = name
        self.model = model
        self.addr = addr

        self._namelabel = QLabel(name)
        self._namelabel.setMinimumWidth(120)
        self._namelabel.setMaximumWidth(120)
        # self._groupbox = QGroupBox(name)
        self._groupbox = QFrame()
        # self._groupbox.setFlat(False)
        # self._groupbox.setCheckable(False)
        self._hlayout = QHBoxLayout()
        self._hlayout.addWidget(self._namelabel)
        self._hlayout.addWidget(self._groupbox)
        self._hlayout.setSpacing(0)

        # inside of the groupbox there is a vbox with 1 or 2 hboxes
        self._inner_vbox = QVBoxLayout()
        self._groupbox.setLayout(self._inner_vbox)

        # upper inner hbox
        self._inner_hbox1 = QHBoxLayout()
        self._inner_vbox.addLayout(self._inner_hbox1)

        # fill upper hbox
        self.valueWidget = QLineEdit('0b123456789abcdef0')
        self.valueWidget.setMaximumWidth(120)
        self._inner_hbox1.addWidget(self.valueWidget)

        if self.has_target:
            self.targetWidget = QLineEdit()
            self.targetWidget.setPlaceholderText('')
            self.targetWidget.setMaximumWidth(120)
            self.targetWidget.returnPressed.connect(self._go_clicked)
            self._inner_hbox1.addWidget(self.targetWidget)
            self.goButton = QPushButton('Go')
            self.goButton.clicked.connect(self._go_clicked)
            self._inner_hbox1.addWidget(self.goButton)
            self.stopButton = QPushButton('Stop')
            self.stopButton.clicked.connect(self._stop_clicked)
            self._inner_hbox1.addWidget(self.stopButton)

        # now (conditionally) the second hbox
        if self.has_status:
            self._inner_hbox2 = QHBoxLayout()
            self._inner_vbox.addLayout(self._inner_hbox2)

            self.statvalueWidget = QLineEdit('statval')
            self.statvalueWidget.setMaximumWidth(120)
            self._inner_hbox2.addWidget(self.statvalueWidget)
            self.statusWidget = QLineEdit('Statusstring if available')
            self.statusWidget.setMaximumWidth(10000)
            self._inner_hbox2.addWidget(self.statusWidget)
            self.resetButton = QPushButton('Reset')
            self.resetButton.clicked.connect(self._reset_clicked)
            self._inner_hbox1.addWidget(self.resetButton)
            # self._inner_hbox2.addStretch(0.1)

        # allow space for resizing
        self._inner_hbox1.addStretch(1)

        self._inner_vbox.setSpacing(0)
        self._inner_vbox.setContentsMargins(0, 0, 0, 0)

        self._hlayout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self._hlayout)
        self.show()
Esempio n. 25
0
class ItemsWidget(QScrollArea):

    valueModified = pyqtSignal()
    valueChosen = pyqtSignal(object)

    allow_reorder = True

    def __init__(self, parent, nmin, allow_enter=False):
        QScrollArea.__init__(self, parent)
        self.setWidgetResizable(True)
        self.frame = QFrame(self)
        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(2, 2, 2, 2)
        self.addBtn = QPushButton(QIcon(':/add'), '', self.frame)
        self.addBtn.clicked.connect(self.on_addBtn_clicked)
        self.addBtn.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred))
        self.layout.addWidget(self.addBtn)
        self.layout.addStretch()
        self.frame.setLayout(self.layout)
        self.setWidget(self.frame)
        self.items = []
        self.nmin = nmin
        self.allow_enter = allow_enter

    def insertItem(self, *widgets):
        item = QWidget(self.frame)
        item._widgets = widgets
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        for widget in widgets:
            layout.addWidget(widget)
        if self.allow_reorder:
            btn = QPushButton(QIcon(':/up'), '', item)
            btn._item = item
            btn.clicked.connect(self.on_upBtn_clicked)
            layout.addWidget(btn)
            btn = QPushButton(QIcon(':/down'), '', item)
            btn._item = item
            btn.clicked.connect(self.on_downBtn_clicked)
            layout.addWidget(btn)
        btn = QPushButton(QIcon(':/remove'), '', item)
        btn._item = item
        btn.clicked.connect(self.on_removeBtn_clicked)
        layout.addWidget(btn)
        item.setLayout(layout)
        self.layout.insertWidget(self.layout.count() - 2, item)
        self.items.append(item)

    def on_addBtn_clicked(self):
        self.insertItem(*self.createItem())
        self.valueModified.emit()

    def on_removeBtn_clicked(self):
        if len(self.items) <= self.nmin:
            return
        item = self.sender()._item
        index = self.items.index(item)
        del self.items[index]
        self.layout.takeAt(index).widget().deleteLater()
        self.valueModified.emit()

    def on_upBtn_clicked(self):
        item = self.sender()._item
        index = self.items.index(item)
        if index <= 0:
            return
        self._swapItems(index - 1)

    def on_downBtn_clicked(self):
        item = self.sender()._item
        index = self.items.index(item)
        if index >= len(self.items) - 1:
            return
        self._swapItems(index)

    def _swapItems(self, firstindex):
        item1 = self.items[firstindex]
        item2 = self.items[firstindex + 1]
        self.layout.takeAt(firstindex)
        self.layout.takeAt(firstindex)  # moved up one
        self.items[firstindex:firstindex + 2] = [item2, item1]
        self.layout.insertWidget(firstindex, item2)
        self.layout.insertWidget(firstindex + 1, item1)
        self.valueModified.emit()
Esempio n. 26
0
 def __init__(self, title, parent=None):
     QGroupBox.__init__(self, title=title, parent=parent)
     self.setContentsMargins(0, 0, 0, 0)
     vbox = QVBoxLayout()
     vbox.setContentsMargins(0, 0, 0, 0)
     self.setLayout(vbox)
Esempio n. 27
0
class MainWindow(QMainWindow):
    i = 0

    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)

        # scroll area Widget contents - layout
        self.scrollLayout = QFormLayout()
        self.scrollLayout.setContentsMargins(0, 0, 0, 0)

        # scroll area Widget contents
        self.scrollWidget = QWidget()
        self.scrollWidget.setLayout(self.scrollLayout)

        # scroll area
        self.scrollArea = QScrollArea()
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setWidget(self.scrollWidget)

        # main layout
        self.mainLayout = QVBoxLayout()
        self.mainLayout.setSpacing(0)

        # add all main to the main vLayout
        self.mainLayout.addWidget(self.scrollArea)

        # central Widget
        self.centralWidget = QWidget()
        self.centralWidget.setLayout(self.mainLayout)

        # set central Widget
        self.setCentralWidget(self.centralWidget)

        try:
            self._bus = ModbusTcpClient('drum.panda.frm2')
            self._bus.connect()
            self._sync()
            print("Modbus synced!")
            print(self.ReadWord(0x20), self.ReadWord(0x21))
        except Exception as err:
            print("Modbus failed: %r, using demo mode!" % err)
            self._bus = None

        self._sync()

        widgets = []

        widgets.append(
            BaseDev(self, 'mtt motor inputs', 0, has_status=True, addr=34))

        widgets.append(
            BaseDev(self, 'spare inputs', 1, has_status=True, addr=44))
        widgets.append(
            BaseDev(self,
                    'spare outputs',
                    2,
                    has_status=True,
                    target='0x%04x' % self.ReadWord(60),
                    addr=59))
        widgets.append(
            BaseDev(self,
                    'enable_word',
                    3,
                    target='0x%04x' % self.ReadWord(73),
                    addr=73))
        widgets.append(BaseDev(self, 'cycle_counter', 4, addr=74))
        widgets.append(
            BaseDev(self,
                    'handle_cw',
                    5,
                    has_status=True,
                    target='%d' % self.ReadWord(51),
                    addr=50))
        widgets.append(
            BaseDev(self,
                    'handle_ccw',
                    6,
                    has_status=True,
                    target='%d' % self.ReadWord(54),
                    addr=53))
        widgets.append(BaseDev(self, 'enc1', 7, has_status=True, addr=46))
        widgets.append(BaseDev(self, 'enc2', 8, has_status=True, addr=48))
        widgets.append(
            BaseDev(self,
                    'arm_switch',
                    9,
                    has_status=True,
                    target='%d' % self.ReadWord(63),
                    addr=62))
        widgets.append(BaseDev(self, 'encoder1', 10, has_status=False,
                               addr=65))
        widgets.append(
            BaseDev(self,
                    'arm',
                    11,
                    has_status=True,
                    target='%f' % self.ReadFloat(70),
                    value_offset=2,
                    addr=68))
        widgets.append(
            BaseDev(self,
                    'magnet',
                    12,
                    has_status=True,
                    target='%d' % self.ReadWord(57),
                    addr=56))
        widgets.append(
            BaseDev(self,
                    'air',
                    13,
                    has_status=True,
                    target='%d' % self.ReadWord(60),
                    addr=59))

        for w in widgets:
            self.addWidget(w)

        widgets.sort(key=lambda w: w.index)
        self.widgets = widgets

        self.startTimer(225)  # in ms !

    def resetter(self, index):
        w = self.widgets[index]
        if w.has_status:
            addr = w.base_address + w.offset
            if w.has_target:
                addr += w.offset
            print(addr)
            self.reset(addr)
        else:
            print("resetter: device %d has no status" % index)

    def stopper(self, index):
        w = self.widgets[index]
        if w.has_target:
            addr = w.base_address
            if w.name == 'enable_word':
                print("stopper: DISABLING %d" % (addr))
                self.WriteWord(addr, 0)
            else:
                addr += w.offset
                if w.has_target:
                    addr += w.offset
                print("stopper: stopping on addr %d" % (addr))
                self.stop(addr)

        else:
            print("stopper: cannot stop - no target %d" % index)

    def targeter(self, index, valuestr):
        w = self.widgets[index]
        if w.has_target:
            v = str(valuestr).strip()
            if not v:
                return  # ignore empty values (no value entered into the box?)
            addr = w.base_address
            if w.offset == 2:
                v = float(v)
                addr += w.offset
                print("targeter: setting addr %d to %f" % (addr, v))
                self.WriteFloat(addr, v)
            else:
                if v.startswith('0x') or v.startswith('0X'):
                    v = int(v[2:], 16)
                elif v.startswith(('x', 'X', '$')):
                    v = int(v[1:], 16)
                else:
                    v = int(v)
                if w.name != 'enable_word':
                    addr += w.offset
                print("targeter: setting addr %d to %r" % (addr, valuestr))
                self.WriteWord(addr, v)
        else:
            print("targeter: device fas no target %d:%r" % (index, valuestr))

    def ReadWord(self, addr):
        return self._registers[int(addr)]

    def WriteWord(self, addr, value):
        self._bus.write_register(int(addr | 0x4000), int(value))
        self._sync()

    def ReadDWord(self, addr):
        return unpack(
            '<I',
            pack('<HH', self._registers[int(addr)],
                 self._registers[int(addr) + 1]))

    def WriteDWord(self, addr, value):
        low, high = unpack('<HH', pack('<I', int(value)))
        self._bus.write_registers(int(addr | 0x4000), [low, high])
        self._sync()

    def ReadFloat(self, addr):
        return unpack(
            '<f',
            pack('<HH', self._registers[int(addr) + 1],
                 self._registers[int(addr)]))

    def WriteFloat(self, addr, value):
        low, high = unpack('<HH', pack('<f', float(value)))
        self._bus.write_registers(int(addr | 0x4000), [high, low])
        self._sync()

    def _sync(self):
        if self._bus:
            self._registers = self._bus.read_holding_registers(0x4000,
                                                               75).registers[:]
            # print(self._registers)
        else:
            self._registers = [self.i] * 75
            self.i += 1

    def reset(self, addr):
        self.WriteWord(addr, 0x0fff & self.ReadWord(addr))

    def stop(self, addr):
        self.WriteWord(addr, 0x1000 | (0x0fff & self.ReadWord(addr)))

    def timerEvent(self, event):

        self._sync()
        w = self.widgets

        # 1: %MB68: cycle counter
        val = self.ReadWord(34)
        stat = self.ReadWord(35)
        w[0].valueWidget.setText(bin(65536 | val)[3:])
        w[0].statvalueWidget.setText('0x%04x' % stat)
        w[0].statusWidget.setText('mtt motor inputs')

        # 10: %MB96: spare inputs
        val = self.ReadWord(44)
        stat = self.ReadWord(45)
        w[1].valueWidget.setText(bin(65536 | val)[3:])
        w[1].statvalueWidget.setText('0x%04x' % stat)
        w[1].statusWidget.setText('spare inputs')

        # 17: %MB136: spare outputs
        val = self.ReadWord(59)
        target = self.ReadWord(60)
        stat = self.ReadWord(61)
        stati = Stati(stat)
        w[2].valueWidget.setText('%d' % val)
        w[2].statvalueWidget.setText('0x%04x' % stat)
        w[2].statusWidget.setText(', '.join(stati))
        w[2].targetWidget.setPlaceholderText('%d' % target)

        # 19: %MB146: enable code word
        val = self.ReadWord(73)
        w[3].valueWidget.setText('0x%04x' % val)
        w[3].targetWidget.setPlaceholderText('0x%04x' % val)

        # 20: %MB148: cycle counter
        val = self.ReadWord(74)
        w[4].valueWidget.setText('0x%04x' % val)

        # 13: %MB112: liftclamp
        val = self.ReadWord(50)
        target = self.ReadWord(51)
        stat = self.ReadWord(52)
        stati = Stati(stat)
        if (stat & 0x9000) == 0x9000:
            stati.append('ERR:Movement timed out')
        if stat & 0x0800:
            stati.append('ERR:liftclamp switches in Error')
        if stat & 0x0004:
            stati.append('No Air pressure')
        if stat & 0x0002:
            stati.append('ERR:Actuator Wire shorted!')
        if stat & 0x0001:
            stati.append('ERR:Actuator Wire open!')
        w[5].valueWidget.setText('%d' % val)
        w[5].statvalueWidget.setText('0x%04x' % stat)
        w[5].statusWidget.setText(', '.join(stati))
        w[5].targetWidget.setPlaceholderText('%d' % target)

        # 13: %MB112: liftclamp
        val = self.ReadWord(53)
        target = self.ReadWord(54)
        stat = self.ReadWord(55)
        stati = Stati(stat)
        if (stat & 0x9000) == 0x9000:
            stati.append('ERR:Movement timed out')
        if stat & 0x0800:
            stati.append('ERR:liftclamp switches in Error')
        if stat & 0x0004:
            stati.append('No Air pressure')
        if stat & 0x0002:
            stati.append('ERR:Actuator Wire shorted!')
        if stat & 0x0001:
            stati.append('ERR:Actuator Wire open!')
        w[6].valueWidget.setText('%d' % val)
        w[6].statvalueWidget.setText('0x%04x' % stat)
        w[6].statusWidget.setText(', '.join(stati))
        w[6].targetWidget.setPlaceholderText('%d' % target)

        # 7: %MB192: enc1
        val = self.ReadWord(46)
        stat = self.ReadWord(47)
        stati = Stati(stat)
        if stat & 0x0002:
            stati.append('ERR:Underflow!')
        if stat & 0x0001:
            stati.append('ERR:Overflow!')
        w[7].valueWidget.setText(str(val))
        w[7].statvalueWidget.setText('0x%04x' % stat)
        w[7].statusWidget.setText(', '.join(stati))

        # 7: %MB192: arm
        val = self.ReadWord(48)
        stat = self.ReadWord(49)
        stati = Stati(stat)
        if stat & 0x0002:
            stati.append('ERR:Underflow!')
        if stat & 0x0001:
            stati.append('ERR:Overflow!')
        w[8].valueWidget.setText(str(val))
        w[8].statvalueWidget.setText('0x%04x' % stat)
        w[8].statusWidget.setText(', '.join(stati))

        # 17: %MB136: spare outputs
        val = self.ReadWord(62)
        target = self.ReadWord(63)
        stat = self.ReadWord(64)
        stati = Stati(stat)
        w[9].valueWidget.setText('%d' % val)
        w[9].statvalueWidget.setText('0x%04x' % stat)
        w[9].statusWidget.setText(', '.join(stati))
        w[9].targetWidget.setPlaceholderText('%d' % target)

        # encoder
        val = self.ReadFloat(65)
        w[10].valueWidget.setText('%f' % val)

        # motro
        val = self.ReadFloat(68)
        target = self.ReadFloat(70)
        stat = self.ReadWord(72)
        stati = Stati(stat)
        w[11].valueWidget.setText('%f' % val)
        w[11].statvalueWidget.setText('0x%04x' % stat)
        w[11].statusWidget.setText(', '.join(stati))
        w[11].targetWidget.setPlaceholderText('%f' % target)

        # 13: %MB112: liftclamp
        val = self.ReadWord(56)
        target = self.ReadWord(57)
        stat = self.ReadWord(58)
        stati = Stati(stat)
        if (stat & 0x9000) == 0x9000:
            stati.append('ERR:Movement timed out')
        if stat & 0x0800:
            stati.append('ERR:liftclamp switches in Error')
        if stat & 0x0004:
            stati.append('No Air pressure')
        if stat & 0x0002:
            stati.append('ERR:Actuator Wire shorted!')
        if stat & 0x0001:
            stati.append('ERR:Actuator Wire open!')
        w[12].valueWidget.setText('%d' % val)
        w[12].statvalueWidget.setText('0x%04x' % stat)
        w[12].statusWidget.setText(', '.join(stati))
        w[12].targetWidget.setPlaceholderText('%d' % target)

        # 13: %MB112: air
        val = self.ReadWord(59)
        target = self.ReadWord(60)
        stat = self.ReadWord(61)
        stati = Stati(stat)
        if (stat & 0x9000) == 0x9000:
            stati.append('ERR:Movement timed out')
        if stat & 0x0800:
            stati.append('ERR:liftclamp switches in Error')
        if stat & 0x0004:
            stati.append('No Air pressure')
        if stat & 0x0002:
            stati.append('ERR:Actuator Wire shorted!')
        if stat & 0x0001:
            stati.append('ERR:Actuator Wire open!')
        w[13].valueWidget.setText('%d' % val)
        w[13].statvalueWidget.setText('0x%04x' % stat)
        w[13].statusWidget.setText(', '.join(stati))
        w[13].targetWidget.setPlaceholderText('%d' % target)

    def addWidget(self, which):
        which.setContentsMargins(10, 0, 0, 0)
        self.scrollLayout.addRow(which)
        l = QFrame()
        l.setLineWidth(1)
        # l.setMidLineWidth(4)
        l.setFrameShape(QFrame.HLine)
        l.setContentsMargins(10, 0, 10, 0)
        self.scrollLayout.addRow(l)
Esempio n. 28
0
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)

        # scroll area Widget contents - layout
        self.scrollLayout = QFormLayout()
        self.scrollLayout.setContentsMargins(0, 0, 0, 0)

        # scroll area Widget contents
        self.scrollWidget = QWidget()
        self.scrollWidget.setLayout(self.scrollLayout)

        # scroll area
        self.scrollArea = QScrollArea()
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setWidget(self.scrollWidget)

        # main layout
        self.mainLayout = QVBoxLayout()
        self.mainLayout.setSpacing(0)

        # add all main to the main vLayout
        self.mainLayout.addWidget(self.scrollArea)

        # central Widget
        self.centralWidget = QWidget()
        self.centralWidget.setLayout(self.mainLayout)

        # set central Widget
        self.setCentralWidget(self.centralWidget)

        try:
            self._bus = ModbusTcpClient('drum.panda.frm2')
            self._bus.connect()
            self._sync()
            print("Modbus synced!")
            print(self.ReadWord(0x20), self.ReadWord(0x21))
        except Exception as err:
            print("Modbus failed: %r, using demo mode!" % err)
            self._bus = None

        self._sync()

        widgets = []

        widgets.append(
            BaseDev(self, 'mtt motor inputs', 0, has_status=True, addr=34))

        widgets.append(
            BaseDev(self, 'spare inputs', 1, has_status=True, addr=44))
        widgets.append(
            BaseDev(self,
                    'spare outputs',
                    2,
                    has_status=True,
                    target='0x%04x' % self.ReadWord(60),
                    addr=59))
        widgets.append(
            BaseDev(self,
                    'enable_word',
                    3,
                    target='0x%04x' % self.ReadWord(73),
                    addr=73))
        widgets.append(BaseDev(self, 'cycle_counter', 4, addr=74))
        widgets.append(
            BaseDev(self,
                    'handle_cw',
                    5,
                    has_status=True,
                    target='%d' % self.ReadWord(51),
                    addr=50))
        widgets.append(
            BaseDev(self,
                    'handle_ccw',
                    6,
                    has_status=True,
                    target='%d' % self.ReadWord(54),
                    addr=53))
        widgets.append(BaseDev(self, 'enc1', 7, has_status=True, addr=46))
        widgets.append(BaseDev(self, 'enc2', 8, has_status=True, addr=48))
        widgets.append(
            BaseDev(self,
                    'arm_switch',
                    9,
                    has_status=True,
                    target='%d' % self.ReadWord(63),
                    addr=62))
        widgets.append(BaseDev(self, 'encoder1', 10, has_status=False,
                               addr=65))
        widgets.append(
            BaseDev(self,
                    'arm',
                    11,
                    has_status=True,
                    target='%f' % self.ReadFloat(70),
                    value_offset=2,
                    addr=68))
        widgets.append(
            BaseDev(self,
                    'magnet',
                    12,
                    has_status=True,
                    target='%d' % self.ReadWord(57),
                    addr=56))
        widgets.append(
            BaseDev(self,
                    'air',
                    13,
                    has_status=True,
                    target='%d' % self.ReadWord(60),
                    addr=59))

        for w in widgets:
            self.addWidget(w)

        widgets.sort(key=lambda w: w.index)
        self.widgets = widgets

        self.startTimer(225)  # in ms !
Esempio n. 29
0
    def __init__(self,
                 model,
                 name,
                 index,
                 addr,
                 has_status=False,
                 target=None,
                 value_offset=1):
        QWidget.__init__(self)
        self.index = index
        self.name = name
        self.model = model

        self.offset = value_offset
        self.has_status = has_status
        self.has_target = target is not None
        self.base_address = addr

        self._namelabel = QLabel(name)
        self._namelabel.setMinimumWidth(120)
        self._namelabel.setMaximumWidth(120)
        # self._groupbox = QGroupBox(name)
        self._groupbox = QFrame()
        # self._groupbox.setFlat(False)
        # self._groupbox.setCheckable(False)
        self._hlayout = QHBoxLayout()
        self._hlayout.addWidget(self._namelabel)
        self._hlayout.addWidget(self._groupbox)
        self._hlayout.setSpacing(0)

        # inside of the groupbox there is a vbox with 1 or 2 hboxes
        self._inner_vbox = QVBoxLayout()
        self._groupbox.setLayout(self._inner_vbox)

        # upper inner hbox
        self._inner_hbox1 = QHBoxLayout()
        self._inner_vbox.addLayout(self._inner_hbox1)

        # fill upper hbox
        self.valueWidget = QLineEdit('0b123456789abcdef0')
        self.valueWidget.setMaximumWidth(120)
        self._inner_hbox1.addWidget(self.valueWidget)

        if self.has_target:
            self.targetWidget = QLineEdit()
            self.targetWidget.setPlaceholderText(target)
            self.targetWidget.setMaximumWidth(120)
            self.targetWidget.returnPressed.connect(lambda *a: model.targeter(
                index,
                (self.targetWidget.text(), self.targetWidget.setText(''))[0]))
            self._inner_hbox1.addWidget(self.targetWidget)
            self.goButton = QPushButton('Go')
            self.goButton.clicked.connect(lambda *a: model.targeter(
                index,
                (self.targetWidget.text(), self.targetWidget.setText(''))[0]))
            self._inner_hbox1.addWidget(self.goButton)
            self.stopButton = QPushButton('Stop')
            self.stopButton.clicked.connect(lambda *a: model.stopper(index))
            self._inner_hbox1.addWidget(self.stopButton)

        # now (conditionally) the second hbox
        if has_status:
            self._inner_hbox2 = QHBoxLayout()
            self._inner_vbox.addLayout(self._inner_hbox2)

            self.statvalueWidget = QLineEdit('statval')
            self.statvalueWidget.setMaximumWidth(120)
            self._inner_hbox2.addWidget(self.statvalueWidget)
            self.statusWidget = QLineEdit('Statusstring if available')
            self.statusWidget.setMaximumWidth(10000)
            self._inner_hbox2.addWidget(self.statusWidget)
            self.resetButton = QPushButton('Reset')
            self.resetButton.clicked.connect(lambda *a: model.resetter(index))
            self._inner_hbox1.addWidget(self.resetButton)
            # self._inner_hbox2.addStretch(0.1)

        # allow space for resizing
        self._inner_hbox1.addStretch(1)

        self._inner_vbox.setSpacing(0)
        self._inner_vbox.setContentsMargins(0, 0, 0, 0)

        self._hlayout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self._hlayout)
        self.show()
Esempio n. 30
0
class MultiList(NicosWidget, QWidget):
    """A list of entries, where each entry is a frame loaded from a UI file."""

    designer_description = 'A list (with add/remove controls) of .ui entries'

    uifile = PropDef('uifile', str, '', 'UI file to use for the entries')

    entryAdded = pyqtSignal(object)
    entryRemoved = pyqtSignal(object)

    def __init__(self, parent, designMode=False, **kwds):
        QWidget.__init__(self, parent, **kwds)
        NicosWidget.__init__(self)
        self._entries = []
        self._vlayout = QVBoxLayout()
        self._vlayout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self._vlayout)
        self._add()
        self._designer = designMode

    def registerKeys(self):
        pass

    def propertyUpdated(self, pname, value):
        if pname == 'uifile':
            self.clear()
        NicosWidget.propertyUpdated(self, pname, value)

    def entry(self, i):
        return self._entries[i].subwidget

    def entries(self):
        return [e.subwidget for e in self._entries]

    def count(self):
        return len(self._entries)

    def clear(self):
        for entry in self._entries[::-1]:
            self._remove(entry)
        self._add()

    def _remove(self, entry):
        self._entries.remove(entry)
        self._vlayout.removeWidget(entry)
        self.entryRemoved.emit(entry.subwidget)
        entry.deleteLater()
        if self._entries:
            self._entries[-1].setButton('+')

    def _add(self):
        new_frame = MultiEntry(self, self._client, self.props['uifile'])
        new_frame.addOrRemove.connect(self._addRemove)
        if self._entries:
            self._entries[-1].setButton('-')
        self._entries.append(new_frame)
        self._vlayout.addWidget(new_frame)
        self.entryAdded.emit(self._entries[-1].subwidget)

    def _addRemove(self):
        if not self._entries or self.sender() is self._entries[-1]:
            self._add()
        else:
            self._remove(self.sender())