Example #1
0
class SpinBox(QtGui.QAbstractSpinBox):
    """
    **Bases:** QtGui.QAbstractSpinBox

    QSpinBox widget on steroids. Allows selection of numerical value, with extra features:

    - SI prefix notation (eg, automatically display "300 mV" instead of "0.003 V")
    - Float values with linear and decimal stepping (1-9, 10-90, 100-900, etc.)
    - Option for unbounded values
    - Delayed signals (allows multiple rapid changes with only one change signal)

    =============================  ==============================================
    **Signals:**
    valueChanged(value)            Same as QSpinBox; emitted every time the value
                                   has changed.
    sigValueChanged(self)          Emitted when value has changed, but also combines
                                   multiple rapid changes into one signal (eg,
                                   when rolling the mouse wheel).
    sigValueChanging(self, value)  Emitted immediately for all value changes.
    =============================  ==============================================
    """

    ## There's a PyQt bug that leaks a reference to the
    ## QLineEdit returned from QAbstractSpinBox.lineEdit()
    ## This makes it possible to crash the entire program
    ## by making accesses to the LineEdit after the spinBox has been deleted.
    ## I have no idea how to get around this..

    valueChanged = QtCore.Signal(
        object)  # (value)  for compatibility with QSpinBox
    sigValueChanged = QtCore.Signal(object)  # (self)
    sigValueChanging = QtCore.Signal(
        object, object)  # (self, value)  sent immediately; no delay.

    def __init__(self, parent=None, value=0.0, **kwargs):
        """
        ============== ========================================================================
        **Arguments:**
        parent         Sets the parent widget for this SpinBox (optional). Default is None.
        value          (float/int) initial value. Default is 0.0.
        bounds         (min,max) Minimum and maximum values allowed in the SpinBox.
                       Either may be None to leave the value unbounded. By default, values are unbounded.
        suffix         (str) suffix (units) to display after the numerical value. By default, suffix is an empty str.
        siPrefix       (bool) If True, then an SI prefix is automatically prepended
                       to the units and the value is scaled accordingly. For example,
                       if value=0.003 and suffix='V', then the SpinBox will display
                       "300 mV" (but a call to SpinBox.value will still return 0.003). Default is False.
        step           (float) The size of a single step. This is used when clicking the up/
                       down arrows, when rolling the mouse wheel, or when pressing
                       keyboard arrows while the widget has keyboard focus. Note that
                       the interpretation of this value is different when specifying
                       the 'dec' argument. Default is 0.01.
        dec            (bool) If True, then the step value will be adjusted to match
                       the current size of the variable (for example, a value of 15
                       might step in increments of 1 whereas a value of 1500 would
                       step in increments of 100). In this case, the 'step' argument
                       is interpreted *relative* to the current value. The most common
                       'step' values when dec=True are 0.1, 0.2, 0.5, and 1.0. Default is False.
        minStep        (float) When dec=True, this specifies the minimum allowable step size.
        int            (bool) if True, the value is forced to integer type. Default is False
        decimals       (int) Number of decimal values to display. Default is 2.
        readonly       (bool) If True, then mouse and keyboard interactions are caught and
                       will not produce a valueChanged signal, but the value can be still
                       changed programmatic via the setValue method. Default is False.
        ============== ========================================================================
        """
        QtGui.QAbstractSpinBox.__init__(self, parent)
        self.lastValEmitted = None
        self.lastText = ''
        self.textValid = True  ## If false, we draw a red border
        self.setMinimumWidth(0)
        self.setMaximumHeight(20)
        self.setSizePolicy(QtGui.QSizePolicy.Expanding,
                           QtGui.QSizePolicy.Preferred)
        self.opts = {
            'bounds': [None, None],

            ## Log scaling options   #### Log mode is no longer supported.
            #'step': 0.1,
            #'minStep': 0.001,
            #'log': True,
            #'dec': False,

            ## decimal scaling option - example
            #'step': 0.1,
            #'minStep': .001,
            #'log': False,
            #'dec': True,

            ## normal arithmetic step
            'step':
            D('0.01'
              ),  ## if 'dec' is false, the spinBox steps by 'step' every time
            ## if 'dec' is True, the step size is relative to the value
            ## 'step' needs to be an integral divisor of ten, ie 'step'*n=10 for some integer value of n (but only if dec is True)
            'log': False,
            'dec':
            False,  ## if true, does decimal stepping. ie from 1-10 it steps by 'step', from 10 to 100 it steps by 10*'step', etc.
            ## if true, minStep must be set in order to cross zero.
            'int': False,  ## Set True to force value to be integer
            'suffix': '',
            'siPrefix':
            False,  ## Set to True to display numbers with SI prefix (ie, 100pA instead of 1e-10A)
            'delay': 0.3,  ## delay sending wheel update signals for 300ms
            'delayUntilEditFinished':
            True,  ## do not send signals until text editing has finished

            ## for compatibility with QDoubleSpinBox and QSpinBox
            'decimals': 2,
            'readonly': False,
        }

        self.decOpts = ['step', 'minStep']
        self.val = D(asUnicode(
            value))  ## Value is precise decimal. Ordinary math not allowed.
        self.updateText()
        self.skipValidate = False
        self.setCorrectionMode(self.CorrectToPreviousValue)
        self.setKeyboardTracking(False)
        self.setOpts(**kwargs)

        self.editingFinished.connect(self.editingFinishedEvent)
        self.proxy = SignalProxy(self.sigValueChanging,
                                 slot=self.delayedChange,
                                 delay=self.opts['delay'])

    def event(self, ev):
        ret = QtGui.QAbstractSpinBox.event(self, ev)
        if ev.type() == QtCore.QEvent.KeyPress and ev.key(
        ) == QtCore.Qt.Key_Return:
            ret = True  ## For some reason, spinbox pretends to ignore return key press

        #Fix: introduce the Escape event, which is restoring the previous display.
        if ev.type() == QtCore.QEvent.KeyPress and ev.key(
        ) == QtCore.Qt.Key_Escape:
            self.updateText()
            ret = True
        return ret

    ##lots of config options, just gonna stuff 'em all in here rather than do the get/set crap.
    def setOpts(self, **opts):
        """
        Changes the behavior of the SpinBox. Accepts most of the arguments
        allowed in :func:`__init__ <pyqtgraph.SpinBox.__init__>`.

        """
        #print opts
        for k in opts:
            if k == 'bounds':
                #print opts[k]
                self.setMinimum(opts[k][0], update=False)
                self.setMaximum(opts[k][1], update=False)
                #for i in [0,1]:
                #if opts[k][i] is None:
                #self.opts[k][i] = None
                #else:
                #self.opts[k][i] = D(unicode(opts[k][i]))
            elif k in ['step', 'minStep']:
                self.opts[k] = D(asUnicode(opts[k]))
            elif k == 'value':
                pass  ## don't set value until bounds have been set
            else:
                self.opts[k] = opts[k]
        if 'value' in opts:
            self.setValue(opts['value'])

        ## If bounds have changed, update value to match
        if 'bounds' in opts and 'value' not in opts:
            self.setValue()

        ## sanity checks:
        if self.opts['int']:
            if 'step' in opts:
                step = opts['step']
                ## not necessary..
                #if int(step) != step:
                #raise Exception('Integer SpinBox must have integer step size.')
            else:
                self.opts['step'] = int(self.opts['step'])

            if 'minStep' in opts:
                step = opts['minStep']
                if int(step) != step:
                    raise Exception(
                        'Integer SpinBox must have integer minStep size.')
            else:
                ms = int(self.opts.get('minStep', 1))
                if ms < 1:
                    ms = 1
                self.opts['minStep'] = ms

        if 'delay' in opts:
            self.proxy.setDelay(opts['delay'])

        if 'readonly' in opts:
            self.opts['readonly'] = opts['readonly']

        self.updateText()

    #Fix: reimplement the methods for compatibility reasons to QSpinBox and QDoubleSpinBox methods
    def maximum(self):
        """ Reimplement the maximum functionality."""
        max_val = self.opts['bounds'][1]

        if self.opts['int'] and max_val is not None:
            return int(max_val)
        elif max_val is not None:
            return float(max_val)
        else:
            return max_val

    def setMaximum(self, m, update=True):
        """Set the maximum allowed value (or None for no limit)"""
        if m is not None:

            #FIX: insert the integer functionality:
            if self.opts['int']:
                m = int(m)

            m = D(asUnicode(m))
        self.opts['bounds'][1] = m
        if update:
            self.setValue()

    #Fix: reimplement the methods for compatibility reasons to QSpinBox and QDoubleSpinBox methods
    def minimum(self):
        """ Reimplement the minimum functionality."""

        min_val = self.opts['bounds'][0]

        if self.opts['int'] and min_val is not None:
            return int(min_val)
        elif min_val is not None:
            return float(min_val)
        else:
            return min_val

    def setMinimum(self, m, update=True):
        """Set the minimum allowed value (or None for no limit)"""
        if m is not None:

            #FIX: insert the integer functionality:
            if self.opts['int']:
                m = int(m)

            m = D(asUnicode(m))
        self.opts['bounds'][0] = m
        if update:
            self.setValue()

    def setPrefix(self, p):
        self.setOpts(prefix=p)

    def setRange(self, r0, r1):
        self.setOpts(bounds=[r0, r1])

    def setProperty(self, prop, val):
        ## for QSpinBox compatibility
        if prop == 'value':
            #if type(val) is QtCore.QVariant:
            #val = val.toDouble()[0]
            self.setValue(val)
        else:
            print("Warning: SpinBox.setProperty('{0!s}', ..) not supported.".
                  format(prop))

    def setSuffix(self, suf):
        self.setOpts(suffix=suf)

    def setSingleStep(self, step):
        self.setOpts(step=step)

    def setDecimals(self, decimals):
        self.setOpts(decimals=decimals)

    def selectNumber(self):
        """
        Select the numerical portion of the text to allow quick editing by the user.
        """
        le = self.lineEdit()
        text = asUnicode(le.text())
        if self.opts['suffix'] == '':
            le.setSelection(0, len(text))
        else:
            try:
                index = text.index(' ')
            except ValueError:
                return
            le.setSelection(0, index)

    #Fix: reimplement the methods for compatibility reasons to QSpinBox and QDoubleSpinBox methods
    def suffix(self):
        return self.opts['suffix']

    #Fix: reimplement the methods for compatibility reasons to QSpinBox and QDoubleSpinBox methods
    def prefix(self):
        return self.opts['prefix']

    def value(self):
        """
        Return the value of this SpinBox.

        """
        if self.opts['int']:
            return int(self.val)
        else:
            return float(self.val)

    def setValue(self, value=None, update=True, delaySignal=False):
        """
        Set the value of this spin.
        If the value is out of bounds, it will be clipped to the nearest boundary.
        If the spin is integer type, the value will be coerced to int.
        Returns the actual value set.

        If value is None, then the current value is used (this is for resetting
        the value after bounds, etc. have changed)
        """

        if value is None:
            value = self.value()

        bounds = self.opts['bounds']
        if bounds[0] is not None and value < bounds[0]:
            value = bounds[0]
        if bounds[1] is not None and value > bounds[1]:
            value = bounds[1]

        if self.opts['int']:
            value = int(value)

        value = D(asUnicode(value))
        if value == self.val:
            return
        prev = self.val

        self.val = value
        if update:
            self.updateText(prev=prev)

        self.sigValueChanging.emit(
            self, float(self.val)
        )  ## change will be emitted in 300ms if there are no subsequent changes.
        if not delaySignal:
            self.emitChanged()

        return value

    def emitChanged(self):
        self.lastValEmitted = self.val
        self.valueChanged.emit(float(self.val))
        self.sigValueChanged.emit(self)

    def delayedChange(self):
        try:
            if self.val != self.lastValEmitted:
                self.emitChanged()
        except RuntimeError:
            pass  ## This can happen if we try to handle a delayed signal after someone else has already deleted the underlying C++ object.

    def widgetGroupInterface(self):
        return (self.valueChanged, SpinBox.value, SpinBox.setValue)

    def sizeHint(self):
        return QtCore.QSize(120, 0)

    def stepEnabled(self):
        return self.StepUpEnabled | self.StepDownEnabled

    #def fixup(self, *args):
    #print "fixup:", args

    def stepBy(self, n):
        n = D(int(n))  ## n must be integral number of steps.
        s = [D(-1), D(1)][n >= 0]  ## determine sign of step
        val = self.val

        for i in range(int(abs(n))):

            if self.opts['log']:
                raise Exception("Log mode no longer supported.")
            #    step = abs(val) * self.opts['step']
            #    if 'minStep' in self.opts:
            #        step = max(step, self.opts['minStep'])
            #    val += step * s
            if self.opts['dec']:
                if val == 0:
                    step = self.opts['minStep']
                    exp = None
                else:
                    vs = [D(-1), D(1)][val >= 0]
                    #exp = D(int(abs(val*(D('1.01')**(s*vs))).log10()))
                    fudge = D('1.01')**(
                        s * vs
                    )  ## fudge factor. at some places, the step size depends on the step sign.
                    exp = abs(val * fudge).log10().quantize(1, ROUND_FLOOR)
                    step = self.opts['step'] * D(10)**exp
                if 'minStep' in self.opts:
                    step = max(step, self.opts['minStep'])
                val += s * step
                #print "Exp:", exp, "step", step, "val", val
            else:
                val += s * self.opts['step']

            if 'minStep' in self.opts and abs(val) < self.opts['minStep']:
                val = D(0)
        self.setValue(
            val, delaySignal=True
        )  ## note all steps (arrow buttons, wheel, up/down keys..) emit delayed signals only.

    def valueInRange(self, value):
        bounds = self.opts['bounds']
        if bounds[0] is not None and value < bounds[0]:
            return False
        if bounds[1] is not None and value > bounds[1]:
            return False
        if self.opts.get('int', False):
            if int(value) != value:
                return False
        return True

    def updateText(self, prev=None):
        # print("Update text.")

        self.skipValidate = True
        if self.opts['siPrefix']:
            if self.val == 0 and prev is not None:
                (s, p) = fn.siScale(prev)
                txt = "0.0 {0!s}{1!s}".format(p, self.opts['suffix'])
            else:
                txt = fn.siFormat(float(self.val),
                                  precision=self.opts['decimals'] + 1,
                                  suffix=self.opts['suffix'])
        else:
            txt = '{0:.14g}{1!s}'.format(self.val, self.opts['suffix'])
        self.lineEdit().setText(txt)
        self.lastText = txt
        self.skipValidate = False

    def validate(self, strn, pos):
        # print('validate', strn, pos)
        if self.skipValidate:
            # print("skip validate")
            #self.textValid = False
            ret = QtGui.QValidator.Acceptable
        else:
            try:
                ## first make sure we didn't mess with the suffix
                suff = self.opts.get('suffix', '')

                # fix: if the whole text is selected and one needs to typ in a
                #      new number, then a single integer character is ignored.
                if len(strn) == 1 and strn.isdigit():
                    scl_str = fn.siScale(self.val)[1]
                    strn = '{0} {1}{2}'.format(strn, scl_str, suff)

                if len(suff) > 0 and asUnicode(strn)[-len(suff):] != suff:
                    #print '"%s" != "%s"' % (unicode(strn)[-len(suff):], suff)
                    ret = QtGui.QValidator.Invalid
                    # print('invalid input', 'suff:', suff, '{0} != {1}'.format(asUnicode(strn)[-len(suff):], suff))

                ## next see if we actually have an interpretable value
                else:
                    val = self.interpret()
                    if val is False:
                        #print "can't interpret"
                        #self.setStyleSheet('SpinBox {border: 2px solid #C55;}')
                        #self.textValid = False
                        ret = QtGui.QValidator.Intermediate
                    else:
                        if self.valueInRange(val):
                            if not self.opts['delayUntilEditFinished']:
                                self.setValue(val, update=False)
                            #print "  OK:", self.val
                            #self.setStyleSheet('')
                            #self.textValid = True

                            ret = QtGui.QValidator.Acceptable
                        else:
                            ret = QtGui.QValidator.Intermediate

            except:
                #print "  BAD"
                #import sys
                #sys.excepthook(*sys.exc_info())
                #self.textValid = False
                #self.setStyleSheet('SpinBox {border: 2px solid #C55;}')
                ret = QtGui.QValidator.Intermediate

        ## draw / clear border
        if ret == QtGui.QValidator.Intermediate:
            self.textValid = False
        elif ret == QtGui.QValidator.Acceptable:
            self.textValid = True
        ## note: if text is invalid, we don't change the textValid flag
        ## since the text will be forced to its previous state anyway
        self.update()

        ## support 2 different pyqt APIs. Bleh.
        if hasattr(QtCore, 'QString'):
            return (ret, pos)
        else:
            return (ret, strn, pos)

    def paintEvent(self, ev):
        QtGui.QAbstractSpinBox.paintEvent(self, ev)

        ## draw red border if text is invalid
        if not self.textValid:
            p = QtGui.QPainter(self)
            p.setRenderHint(p.Antialiasing)
            p.setPen(fn.mkPen((200, 50, 50), width=2))
            p.drawRoundedRect(self.rect().adjusted(2, 2, -2, -2), 4, 4)
            p.end()

    def interpret(self):
        """Return value of text. Return False if text is invalid, raise exception if text is intermediate"""
        strn = self.lineEdit().text()

        #Fix: strip leading blank characters, which produce errors:
        strn = strn.lstrip()

        suf = self.opts['suffix']
        if len(suf) > 0:
            if strn[-len(suf):] != suf:
                return False
            #raise Exception("Units are invalid.")
            strn = strn[:-len(suf)]
        try:
            val = fn.siEval(strn)
        except:
            #sys.excepthook(*sys.exc_info())
            #print "invalid"
            return False
        #print val
        return val

    #def interpretText(self, strn=None):
    #print "Interpret:", strn
    #if strn is None:
    #strn = self.lineEdit().text()
    #self.setValue(siEval(strn), update=False)
    ##QtGui.QAbstractSpinBox.interpretText(self)

    def editingFinishedEvent(self):
        """Edit has finished; set value."""
        #print "Edit finished."
        if asUnicode(self.lineEdit().text()) == self.lastText:
            #print "no text change."
            return
        try:
            val = self.interpret()
        except:
            return

        if val is False:
            #print "value invalid:", str(self.lineEdit().text())
            return
        if val == self.val:
            #print "no value change:", val, self.val
            return
        self.setValue(
            val, delaySignal=False
        )  ## allow text update so that values are reformatted pretty-like

    #def textChanged(self):
    #print "Text changed."

### Drop-in replacement for SpinBox; just for crash-testing
#class SpinBox(QtGui.QDoubleSpinBox):
#valueChanged = QtCore.Signal(object)     # (value)  for compatibility with QSpinBox
#sigValueChanged = QtCore.Signal(object)  # (self)
#sigValueChanging = QtCore.Signal(object)  # (value)
#def __init__(self, parent=None, *args, **kargs):
#QtGui.QSpinBox.__init__(self, parent)

#def  __getattr__(self, attr):
#return lambda *args, **kargs: None

#def widgetGroupInterface(self):
#return (self.valueChanged, SpinBox.value, SpinBox.setValue)

    def isReadOnly(self):
        """ Overwrite the QAbstractSpinBox method to obtain the ReadOnly state.
        """
        return self.opts['readonly']

    def mousePressEvent(self, event):
        """ Handle what happens on press event of the mouse.

        @param event: QEvent of a Mouse Release action
        """

        if self.isReadOnly():
            event.accept()
        else:
            super(SpinBox, self).mousePressEvent(event)

    # Comment out this method, since it is called, if QToolTip is going to be
    # displayed. You would not see any QTooltip if you catch that signal.
    # def mouseMoveEvent(self, event):
    #     """ Handle what happens on move (over) event of the mouse.
    #
    #     @param event: QEvent of a Mouse Move action
    #     """
    #
    #     if ( self.isReadOnly() ):
    #         event.accept()
    #     else:
    #         super(SpinBox, self).mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        """ Handle what happens on release of the mouse.

        @param event: QEvent of a Mouse Release action
        """

        if self.isReadOnly():
            event.accept()
        else:
            super(SpinBox, self).mouseReleaseEvent(event)

    # Handle event in which the widget has focus and the spacebar is pressed.
    def keyPressEvent(self, event):
        """ Handle what happens on keypress.

        @param event: QEvent of a key press Action
        """

        if self.isReadOnly():
            event.accept()
        else:
            super(SpinBox, self).keyPressEvent(event)

    def wheelEvent(self, event):
        """ Handle what happens on a wheel event of the mouse.

        @param event: QEvent of a Mouse Wheel event
        """

        if self.isReadOnly():
            event.accept()
        else:
            super(SpinBox, self).wheelEvent(event)

    @QtCore.pyqtSlot(bool)
    def setReadOnly(self, state):
        """ Overwrite the QAbstractSpinBox method to set the ReadOnly state.

        @param bool state: True or False, for having a readonly QRadioButton.

        Important, declare that slot as a qt slot to provide a C++ signature for
        that. The advantage is less memory consumption and slightly faster
        performance.
        Note: Not using the slot decorator causes that the signal connection
        mechanism is forced to work out manually the type conversion to map from
        the underlying C++ function signatures to the Python functions. When the
        slot decorators are used, the type mapping can be explicit!
        """

        self.setOpts(readonly=state)

    readOnly = QtCore.pyqtProperty(bool, isReadOnly, setReadOnly)
circle2=pg.CircleROI((0,0), size=(5,5),pen={'color':1,'width':3})
circle2.setParentItem(curvePoint2)
circle3=pg.CircleROI((0,0), size=(5,5),pen={'color':"7F00FF",'width':3})
circle3.setParentItem(curvePoint3)

def update():
    global curvePoint,curvePoint2,curvePoint3, index,index2,index3
    index = (index + 1) % len(orbitX)
    index2=(index2+1) % len(x)
    index3=(index3+1) % len(middleX)
    curvePoint.setPos(float(index)/(len(orbitX)-1))
    curvePoint2.setPos(float(index2)/(len(x)-1))
    curvePoint3.setPos(float(index3)/(len(middleX)-1))
   
    text2.setText('[%0.1f, %0.1f]' % (orbitX[index], orbitY[index]))
    text1.setText('[%0.1f, %0.1f]' % (x[index2], y[index2]))
    text3.setText('[%0.1f, %0.1f]' % (middleX[index3],middleY[index3]))

    


timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(20)



## Start Qt event loop unless running in interactive mode or using pyside.
QtGui.QApplication.instance().exec_()

Example #3
0
 def resizeEvent(self, event):
     super(ParentGraphicsView, self).resizeEvent(event)
     if event != None:
         self.emit(QtCore.SIGNAL("resize"), event)
Example #4
0
angles = [
	((1, 0), (0, 1), 90),
	((0, 1), (1, 0), -90),
	((-1, 0), (-1, 0), 0),
	((0, -1), (0, 1), 180),
]
@pytest.mark.parametrize("p1, p2, angle", angles)
def test_Point_angle(p1, p2, angle):
	p1 = pg.Point(*p1)
	p2 = pg.Point(*p2)
	assert p2.angle(p1) == angle


inits = [
	(QtCore.QSizeF(1, 0), (1.0, 0.0)),
	((0, -1), (0.0, -1.0)),
	([1, 1], (1.0, 1.0)),
]
@pytest.mark.parametrize("initArgs, positions", inits)
def test_Point_init(initArgs, positions):
	if isinstance(initArgs, QtCore.QSizeF):
		point = pg.Point(initArgs)
	else:
		point = pg.Point(*initArgs)
	assert (point.x(), point.y()) == positions

lengths = [
	((0, 1), 1),
	((1, 0), 1),
	((0, 0), 0),
Example #5
0
    def ori_plots(self):
        """
            Screen: ori_plots
            Desc: Shows each channel for MYO orientation data on separate 
                plots. Also allows for recording of data.
        """
        # Closes previous plot widget
        self.cw.close()

        # Creates new plot widget and sets layout for orientation plots
        self.cw = QtGui.QWidget()
        self.setCentralWidget(self.cw)
        self.l = QtGui.QGridLayout()
        self.cw.setLayout(self.l)

        # Creates plot widget title label
        titleLabel = QtGui.QLabel('Orientation Plots')
        titleLabel.setFont(QtGui.QFont("Garamond", 16, QtGui.QFont.Bold))
        self.l.addWidget(titleLabel, 0, 0, 2, 1)

        # Creates button to go back to all plots main screen
        all_button = QtGui.QPushButton('All Plots')
        all_button.setToolTip('Open all plots.')
        all_button.clicked.connect(self.revert_to_all_plots)
        self.l.addWidget(all_button, 0, 0, 1, 1)

        # Creates button to start recording data
        record = QtGui.QPushButton('Record Data')
        record.setToolTip('Record MYO Data')
        record.setStyleSheet("background-color: green")
        record.clicked.connect(self.toggle_record)
        self.record = record
        self.l.addWidget(record, 1, 0, 1, 1)

        save_plot_data = QtGui.QPushButton('Save Plotted Data')
        save_plot_data.setToolTip('Save MYO data seen on plots')
        save_plot_data.setStyleSheet('background-color: green')
        save_plot_data.clicked.connect(self.record_plot_data)
        self.save_plot_data = save_plot_data
        self.l.addWidget(save_plot_data, 2, 0, 1, 1)

        # Creates list for orientation channel plots
        self.oriplot_channels = []
        self.oricurve = []
        for i in range(ORI_RANGE):
            # Creates plot widget
            self.oriplot_channels.append(
                pg.PlotWidget(name='Orientation Channel %s' % (i + 1)))

            # Sets range and title for plot
            self.oriplot_channels[-1].setRange(QtCore.QRectF(0, -1, 1000, 8))
            self.oriplot_channels[-1].disableAutoRange()
            self.oriplot_channels[-1].setTitle("Orientation Channel %s" %
                                               (i + 1))

            # Adds widget to screen
            self.l.addWidget(self.oriplot_channels[-1], i, 1, 1, 1)

            # Gets orientation buffer data and puts on plot
            c = self.oriplot_channels[i].plot(pen=(i, 10))
            c.setPos(0, 0)
            self.oricurve.append(c)

        # Creates button to go back to all plots main screen
        record_config = QtGui.QPushButton('Record Configuration')
        record_config.setToolTip('Configure recording options.')
        record_config.clicked.connect(self.record_settings)
        self.l.addWidget(record_config, 4, 0, 1, 1)
        #self.record_settings()

        self.settingsTick = QtGui.QLabel(self.get_settings_tick())
        self.settingsTick.setFont(QtGui.QFont("Garamond", 10,
                                              QtGui.QFont.Bold))
        self.l.addWidget(self.settingsTick, 4, 1, 2, 1)

        self.lastUpdateTime = time.time()

        # Sets data update variable
        self.window_type = 'ori'
Example #6
0
    def all_plots(self):
        """
            Screen: snip_all_plots
            Desc: Shows EMG, acceleration and orientation plots for raw
                MYO record. All channels for like-fields are shown on same
                plot (8 channels for EMG, 3 channels for acceleration and
                4 channels for orientation).
        """
        # Creates QtGUI widget and sets layout
        self.cw = QtGui.QWidget()
        self.setCentralWidget(self.cw)
        l = QtGui.QGridLayout()
        self.cw.setLayout(l)
        print('Layout set')
        # Creates inner widget label for screen title
        titleLabel = QtGui.QLabel('MYO Record Snipper')
        titleLabel.setFont(QtGui.QFont("Garamond", 16, QtGui.QFont.Bold))
        l.addWidget(titleLabel, 0, 0, 1, 2)

        # Shows EMG plot
        self.emgrecord = pg.PlotWidget(name='EMGRecordPlot')
        self.emgrecord.setRange(QtCore.QRectF(0, -200, self.sample_size, 700))
        self.emgrecord.disableAutoRange()
        self.emgrecord.setTitle("EMG Record")
        l.addWidget(self.emgrecord, 0, 3, 1, 2)
        print('Emg plot set')

        # Shows acceleration plot
        self.accrecord = pg.PlotWidget(name='ACCRecordPlot')
        self.accrecord.setRange(QtCore.QRectF(0, -2, self.sample_size, 7))
        self.accrecord.setTitle("Accelerometer Record")
        l.addWidget(self.accrecord, 1, 3, 1, 2)
        print('Acc plot set')

        # Shows orientation plot
        self.orirecord = pg.PlotWidget(name='ORIRecordPlot')
        self.orirecord.setRange(QtCore.QRectF(0, -1, self.sample_size, 8))
        self.orirecord.setTitle("Orientation Record")
        l.addWidget(self.orirecord, 2, 3, 1, 2)
        print('Ori plot set')

        # Creates list for EMG recordings then starts recording buffer
        # readings. Plots EMG buffer data on plot as well.
        self.record_emgcurve = []
        for i in range(EMG_RANGE):
            c = self.emgrecord.plot(pen=(i, 10))
            c.setPos(0, i * 50)
            self.record_emgcurve.append(c)
            self.record_emgcurve[-1].setData(self.emg[i])
        print('Emg curve')

        # Creates list for acceleration recordings then starts recording buffer
        # readings. Plots acceleration buffer data on plot as well.
        self.record_oricurve = []
        for i in range(ORI_RANGE):
            c = self.orirecord.plot(pen=(i, 5))
            c.setPos(0, i * 2)
            self.record_oricurve.append(c)
            self.record_oricurve[-1].setData(self.ori[i])
        print('Ori curve')
        # Creates list for orientation recordings then starts recording buffer
        # readings. Plots acceleration buffer data on plot as well.
        self.record_acccurve = []
        for i in range(ACC_RANGE):
            c = self.accrecord.plot(pen=(i, 5))
            c.setPos(0, i * 2)
            self.record_acccurve.append(c)
            self.record_acccurve[-1].setData(self.acc[i])
        print('Acc curve')

        begin_label = QtGui.QLabel('Starting index to snip from')
        l.addWidget(begin_label, 1, 0, 1, 1)

        self.begin = QtGui.QLineEdit()
        self.begin.setText('0')
        self.begin.textChanged.connect(self.update_region)
        l.addWidget(self.begin, 1, 1, 1, 1)

        end_label = QtGui.QLabel('Ending index to snip to')
        l.addWidget(end_label, 2, 0, 1, 1)

        self.end = QtGui.QLineEdit()
        self.end.setText(str(self.sample_size))
        self.end.textChanged.connect(self.update_region)
        l.addWidget(self.end, 2, 1, 1, 1)

        reset_plot_range = QtGui.QPushButton('Reset Waveform')
        reset_plot_range.setToolTip('Reset xRange')
        reset_plot_range.clicked.connect(self.resetEvent)
        self.reset_plot_range = reset_plot_range
        l.addWidget(reset_plot_range, 3, 0, 1, 2)

        save_plot_range = QtGui.QPushButton('Save Waveform')
        save_plot_range.setToolTip('Save to CSV')
        save_plot_range.clicked.connect(self.saveEvent)
        self.save_plot_range = save_plot_range
        l.addWidget(save_plot_range, 3, 4, 1, 1)

        open_new_file = QtGui.QPushButton('Open File')
        open_new_file.setToolTip('Open New CSV')
        open_new_file.clicked.connect(self.openEvent)
        self.open_new_file = open_new_file
        l.addWidget(open_new_file, 4, 2, 1, 2)

        if self.main:
            self.show()
Example #7
0
    def childrenBoundingRect(self, frac=None):
        """Return the bounding range of all children.
        [[xmin, xmax], [ymin, ymax]]
        Values may be None if there are no specific bounds for an axis.
        """

        #items = self.allChildren()
        items = self.addedItems

        #if item is None:
        ##print "children bounding rect:"
        #item = self.childGroup

        range = [None, None]

        for item in items:
            if not item.isVisible():
                continue

            #print "=========", item
            useX = True
            useY = True
            if hasattr(item, 'dataBounds'):
                if frac is None:
                    frac = (1.0, 1.0)
                xr = item.dataBounds(0, frac=frac[0])
                yr = item.dataBounds(1, frac=frac[1])
                if xr is None or xr == (None, None):
                    useX = False
                    xr = (0, 0)
                if yr is None or yr == (None, None):
                    useY = False
                    yr = (0, 0)

                bounds = QtCore.QRectF(xr[0], yr[0], xr[1] - xr[0],
                                       yr[1] - yr[0])
                #print "   item real:", bounds
            else:
                if int(item.flags() & item.ItemHasNoContents) > 0:
                    continue
                    #print "   empty"
                else:
                    bounds = item.boundingRect()
                    #bounds = [[item.left(), item.top()], [item.right(), item.bottom()]]
                #print "   item:", bounds
            #bounds = QtCore.QRectF(bounds[0][0], bounds[1][0], bounds[0][1]-bounds[0][0], bounds[1][1]-bounds[1][0])
            bounds = self.mapFromItemToView(item, bounds).boundingRect()
            #print "    ", bounds

            if not any([useX, useY]):
                continue

            if useX != useY:  ##   !=  means  xor
                ang = item.transformAngle()
                if ang == 0 or ang == 180:
                    pass
                elif ang == 90 or ang == 270:
                    tmp = useX
                    useY = useX
                    useX = tmp
                else:
                    continue  ## need to check for item rotations and decide how best to apply this boundary.

            if useY:
                if range[1] is not None:
                    range[1] = [
                        min(bounds.top(), range[1][0]),
                        max(bounds.bottom(), range[1][1])
                    ]
                    #bounds.setTop(min(bounds.top(), chb.top()))
                    #bounds.setBottom(max(bounds.bottom(), chb.bottom()))
                else:
                    range[1] = [bounds.top(), bounds.bottom()]
                    #bounds.setTop(chb.top())
                    #bounds.setBottom(chb.bottom())
            if useX:
                if range[0] is not None:
                    range[0] = [
                        min(bounds.left(), range[0][0]),
                        max(bounds.right(), range[0][1])
                    ]
                    #bounds.setLeft(min(bounds.left(), chb.left()))
                    #bounds.setRight(max(bounds.right(), chb.right()))
                else:
                    range[0] = [bounds.left(), bounds.right()]
                    #bounds.setLeft(chb.left())
                    #bounds.setRight(chb.right())

        tr = self.targetRange()
        if range[0] is None:
            range[0] = tr[0]
        if range[1] is None:
            range[1] = tr[1]

        bounds = QtCore.QRectF(range[0][0], range[1][0],
                               range[0][1] - range[0][0],
                               range[1][1] - range[1][0])
        return bounds
Example #8
0
class Flowchart(Node):

    sigFileLoaded = QtCore.Signal(object)
    sigFileSaved = QtCore.Signal(object)

    #sigOutputChanged = QtCore.Signal() ## inherited from Node
    sigChartLoaded = QtCore.Signal()
    sigStateChanged = QtCore.Signal()

    def __init__(self, terminals=None, name=None, filePath=None):
        if name is None:
            name = "Flowchart"
        if terminals is None:
            terminals = {}
        self.filePath = filePath
        Node.__init__(
            self, name, allowAddInput=True, allowAddOutput=True
        )  ## create node without terminals; we'll add these later

        self.inputWasSet = False  ## flag allows detection of changes in the absence of input change.
        self._nodes = {}
        self.nextZVal = 10
        #self.connects = []
        #self._chartGraphicsItem = FlowchartGraphicsItem(self)
        self._widget = None
        self._scene = None
        self.processing = False  ## flag that prevents recursive node updates

        self.widget()

        self.inputNode = Node('Input', allowRemove=False, allowAddOutput=True)
        self.outputNode = Node('Output', allowRemove=False, allowAddInput=True)
        self.addNode(self.inputNode, 'Input', [-150, 0])
        self.addNode(self.outputNode, 'Output', [300, 0])

        self.outputNode.sigOutputChanged.connect(self.outputChanged)
        self.outputNode.sigTerminalRenamed.connect(
            self.internalTerminalRenamed)
        self.inputNode.sigTerminalRenamed.connect(self.internalTerminalRenamed)
        self.outputNode.sigTerminalRemoved.connect(
            self.internalTerminalRemoved)
        self.inputNode.sigTerminalRemoved.connect(self.internalTerminalRemoved)
        self.outputNode.sigTerminalAdded.connect(self.internalTerminalAdded)
        self.inputNode.sigTerminalAdded.connect(self.internalTerminalAdded)

        self.viewBox.autoRange(padding=0.04)

        for name, opts in terminals.items():
            self.addTerminal(name, **opts)

    def setInput(self, **args):
        """Set the input values of the flowchart. This will automatically propagate
        the new values throughout the flowchart, (possibly) causing the output to change.
        """
        #print "setInput", args
        #Node.setInput(self, **args)
        #print "  ....."
        self.inputWasSet = True
        self.inputNode.setOutput(**args)

    def outputChanged(self):
        ## called when output of internal node has changed
        vals = self.outputNode.inputValues()
        self.widget().outputChanged(vals)
        self.setOutput(**vals)
        #self.sigOutputChanged.emit(self)

    def output(self):
        """Return a dict of the values on the Flowchart's output terminals.
        """
        return self.outputNode.inputValues()

    def nodes(self):
        return self._nodes

    def addTerminal(self, name, **opts):
        term = Node.addTerminal(self, name, **opts)
        name = term.name()
        if opts['io'] == 'in':  ## inputs to the flowchart become outputs on the input node
            opts['io'] = 'out'
            opts['multi'] = False
            self.inputNode.sigTerminalAdded.disconnect(
                self.internalTerminalAdded)
            try:
                term2 = self.inputNode.addTerminal(name, **opts)
            finally:
                self.inputNode.sigTerminalAdded.connect(
                    self.internalTerminalAdded)

        else:
            opts['io'] = 'in'
            #opts['multi'] = False
            self.outputNode.sigTerminalAdded.disconnect(
                self.internalTerminalAdded)
            try:
                term2 = self.outputNode.addTerminal(name, **opts)
            finally:
                self.outputNode.sigTerminalAdded.connect(
                    self.internalTerminalAdded)
        return term

    def removeTerminal(self, name):
        #print "remove:", name
        term = self[name]
        inTerm = self.internalTerminal(term)
        Node.removeTerminal(self, name)
        inTerm.node().removeTerminal(inTerm.name())

    def internalTerminalRenamed(self, term, oldName):
        self[oldName].rename(term.name())

    def internalTerminalAdded(self, node, term):
        if term._io == 'in':
            io = 'out'
        else:
            io = 'in'
        Node.addTerminal(self,
                         term.name(),
                         io=io,
                         renamable=term.isRenamable(),
                         removable=term.isRemovable(),
                         multiable=term.isMultiable())

    def internalTerminalRemoved(self, node, term):
        try:
            Node.removeTerminal(self, term.name())
        except KeyError:
            pass

    def terminalRenamed(self, term, oldName):
        newName = term.name()
        #print "flowchart rename", newName, oldName
        #print self.terminals
        Node.terminalRenamed(self, self[oldName], oldName)
        #print self.terminals
        for n in [self.inputNode, self.outputNode]:
            if oldName in n.terminals:
                n[oldName].rename(newName)

    def createNode(self, nodeType, name=None, pos=None):
        if name is None:
            n = 0
            while True:
                name = "%s.%d" % (nodeType, n)
                if name not in self._nodes:
                    break
                n += 1

        node = library.getNodeType(nodeType)(name)
        self.addNode(node, name, pos)
        return node

    def addNode(self, node, name, pos=None):
        if pos is None:
            pos = [0, 0]
        if type(pos) in [QtCore.QPoint, QtCore.QPointF]:
            pos = [pos.x(), pos.y()]
        item = node.graphicsItem()
        item.setZValue(self.nextZVal * 2)
        self.nextZVal += 1
        self.viewBox.addItem(item)
        item.moveBy(*pos)
        self._nodes[name] = node
        self.widget().addNode(node)
        node.sigClosed.connect(self.nodeClosed)
        node.sigRenamed.connect(self.nodeRenamed)
        node.sigOutputChanged.connect(self.nodeOutputChanged)

    def removeNode(self, node):
        node.close()

    def nodeClosed(self, node):
        del self._nodes[node.name()]
        self.widget().removeNode(node)
        try:
            node.sigClosed.disconnect(self.nodeClosed)
        except TypeError:
            pass
        try:
            node.sigRenamed.disconnect(self.nodeRenamed)
        except TypeError:
            pass
        try:
            node.sigOutputChanged.disconnect(self.nodeOutputChanged)
        except TypeError:
            pass

    def nodeRenamed(self, node, oldName):
        del self._nodes[oldName]
        self._nodes[node.name()] = node
        self.widget().nodeRenamed(node, oldName)

    def arrangeNodes(self):
        pass

    def internalTerminal(self, term):
        """If the terminal belongs to the external Node, return the corresponding internal terminal"""
        if term.node() is self:
            if term.isInput():
                return self.inputNode[term.name()]
            else:
                return self.outputNode[term.name()]
        else:
            return term

    def connectTerminals(self, term1, term2):
        """Connect two terminals together within this flowchart."""
        term1 = self.internalTerminal(term1)
        term2 = self.internalTerminal(term2)
        term1.connectTo(term2)

    def process(self, **args):
        """
        Process data through the flowchart, returning the output.
        
        Keyword arguments must be the names of input terminals. 
        The return value is a dict with one key per output terminal.
        
        """
        data = {}  ## Stores terminal:value pairs

        ## determine order of operations
        ## order should look like [('p', node1), ('p', node2), ('d', terminal1), ...]
        ## Each tuple specifies either (p)rocess this node or (d)elete the result from this terminal
        order = self.processOrder()
        #print "ORDER:", order

        ## Record inputs given to process()
        for n, t in self.inputNode.outputs().items():
            if n not in args:
                raise Exception(
                    "Parameter %s required to process this chart." % n)
            data[t] = args[n]

        ret = {}

        ## process all in order
        for c, arg in order:

            if c == 'p':  ## Process a single node
                #print "===> process:", arg
                node = arg
                if node is self.inputNode:
                    continue  ## input node has already been processed.

                ## get input and output terminals for this node
                outs = list(node.outputs().values())
                ins = list(node.inputs().values())

                ## construct input value dictionary
                args = {}
                for inp in ins:
                    inputs = inp.inputTerminals()
                    if len(inputs) == 0:
                        continue
                    if inp.isMultiValue(
                    ):  ## multi-input terminals require a dict of all inputs
                        args[inp.name()] = dict([(i, data[i]) for i in inputs])
                    else:  ## single-inputs terminals only need the single input value available
                        args[inp.name()] = data[inputs[0]]

                if node is self.outputNode:
                    ret = args  ## we now have the return value, but must keep processing in case there are other endpoint nodes in the chart
                else:
                    try:
                        if node.isBypassed():
                            result = node.processBypassed(args)
                        else:
                            result = node.process(display=False, **args)
                    except:
                        print("Error processing node %s. Args are: %s" %
                              (str(node), str(args)))
                        raise
                    for out in outs:
                        #print "    Output:", out, out.name()
                        #print out.name()
                        try:
                            data[out] = result[out.name()]
                        except:
                            print(out, out.name())
                            raise
            elif c == 'd':  ## delete a terminal result (no longer needed; may be holding a lot of memory)
                #print "===> delete", arg
                if arg in data:
                    del data[arg]

        return ret

    def processOrder(self):
        """Return the order of operations required to process this chart.
        The order returned should look like [('p', node1), ('p', node2), ('d', terminal1), ...] 
        where each tuple specifies either (p)rocess this node or (d)elete the result from this terminal
        """

        ## first collect list of nodes/terminals and their dependencies
        deps = {}
        tdeps = {}  ## {terminal: [nodes that depend on terminal]}
        for name, node in self._nodes.items():
            deps[node] = node.dependentNodes()
            for t in node.outputs().values():
                tdeps[t] = t.dependentNodes()

        #print "DEPS:", deps
        ## determine correct node-processing order
        #deps[self] = []
        order = toposort(deps)
        #print "ORDER1:", order

        ## construct list of operations
        ops = [('p', n) for n in order]

        ## determine when it is safe to delete terminal values
        dels = []
        for t, nodes in tdeps.items():
            lastInd = 0
            lastNode = None
            for n in nodes:  ## determine which node is the last to be processed according to order
                if n is self:
                    lastInd = None
                    break
                else:
                    try:
                        ind = order.index(n)
                    except ValueError:
                        continue
                if lastNode is None or ind > lastInd:
                    lastNode = n
                    lastInd = ind
            #tdeps[t] = lastNode
            if lastInd is not None:
                dels.append((lastInd + 1, t))
        dels.sort(lambda a, b: cmp(b[0], a[0]))
        for i, t in dels:
            ops.insert(i, ('d', t))

        return ops

    def nodeOutputChanged(self, startNode):
        """Triggered when a node's output values have changed. (NOT called during process())
        Propagates new data forward through network."""
        ## first collect list of nodes/terminals and their dependencies

        if self.processing:
            return
        self.processing = True
        try:
            deps = {}
            for name, node in self._nodes.items():
                deps[node] = []
                for t in node.outputs().values():
                    deps[node].extend(t.dependentNodes())

            ## determine order of updates
            order = toposort(deps, nodes=[startNode])
            order.reverse()

            ## keep track of terminals that have been updated
            terms = set(startNode.outputs().values())

            #print "======= Updating", startNode
            #print "Order:", order
            for node in order[1:]:
                #print "Processing node", node
                for term in list(node.inputs().values()):
                    #print "  checking terminal", term
                    deps = list(term.connections().keys())
                    update = False
                    for d in deps:
                        if d in terms:
                            #print "    ..input", d, "changed"
                            update = True
                            term.inputChanged(d, process=False)
                    if update:
                        #print "  processing.."
                        node.update()
                        terms |= set(node.outputs().values())

        finally:
            self.processing = False
            if self.inputWasSet:
                self.inputWasSet = False
            else:
                self.sigStateChanged.emit()

    def chartGraphicsItem(self):
        """Return the graphicsItem which displays the internals of this flowchart.
        (graphicsItem() still returns the external-view item)"""
        #return self._chartGraphicsItem
        return self.viewBox

    def widget(self):
        if self._widget is None:
            self._widget = FlowchartCtrlWidget(self)
            self.scene = self._widget.scene()
            self.viewBox = self._widget.viewBox()
            #self._scene = QtGui.QGraphicsScene()
            #self._widget.setScene(self._scene)
            #self.scene.addItem(self.chartGraphicsItem())

            #ci = self.chartGraphicsItem()
            #self.viewBox.addItem(ci)
            #self.viewBox.autoRange()
        return self._widget

    def listConnections(self):
        conn = set()
        for n in self._nodes.values():
            terms = n.outputs()
            for n, t in terms.items():
                for c in t.connections():
                    conn.add((t, c))
        return conn

    def saveState(self):
        state = Node.saveState(self)
        state['nodes'] = []
        state['connects'] = []
        #state['terminals'] = self.saveTerminals()

        for name, node in self._nodes.items():
            cls = type(node)
            if hasattr(cls, 'nodeName'):
                clsName = cls.nodeName
                pos = node.graphicsItem().pos()
                ns = {
                    'class': clsName,
                    'name': name,
                    'pos': (pos.x(), pos.y()),
                    'state': node.saveState()
                }
                state['nodes'].append(ns)

        conn = self.listConnections()
        for a, b in conn:
            state['connects'].append(
                (a.node().name(), a.name(), b.node().name(), b.name()))

        state['inputNode'] = self.inputNode.saveState()
        state['outputNode'] = self.outputNode.saveState()

        return state

    def restoreState(self, state, clear=False):
        self.blockSignals(True)
        try:
            if clear:
                self.clear()
            Node.restoreState(self, state)
            nodes = state['nodes']
            nodes.sort(lambda a, b: cmp(a['pos'][0], b['pos'][0]))
            for n in nodes:
                if n['name'] in self._nodes:
                    #self._nodes[n['name']].graphicsItem().moveBy(*n['pos'])
                    self._nodes[n['name']].restoreState(n['state'])
                    continue
                try:
                    node = self.createNode(n['class'], name=n['name'])
                    node.restoreState(n['state'])
                except:
                    printExc("Error creating node %s: (continuing anyway)" %
                             n['name'])
                #node.graphicsItem().moveBy(*n['pos'])

            self.inputNode.restoreState(state.get('inputNode', {}))
            self.outputNode.restoreState(state.get('outputNode', {}))

            #self.restoreTerminals(state['terminals'])
            for n1, t1, n2, t2 in state['connects']:
                try:
                    self.connectTerminals(self._nodes[n1][t1],
                                          self._nodes[n2][t2])
                except:
                    print(self._nodes[n1].terminals)
                    print(self._nodes[n2].terminals)
                    printExc("Error connecting terminals %s.%s - %s.%s:" %
                             (n1, t1, n2, t2))

        finally:
            self.blockSignals(False)

        self.sigChartLoaded.emit()
        self.outputChanged()
        self.sigStateChanged.emit()
        #self.sigOutputChanged.emit()

    def loadFile(self, fileName=None, startDir=None):
        if fileName is None:
            if startDir is None:
                startDir = self.filePath
            if startDir is None:
                startDir = '.'
            self.fileDialog = pg.FileDialog(None, "Load Flowchart..", startDir,
                                            "Flowchart (*.fc)")
            #self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
            #self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
            self.fileDialog.show()
            self.fileDialog.fileSelected.connect(self.loadFile)
            return
            ## NOTE: was previously using a real widget for the file dialog's parent, but this caused weird mouse event bugs..
            #fileName = QtGui.QFileDialog.getOpenFileName(None, "Load Flowchart..", startDir, "Flowchart (*.fc)")
        fileName = str(fileName)
        state = configfile.readConfigFile(fileName)
        self.restoreState(state, clear=True)
        self.viewBox.autoRange()
        #self.emit(QtCore.SIGNAL('fileLoaded'), fileName)
        self.sigFileLoaded.emit(fileName)

    def saveFile(self,
                 fileName=None,
                 startDir=None,
                 suggestedFileName='flowchart.fc'):
        if fileName is None:
            if startDir is None:
                startDir = self.filePath
            if startDir is None:
                startDir = '.'
            self.fileDialog = pg.FileDialog(None, "Save Flowchart..", startDir,
                                            "Flowchart (*.fc)")
            #self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
            self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
            #self.fileDialog.setDirectory(startDir)
            self.fileDialog.show()
            self.fileDialog.fileSelected.connect(self.saveFile)
            return
            #fileName = QtGui.QFileDialog.getSaveFileName(None, "Save Flowchart..", startDir, "Flowchart (*.fc)")
        configfile.writeConfigFile(self.saveState(), fileName)
        self.sigFileSaved.emit(fileName)

    def clear(self):
        for n in list(self._nodes.values()):
            if n is self.inputNode or n is self.outputNode:
                continue
            n.close()  ## calls self.nodeClosed(n) by signal
        #self.clearTerminals()
        self.widget().clear()

    def clearTerminals(self):
        Node.clearTerminals(self)
        self.inputNode.clearTerminals()
        self.outputNode.clearTerminals()
 def animation(self,frametime=10):
     timer = QtCore.QTimer()
     timer.timeout.connect(self.update)
     timer.start(frametime)
     self.start()
Example #10
0
class ViewBox(GraphicsWidget):
    """
    **Bases:** :class:`GraphicsWidget <pyqtgraph.GraphicsWidget>`
    
    Box that allows internal scaling/panning of children by mouse drag. 
    This class is usually created automatically as part of a :class:`PlotItem <pyqtgraph.PlotItem>` or :class:`Canvas <pyqtgraph.canvas.Canvas>` or with :func:`GraphicsLayout.addViewBox() <pyqtgraph.GraphicsLayout.addViewBox>`.
    
    Features:
    
        - Scaling contents by mouse or auto-scale when contents change
        - View linking--multiple views display the same data ranges
        - Configurable by context menu
        - Item coordinate mapping methods
    
    Not really compatible with GraphicsView having the same functionality.
    """

    sigYRangeChanged = QtCore.Signal(object, object)
    sigXRangeChanged = QtCore.Signal(object, object)
    sigRangeChangedManually = QtCore.Signal(object)
    sigRangeChanged = QtCore.Signal(object, object)
    #sigActionPositionChanged = QtCore.Signal(object)
    sigStateChanged = QtCore.Signal(object)

    ## mouse modes
    PanMode = 3
    RectMode = 1

    ## axes
    XAxis = 0
    YAxis = 1
    XYAxes = 2

    ## for linking views together
    NamedViews = weakref.WeakValueDictionary()  # name: ViewBox
    AllViews = weakref.WeakKeyDictionary()  # ViewBox: None

    def __init__(self,
                 parent=None,
                 border=None,
                 lockAspect=False,
                 enableMouse=True,
                 invertY=False,
                 name=None):
        """
        =============  =============================================================
        **Arguments**
        *parent*       (QGraphicsWidget) Optional parent widget
        *border*       (QPen) Do draw a border around the view, give any 
                       single argument accepted by :func:`mkPen <pyqtgraph.mkPen>`
        *lockAspect*   (False or float) The aspect ratio to lock the view 
                       coorinates to. (or False to allow the ratio to change)
        *enableMouse*  (bool) Whether mouse can be used to scale/pan the view 
        *invertY*      (bool) See :func:`invertY <pyqtgraph.ViewBox.invertY>`
        =============  =============================================================
        """

        GraphicsWidget.__init__(self, parent)
        self.name = None
        self.linksBlocked = False
        self.addedItems = []
        #self.gView = view
        #self.showGrid = showGrid

        self.state = {

            ## separating targetRange and viewRange allows the view to be resized
            ## while keeping all previously viewed contents visible
            'targetRange':
            [[0, 1],
             [0,
              1]],  ## child coord. range visible [[xmin, xmax], [ymin, ymax]]
            'viewRange': [[0, 1], [0, 1]],  ## actual range viewed
            'yInverted':
            invertY,
            'aspectLocked':
            False,  ## False if aspect is unlocked, otherwise float specifies the locked ratio.
            'autoRange': [True, True],  ## False if auto range is disabled, 
            ## otherwise float gives the fraction of data that is visible
            'linkedViews': [None, None],
            'mouseEnabled': [enableMouse, enableMouse],
            'mouseMode':
            ViewBox.PanMode if pyqtgraph.getConfigOption('leftButtonPan') else
            ViewBox.RectMode,
            'wheelScaleFactor':
            -1.0 / 8.0,
        }

        #self.exportMethods = collections.OrderedDict([
        #('SVG', self.saveSvg),
        #('Image', self.saveImage),
        #('Print', self.savePrint),
        #])

        self.setFlag(self.ItemClipsChildrenToShape)
        self.setFlag(self.ItemIsFocusable,
                     True)  ## so we can receive key presses

        ## childGroup is required so that ViewBox has local coordinates similar to device coordinates.
        ## this is a workaround for a Qt + OpenGL but that causes improper clipping
        ## https://bugreports.qt.nokia.com/browse/QTBUG-23723
        self.childGroup = ChildGroup(self)
        self.childGroup.sigItemsChanged.connect(self.itemsChanged)

        #self.useLeftButtonPan = pyqtgraph.getConfigOption('leftButtonPan') # normally use left button to pan
        # this also enables capture of keyPressEvents.

        ## Make scale box that is shown when dragging on the view
        self.rbScaleBox = QtGui.QGraphicsRectItem(0, 0, 1, 1)
        self.rbScaleBox.setPen(fn.mkPen((255, 0, 0), width=1))
        self.rbScaleBox.setBrush(fn.mkBrush(255, 255, 0, 100))
        self.rbScaleBox.hide()
        self.addItem(self.rbScaleBox)

        self.axHistory = []  # maintain a history of zoom locations
        self.axHistoryPointer = -1  # pointer into the history. Allows forward/backward movement, not just "undo"

        self.setZValue(-100)
        self.setSizePolicy(
            QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,
                              QtGui.QSizePolicy.Expanding))

        self.setAspectLocked(lockAspect)

        self.border = fn.mkPen(border)
        self.menu = ViewBoxMenu(self)

        self.register(name)
        if name is None:
            self.updateViewLists()

    def register(self, name):
        """
        Add this ViewBox to the registered list of views. 
        *name* will appear in the drop-down lists for axis linking in all other views.
        The same can be accomplished by initializing the ViewBox with the *name* attribute.
        """
        ViewBox.AllViews[self] = None
        if self.name is not None:
            del ViewBox.NamedViews[self.name]
        self.name = name
        if name is not None:
            ViewBox.NamedViews[name] = self
            ViewBox.updateAllViewLists()

    def unregister(self):
        """
        Remove this ViewBox forom the list of linkable views. (see :func:`register() <pyqtgraph.ViewBox.register>`)
        """
        del ViewBox.AllViews[self]
        if self.name is not None:
            del ViewBox.NamedViews[self.name]

    def close(self):
        self.unregister()

    def implements(self, interface):
        return interface == 'ViewBox'

    def getState(self, copy=True):
        state = self.state.copy()
        state['linkedViews'] = [(None if v is None else v.name)
                                for v in state['linkedViews']]
        if copy:
            return deepcopy(self.state)
        else:
            return self.state

    def setState(self, state):
        state = state.copy()
        self.setXLink(state['linkedViews'][0])
        self.setYLink(state['linkedViews'][1])
        del state['linkedViews']

        self.state.update(state)
        self.updateMatrix()
        self.sigStateChanged.emit(self)

    def setMouseMode(self, mode):
        """
        Set the mouse interaction mode. *mode* must be either ViewBox.PanMode or ViewBox.RectMode.
        In PanMode, the left mouse button pans the view and the right button scales.
        In RectMode, the left button draws a rectangle which updates the visible region (this mode is more suitable for single-button mice)
        """
        if mode not in [ViewBox.PanMode, ViewBox.RectMode]:
            raise Exception("Mode must be ViewBox.PanMode or ViewBox.RectMode")
        self.state['mouseMode'] = mode
        self.sigStateChanged.emit(self)

    #def toggleLeftAction(self, act):  ## for backward compatibility
    #if act.text() is 'pan':
    #self.setLeftButtonAction('pan')
    #elif act.text() is 'zoom':
    #self.setLeftButtonAction('rect')

    def setLeftButtonAction(self, mode='rect'):  ## for backward compatibility
        if mode.lower() == 'rect':
            self.setMouseMode(ViewBox.RectMode)
        elif mode.lower() == 'pan':
            self.setMouseMode(ViewBox.PanMode)
        else:
            raise Exception(
                'graphicsItems:ViewBox:setLeftButtonAction: unknown mode = %s (Options are "pan" and "rect")'
                % mode)

    def innerSceneItem(self):
        return self.childGroup

    def setMouseEnabled(self, x=None, y=None):
        """
        Set whether each axis is enabled for mouse interaction. *x*, *y* arguments must be True or False.
        This allows the user to pan/scale one axis of the view while leaving the other axis unchanged.
        """
        if x is not None:
            self.state['mouseEnabled'][0] = x
        if y is not None:
            self.state['mouseEnabled'][1] = y
        self.sigStateChanged.emit(self)

    def mouseEnabled(self):
        return self.state['mouseEnabled'][:]

    def addItem(self, item, ignoreBounds=False):
        """
        Add a QGraphicsItem to this view. The view will include this item when determining how to set its range
        automatically unless *ignoreBounds* is True.
        """
        if item.zValue() < self.zValue():
            item.setZValue(self.zValue() + 1)
        item.setParentItem(self.childGroup)
        if not ignoreBounds:
            self.addedItems.append(item)
        self.updateAutoRange()
        #print "addItem:", item, item.boundingRect()

    def removeItem(self, item):
        """Remove an item from this view."""
        try:
            self.addedItems.remove(item)
        except:
            pass
        self.scene().removeItem(item)
        self.updateAutoRange()

    def resizeEvent(self, ev):
        #self.setRange(self.range, padding=0)
        #self.updateAutoRange()
        self.updateMatrix()
        self.sigStateChanged.emit(self)
        #self.linkedXChanged()
        #self.linkedYChanged()

    def viewRange(self):
        """Return a the view's visible range as a list: [[xmin, xmax], [ymin, ymax]]"""
        return [x[:] for x in self.state['viewRange']]  ## return copy

    def viewRect(self):
        """Return a QRectF bounding the region visible within the ViewBox"""
        try:
            vr0 = self.state['viewRange'][0]
            vr1 = self.state['viewRange'][1]
            return QtCore.QRectF(vr0[0], vr1[0], vr0[1] - vr0[0],
                                 vr1[1] - vr1[0])
        except:
            print "make qrectf failed:", self.state['viewRange']
            raise

    #def viewportTransform(self):
    ##return self.itemTransform(self.childGroup)[0]
    #return self.childGroup.itemTransform(self)[0]

    def targetRange(self):
        return [x[:] for x in self.state['targetRange']]  ## return copy

    def targetRect(self):
        """
        Return the region which has been requested to be visible. 
        (this is not necessarily the same as the region that is *actually* visible--
        resizing and aspect ratio constraints can cause targetRect() and viewRect() to differ)
        """
        try:
            tr0 = self.state['targetRange'][0]
            tr1 = self.state['targetRange'][1]
            return QtCore.QRectF(tr0[0], tr1[0], tr0[1] - tr0[0],
                                 tr1[1] - tr1[0])
        except:
            print "make qrectf failed:", self.state['targetRange']
            raise

    def setRange(self,
                 rect=None,
                 xRange=None,
                 yRange=None,
                 padding=0.02,
                 update=True,
                 disableAutoRange=True):
        """
        Set the visible range of the ViewBox.
        Must specify at least one of *range*, *xRange*, or *yRange*. 
        
        ============= =====================================================================
        **Arguments**
        *rect*        (QRectF) The full range that should be visible in the view box.
        *xRange*      (min,max) The range that should be visible along the x-axis.
        *yRange*      (min,max) The range that should be visible along the y-axis.
        *padding*     (float) Expand the view by a fraction of the requested range. 
                      By default, this value is 0.02 (2%)
        ============= =====================================================================
        
        """
        changes = {}

        if rect is not None:
            changes = {
                0: [rect.left(), rect.right()],
                1: [rect.top(), rect.bottom()]
            }
        if xRange is not None:
            changes[0] = xRange
        if yRange is not None:
            changes[1] = yRange

        if len(changes) == 0:
            raise Exception(
                "Must specify at least one of rect, xRange, or yRange.")

        changed = [False, False]
        for ax, range in changes.iteritems():
            mn = min(range)
            mx = max(range)
            if mn == mx:  ## If we requested 0 range, try to preserve previous scale. Otherwise just pick an arbitrary scale.
                dy = self.state['viewRange'][ax][1] - self.state['viewRange'][
                    ax][0]
                if dy == 0:
                    dy = 1
                mn -= dy * 0.5
                mx += dy * 0.5
                padding = 0.0
            if any(np.isnan([mn, mx])) or any(np.isinf([mn, mx])):
                raise Exception("Not setting range [%s, %s]" %
                                (str(mn), str(mx)))

            p = (mx - mn) * padding
            mn -= p
            mx += p

            if self.state['targetRange'][ax] != [mn, mx]:
                self.state['targetRange'][ax] = [mn, mx]
                changed[ax] = True

        if any(changed) and disableAutoRange:
            if all(changed):
                ax = ViewBox.XYAxes
            elif changed[0]:
                ax = ViewBox.XAxis
            elif changed[1]:
                ax = ViewBox.YAxis
            self.enableAutoRange(ax, False)

        self.sigStateChanged.emit(self)

        if update:
            self.updateMatrix(changed)

        for ax, range in changes.iteritems():
            link = self.state['linkedViews'][ax]
            if link is not None:
                link.linkedViewChanged(self, ax)

    def setYRange(self, min, max, padding=0.02, update=True):
        """
        Set the visible Y range of the view to [*min*, *max*]. 
        The *padding* argument causes the range to be set larger by the fraction specified.
        """
        self.setRange(yRange=[min, max], update=update, padding=padding)

    def setXRange(self, min, max, padding=0.02, update=True):
        """
        Set the visible X range of the view to [*min*, *max*]. 
        The *padding* argument causes the range to be set larger by the fraction specified.
        """
        self.setRange(xRange=[min, max], update=update, padding=padding)

    def autoRange(self, padding=0.02):
        """
        Set the range of the view box to make all children visible.
        Note that this is not the same as enableAutoRange, which causes the view to 
        automatically auto-range whenever its contents are changed.
        """
        bounds = self.childrenBoundingRect()
        if bounds is not None:
            self.setRange(bounds, padding=padding)

    def scaleBy(self, s, center=None):
        """
        Scale by *s* around given center point (or center of view).
        *s* may be a Point or tuple (x, y)
        """
        scale = Point(s)
        if self.state['aspectLocked'] is not False:
            scale[0] = self.state['aspectLocked'] * scale[1]

        vr = self.targetRect()
        if center is None:
            center = Point(vr.center())
        else:
            center = Point(center)

        tl = center + (vr.topLeft() - center) * scale
        br = center + (vr.bottomRight() - center) * scale

        self.setRange(QtCore.QRectF(tl, br), padding=0)

    def translateBy(self, t):
        """
        Translate the view by *t*, which may be a Point or tuple (x, y).
        """
        t = Point(t)
        #if viewCoords:  ## scale from pixels
        #o = self.mapToView(Point(0,0))
        #t = self.mapToView(t) - o

        vr = self.targetRect()
        self.setRange(vr.translated(t), padding=0)

    def enableAutoRange(self, axis=None, enable=True):
        """
        Enable (or disable) auto-range for *axis*, which may be ViewBox.XAxis, ViewBox.YAxis, or ViewBox.XYAxes for both.
        When enabled, the axis will automatically rescale when items are added/removed or change their shape.
        The argument *enable* may optionally be a float (0.0-1.0) which indicates the fraction of the data that should
        be visible (this only works with items implementing a dataRange method, such as PlotDataItem).
        """
        #print "autorange:", axis, enable
        #if not enable:
        #import traceback
        #traceback.print_stack()
        if enable is True:
            enable = 1.0

        if axis is None:
            axis = ViewBox.XYAxes

        if axis == ViewBox.XYAxes or axis == 'xy':
            self.state['autoRange'][0] = enable
            self.state['autoRange'][1] = enable
        elif axis == ViewBox.XAxis or axis == 'x':
            self.state['autoRange'][0] = enable
        elif axis == ViewBox.YAxis or axis == 'y':
            self.state['autoRange'][1] = enable
        else:
            raise Exception(
                'axis argument must be ViewBox.XAxis, ViewBox.YAxis, or ViewBox.XYAxes.'
            )

        if enable:
            self.updateAutoRange()
        self.sigStateChanged.emit(self)

    def disableAutoRange(self, axis=None):
        """Disables auto-range. (See enableAutoRange)"""
        self.enableAutoRange(axis, enable=False)

    def autoRangeEnabled(self):
        return self.state['autoRange'][:]

    def updateAutoRange(self):
        tr = self.viewRect()
        if not any(self.state['autoRange']):
            return

        fractionVisible = self.state['autoRange'][:]
        for i in [0, 1]:
            if type(fractionVisible[i]) is bool:
                fractionVisible[i] = 1.0
        cr = self.childrenBoundingRect(frac=fractionVisible)
        wp = cr.width() * 0.02
        hp = cr.height() * 0.02
        cr = cr.adjusted(-wp, -hp, wp, hp)

        if self.state['autoRange'][0] is not False:
            tr.setLeft(cr.left())
            tr.setRight(cr.right())
        if self.state['autoRange'][1] is not False:
            tr.setTop(cr.top())
            tr.setBottom(cr.bottom())

        self.setRange(tr, padding=0, disableAutoRange=False)

    def setXLink(self, view):
        """Link this view's X axis to another view. (see LinkView)"""
        self.linkView(self.XAxis, view)

    def setYLink(self, view):
        """Link this view's Y axis to another view. (see LinkView)"""
        self.linkView(self.YAxis, view)

    def linkView(self, axis, view):
        """
        Link X or Y axes of two views and unlink any previously connected axes. *axis* must be ViewBox.XAxis or ViewBox.YAxis.
        If view is None, the axis is left unlinked.
        """
        if isinstance(view, basestring):
            if view == '':
                view = None
            else:
                view = ViewBox.NamedViews[view]

        if hasattr(view, 'implements') and view.implements('ViewBoxWrapper'):
            view = view.getViewBox()

        ## used to connect/disconnect signals between a pair of views
        if axis == ViewBox.XAxis:
            signal = 'sigXRangeChanged'
            slot = self.linkedXChanged
        else:
            signal = 'sigYRangeChanged'
            slot = self.linkedYChanged

        oldLink = self.state['linkedViews'][axis]
        if oldLink is not None:
            getattr(oldLink, signal).disconnect(slot)

        self.state['linkedViews'][axis] = view

        if view is not None:
            getattr(view, signal).connect(slot)
            if view.autoRangeEnabled()[axis] is not False:
                self.enableAutoRange(axis, False)
                slot()
            else:
                if self.autoRangeEnabled()[axis] is False:
                    slot()

        self.sigStateChanged.emit(self)

    def blockLink(self, b):
        self.linksBlocked = b  ## prevents recursive plot-change propagation

    def linkedXChanged(self):
        ## called when x range of linked view has changed
        view = self.state['linkedViews'][0]
        self.linkedViewChanged(view, ViewBox.XAxis)

    def linkedYChanged(self):
        ## called when y range of linked view has changed
        view = self.state['linkedViews'][1]
        self.linkedViewChanged(view, ViewBox.YAxis)

    def linkedViewChanged(self, view, axis):
        if self.linksBlocked or view is None:
            return

        vr = view.viewRect()
        vg = view.screenGeometry()
        if vg is None:
            return

        sg = self.screenGeometry()

        view.blockLink(True)
        try:
            if axis == ViewBox.XAxis:
                overlap = min(sg.right(), vg.right()) - max(
                    sg.left(), vg.left())
                if overlap < min(
                        vg.width() / 3,
                        sg.width() / 3):  ## if less than 1/3 of views overlap,
                    ## then just replicate the view
                    x1 = vr.left()
                    x2 = vr.right()
                else:  ## views overlap; line them up
                    upp = float(vr.width()) / vg.width()
                    x1 = vr.left() + (sg.x() - vg.x()) * upp
                    x2 = x1 + sg.width() * upp
                self.enableAutoRange(ViewBox.XAxis, False)
                self.setXRange(x1, x2, padding=0)
            else:
                overlap = min(sg.bottom(), vg.bottom()) - max(
                    sg.top(), vg.top())
                if overlap < min(vg.height() / 3,
                                 sg.height() /
                                 3):  ## if less than 1/3 of views overlap,
                    ## then just replicate the view
                    x1 = vr.top()
                    x2 = vr.bottom()
                else:  ## views overlap; line them up
                    upp = float(vr.height()) / vg.height()
                    x1 = vr.top() + (sg.y() - vg.y()) * upp
                    x2 = x1 + sg.height() * upp
                self.enableAutoRange(ViewBox.YAxis, False)
                self.setYRange(x1, x2, padding=0)
        finally:
            view.blockLink(False)

    def screenGeometry(self):
        """return the screen geometry of the viewbox"""
        v = self.getViewWidget()
        if v is None:
            return None
        b = self.sceneBoundingRect()
        wr = v.mapFromScene(b).boundingRect()
        pos = v.mapToGlobal(v.pos())
        wr.adjust(pos.x(), pos.y(), pos.x(), pos.y())
        return wr

    def itemsChanged(self):
        ## called when items are added/removed from self.childGroup
        self.updateAutoRange()

    def itemBoundsChanged(self, item):
        self.updateAutoRange()

    def invertY(self, b=True):
        """
        By default, the positive y-axis points upward on the screen. Use invertY(True) to reverse the y-axis.
        """
        self.state['yInverted'] = b
        self.updateMatrix()
        self.sigStateChanged.emit(self)

    def setAspectLocked(self, lock=True, ratio=1):
        """
        If the aspect ratio is locked, view scaling must always preserve the aspect ratio.
        By default, the ratio is set to 1; x and y both have the same scaling.
        This ratio can be overridden (width/height), or use None to lock in the current ratio.
        """
        if not lock:
            self.state['aspectLocked'] = False
        else:
            vr = self.viewRect()
            currentRatio = vr.width() / vr.height()
            if ratio is None:
                ratio = currentRatio
            self.state['aspectLocked'] = ratio
            if ratio != currentRatio:  ## If this would change the current range, do that now
                #self.setRange(0, self.state['viewRange'][0][0], self.state['viewRange'][0][1])
                self.updateMatrix()
        self.sigStateChanged.emit(self)

    def childTransform(self):
        """
        Return the transform that maps from child(item in the childGroup) coordinates to local coordinates.
        (This maps from inside the viewbox to outside)
        """
        m = self.childGroup.transform()
        #m1 = QtGui.QTransform()
        #m1.translate(self.childGroup.pos().x(), self.childGroup.pos().y())
        return m  #*m1

    def mapToView(self, obj):
        """Maps from the local coordinates of the ViewBox to the coordinate system displayed inside the ViewBox"""
        m = self.childTransform().inverted()[0]
        return m.map(obj)

    def mapFromView(self, obj):
        """Maps from the coordinate system displayed inside the ViewBox to the local coordinates of the ViewBox"""
        m = self.childTransform()
        return m.map(obj)

    def mapSceneToView(self, obj):
        """Maps from scene coordinates to the coordinate system displayed inside the ViewBox"""
        return self.mapToView(self.mapFromScene(obj))

    def mapViewToScene(self, obj):
        """Maps from the coordinate system displayed inside the ViewBox to scene coordinates"""
        return self.mapToScene(self.mapFromView(obj))

    def mapFromItemToView(self, item, obj):
        """Maps *obj* from the local coordinate system of *item* to the view coordinates"""
        return self.childGroup.mapFromItem(item, obj)
        #return self.mapSceneToView(item.mapToScene(obj))

    def mapFromViewToItem(self, item, obj):
        """Maps *obj* from view coordinates to the local coordinate system of *item*."""
        return self.childGroup.mapToItem(item, obj)
        #return item.mapFromScene(self.mapViewToScene(obj))

    def itemBoundingRect(self, item):
        """Return the bounding rect of the item in view coordinates"""
        return self.mapSceneToView(item.sceneBoundingRect()).boundingRect()

    #def viewScale(self):
    #vr = self.viewRect()
    ##print "viewScale:", self.range
    #xd = vr.width()
    #yd = vr.height()
    #if xd == 0 or yd == 0:
    #print "Warning: 0 range in view:", xd, yd
    #return np.array([1,1])

    ##cs = self.canvas().size()
    #cs = self.boundingRect()
    #scale = np.array([cs.width() / xd, cs.height() / yd])
    ##print "view scale:", scale
    #return scale

    def wheelEvent(self, ev, axis=None):
        mask = np.array(self.state['mouseEnabled'], dtype=np.float)
        if axis is not None and axis >= 0 and axis < len(mask):
            mv = mask[axis]
            mask[:] = 0
            mask[axis] = mv
        s = ((mask * 0.02) + 1)**(ev.delta() * self.state['wheelScaleFactor']
                                  )  # actual scaling factor

        center = Point(self.childGroup.transform().inverted()[0].map(ev.pos()))
        #center = ev.pos()

        self.scaleBy(s, center)
        self.sigRangeChangedManually.emit(self.state['mouseEnabled'])
        ev.accept()

    def mouseClickEvent(self, ev):
        if ev.button() == QtCore.Qt.RightButton:
            ev.accept()
            self.raiseContextMenu(ev)

    def raiseContextMenu(self, ev):
        #print "viewbox.raiseContextMenu called."

        #menu = self.getMenu(ev)
        menu = self.getMenu(ev)
        self.scene().addParentContextMenus(self, menu, ev)
        #print "2:", [str(a.text()) for a in self.menu.actions()]
        pos = ev.screenPos()
        #pos2 = ev.scenePos()
        #print "3:", [str(a.text()) for a in self.menu.actions()]
        #self.sigActionPositionChanged.emit(pos2)

        menu.popup(QtCore.QPoint(pos.x(), pos.y()))
        #print "4:", [str(a.text()) for a in self.menu.actions()]

    def getMenu(self, ev):
        self._menuCopy = self.menu.copy(
        )  ## temporary storage to prevent menu disappearing
        return self._menuCopy

    def getContextMenus(self, event):
        return self.menu.subMenus()
        #return [self.getMenu(event)]

    def mouseDragEvent(self, ev, axis=None):
        ## if axis is specified, event will only affect that axis.
        ev.accept()  ## we accept all buttons

        pos = ev.pos()
        lastPos = ev.lastPos()
        dif = pos - lastPos
        dif = dif * -1

        ## Ignore axes if mouse is disabled
        mask = np.array(self.state['mouseEnabled'], dtype=np.float)
        if axis is not None:
            mask[1 - axis] = 0.0

        ## Scale or translate based on mouse button
        if ev.button() & (QtCore.Qt.LeftButton | QtCore.Qt.MidButton):
            if self.state['mouseMode'] == ViewBox.RectMode:
                if ev.isFinish(
                ):  ## This is the final move in the drag; change the view scale now
                    #print "finish"
                    self.rbScaleBox.hide()
                    #ax = QtCore.QRectF(Point(self.pressPos), Point(self.mousePos))
                    ax = QtCore.QRectF(Point(ev.buttonDownPos(ev.button())),
                                       Point(pos))
                    ax = self.childGroup.mapRectFromParent(ax)
                    self.showAxRect(ax)
                    self.axHistoryPointer += 1
                    self.axHistory = self.axHistory[:self.axHistoryPointer] + [
                        ax
                    ]
                else:
                    ## update shape of scale box
                    self.updateScaleBox(ev.buttonDownPos(), ev.pos())
            else:
                tr = dif * mask
                tr = self.mapToView(tr) - self.mapToView(Point(0, 0))
                self.translateBy(tr)
                self.sigRangeChangedManually.emit(self.state['mouseEnabled'])
        elif ev.button() & QtCore.Qt.RightButton:
            #print "vb.rightDrag"
            if self.state['aspectLocked'] is not False:
                mask[0] = 0

            dif = ev.screenPos() - ev.lastScreenPos()
            dif = np.array([dif.x(), dif.y()])
            dif[0] *= -1
            s = ((mask * 0.02) + 1)**dif
            center = Point(self.childGroup.transform().inverted()[0].map(
                ev.buttonDownPos(QtCore.Qt.RightButton)))
            #center = Point(ev.buttonDownPos(QtCore.Qt.RightButton))
            self.scaleBy(s, center)
            self.sigRangeChangedManually.emit(self.state['mouseEnabled'])

    def keyPressEvent(self, ev):
        """
        This routine should capture key presses in the current view box.
        Key presses are used only when mouse mode is RectMode
        The following events are implemented:
        ctrl-A : zooms out to the default "full" view of the plot
        ctrl-+ : moves forward in the zooming stack (if it exists)
        ctrl-- : moves backward in the zooming stack (if it exists)
         
        """
        #print ev.key()
        #print 'I intercepted a key press, but did not accept it'

        ## not implemented yet ?
        #self.keypress.sigkeyPressEvent.emit()

        ev.accept()
        if ev.text() == '-':
            self.scaleHistory(-1)
        elif ev.text() in ['+', '=']:
            self.scaleHistory(1)
        elif ev.key() == QtCore.Qt.Key_Backspace:
            self.scaleHistory(len(self.axHistory))
        else:
            ev.ignore()

    def scaleHistory(self, d):
        ptr = max(0, min(len(self.axHistory) - 1, self.axHistoryPointer + d))
        if ptr != self.axHistoryPointer:
            self.axHistoryPointer = ptr
            self.showAxRect(self.axHistory[ptr])

    def updateScaleBox(self, p1, p2):
        r = QtCore.QRectF(p1, p2)
        r = self.childGroup.mapRectFromParent(r)
        self.rbScaleBox.setPos(r.topLeft())
        self.rbScaleBox.resetTransform()
        self.rbScaleBox.scale(r.width(), r.height())
        self.rbScaleBox.show()

    def showAxRect(self, ax):
        self.setRange(ax.normalized())  # be sure w, h are correct coordinates
        self.sigRangeChangedManually.emit(self.state['mouseEnabled'])

    #def mouseRect(self):
    #vs = self.viewScale()
    #vr = self.state['viewRange']
    ## Convert positions from screen (view) pixel coordinates to axis coordinates
    #ax = QtCore.QRectF(self.pressPos[0]/vs[0]+vr[0][0], -(self.pressPos[1]/vs[1]-vr[1][1]),
    #(self.mousePos[0]-self.pressPos[0])/vs[0], -(self.mousePos[1]-self.pressPos[1])/vs[1])
    #return(ax)

    def allChildren(self, item=None):
        """Return a list of all children and grandchildren of this ViewBox"""
        if item is None:
            item = self.childGroup

        children = [item]
        for ch in item.childItems():
            children.extend(self.allChildren(ch))
        return children

    def childrenBoundingRect(self, frac=None):
        """Return the bounding range of all children.
        [[xmin, xmax], [ymin, ymax]]
        Values may be None if there are no specific bounds for an axis.
        """

        #items = self.allChildren()
        items = self.addedItems

        #if item is None:
        ##print "children bounding rect:"
        #item = self.childGroup

        range = [None, None]

        for item in items:
            if not item.isVisible():
                continue

            #print "=========", item
            useX = True
            useY = True
            if hasattr(item, 'dataBounds'):
                if frac is None:
                    frac = (1.0, 1.0)
                xr = item.dataBounds(0, frac=frac[0])
                yr = item.dataBounds(1, frac=frac[1])
                if xr is None or xr == (None, None):
                    useX = False
                    xr = (0, 0)
                if yr is None or yr == (None, None):
                    useY = False
                    yr = (0, 0)

                bounds = QtCore.QRectF(xr[0], yr[0], xr[1] - xr[0],
                                       yr[1] - yr[0])
                #print "   item real:", bounds
            else:
                if int(item.flags() & item.ItemHasNoContents) > 0:
                    continue
                    #print "   empty"
                else:
                    bounds = item.boundingRect()
                    #bounds = [[item.left(), item.top()], [item.right(), item.bottom()]]
                #print "   item:", bounds
            #bounds = QtCore.QRectF(bounds[0][0], bounds[1][0], bounds[0][1]-bounds[0][0], bounds[1][1]-bounds[1][0])
            bounds = self.mapFromItemToView(item, bounds).boundingRect()
            #print "    ", bounds

            if not any([useX, useY]):
                continue

            if useX != useY:  ##   !=  means  xor
                ang = item.transformAngle()
                if ang == 0 or ang == 180:
                    pass
                elif ang == 90 or ang == 270:
                    tmp = useX
                    useY = useX
                    useX = tmp
                else:
                    continue  ## need to check for item rotations and decide how best to apply this boundary.

            if useY:
                if range[1] is not None:
                    range[1] = [
                        min(bounds.top(), range[1][0]),
                        max(bounds.bottom(), range[1][1])
                    ]
                    #bounds.setTop(min(bounds.top(), chb.top()))
                    #bounds.setBottom(max(bounds.bottom(), chb.bottom()))
                else:
                    range[1] = [bounds.top(), bounds.bottom()]
                    #bounds.setTop(chb.top())
                    #bounds.setBottom(chb.bottom())
            if useX:
                if range[0] is not None:
                    range[0] = [
                        min(bounds.left(), range[0][0]),
                        max(bounds.right(), range[0][1])
                    ]
                    #bounds.setLeft(min(bounds.left(), chb.left()))
                    #bounds.setRight(max(bounds.right(), chb.right()))
                else:
                    range[0] = [bounds.left(), bounds.right()]
                    #bounds.setLeft(chb.left())
                    #bounds.setRight(chb.right())

        tr = self.targetRange()
        if range[0] is None:
            range[0] = tr[0]
        if range[1] is None:
            range[1] = tr[1]

        bounds = QtCore.QRectF(range[0][0], range[1][0],
                               range[0][1] - range[0][0],
                               range[1][1] - range[1][0])
        return bounds

    def updateMatrix(self, changed=None):
        if changed is None:
            changed = [False, False]
        #print "udpateMatrix:"
        #print "  range:", self.range
        tr = self.targetRect()
        bounds = self.rect()  #boundingRect()
        #print bounds

        ## set viewRect, given targetRect and possibly aspect ratio constraint
        if self.state['aspectLocked'] is False or bounds.height() == 0:
            self.state['viewRange'] = [
                self.state['targetRange'][0][:],
                self.state['targetRange'][1][:]
            ]
        else:
            viewRatio = bounds.width() / bounds.height()
            targetRatio = self.state['aspectLocked'] * tr.width() / tr.height()
            if targetRatio > viewRatio:
                ## target is wider than view
                dy = 0.5 * (tr.width() /
                            (self.state['aspectLocked'] * viewRatio) -
                            tr.height())
                if dy != 0:
                    changed[1] = True
                self.state['viewRange'] = [
                    self.state['targetRange'][0][:],
                    [
                        self.state['targetRange'][1][0] - dy,
                        self.state['targetRange'][1][1] + dy
                    ]
                ]
            else:
                dx = 0.5 * (tr.height() * viewRatio *
                            self.state['aspectLocked'] - tr.width())
                if dx != 0:
                    changed[0] = True
                self.state['viewRange'] = [[
                    self.state['targetRange'][0][0] - dx,
                    self.state['targetRange'][0][1] + dx
                ], self.state['targetRange'][1][:]]

        vr = self.viewRect()
        #print "  bounds:", bounds
        if vr.height() == 0 or vr.width() == 0:
            return
        scale = Point(bounds.width() / vr.width(),
                      bounds.height() / vr.height())
        if not self.state['yInverted']:
            scale = scale * Point(1, -1)
        m = QtGui.QTransform()

        ## First center the viewport at 0
        #self.childGroup.resetTransform()
        #self.resetTransform()
        #center = self.transform().inverted()[0].map(bounds.center())
        center = bounds.center()
        #print "  transform to center:", center
        #if self.state['yInverted']:
        #m.translate(center.x(), -center.y())
        #print "  inverted; translate", center.x(), center.y()
        #else:
        m.translate(center.x(), center.y())
        #print "  not inverted; translate", center.x(), -center.y()

        ## Now scale and translate properly
        m.scale(scale[0], scale[1])
        st = Point(vr.center())
        #st = translate
        m.translate(-st[0], -st[1])

        self.childGroup.setTransform(m)
        #self.setTransform(m)
        #self.prepareGeometryChange()

        #self.currentScale = scale

        if changed[0]:
            self.sigXRangeChanged.emit(self, tuple(self.state['viewRange'][0]))
        if changed[1]:
            self.sigYRangeChanged.emit(self, tuple(self.state['viewRange'][1]))
        if any(changed):
            self.sigRangeChanged.emit(self, self.state['viewRange'])

    def paint(self, p, opt, widget):
        if self.border is not None:
            bounds = self.shape()
            p.setPen(self.border)
            #p.fillRect(bounds, QtGui.QColor(0, 0, 0))
            p.drawPath(bounds)

    #def saveSvg(self):
    #pass

    #def saveImage(self):
    #pass

    #def savePrint(self):
    #printer = QtGui.QPrinter()
    #if QtGui.QPrintDialog(printer).exec_() == QtGui.QDialog.Accepted:
    #p = QtGui.QPainter(printer)
    #p.setRenderHint(p.Antialiasing)
    #self.scene().render(p)
    #p.end()

    def updateViewLists(self):
        def cmpViews(a, b):
            wins = 100 * cmp(a.window() is self.window(),
                             b.window() is self.window())
            alpha = cmp(a.name, b.name)
            return wins + alpha

        ## make a sorted list of all named views
        nv = ViewBox.NamedViews.values()
        nv.sort(cmpViews)

        if self in nv:
            nv.remove(self)
        names = [v.name for v in nv]
        self.menu.setViewList(names)

    @staticmethod
    def updateAllViewLists():
        for v in ViewBox.AllViews:
            v.updateViewLists()
Example #11
0
class ScatterPlotItem(GraphicsObject):
    """
    Displays a set of x/y points. Instances of this class are created
    automatically as part of PlotDataItem; these rarely need to be instantiated
    directly.
    
    The size, shape, pen, and fill brush may be set for each point individually 
    or for all points. 
    
    
    ========================  ===============================================
    **Signals:**
    sigPlotChanged(self)      Emitted when the data being plotted has changed
    sigClicked(self, points)  Emitted when the curve is clicked. Sends a list
                              of all the points under the mouse pointer.
    ========================  ===============================================
    
    """
    #sigPointClicked = QtCore.Signal(object, object)
    sigClicked = QtCore.Signal(object, object)  ## self, points
    sigPlotChanged = QtCore.Signal(object)
    
    def __init__(self, *args, **kargs):
        """
        Accepts the same arguments as setData()
        """
        
        GraphicsObject.__init__(self)
        self.data = None
        self.spots = []
        self.bounds = [None, None]
        self.opts = {}
        self.spotsValid = False
        self._spotPixmap = None
        
        self.setPen(200,200,200)
        self.setBrush(100,100,150)
        self.setSymbol('o')
        self.setSize(7)
        self.setPxMode(True)
        self.setIdentical(False)
        
        self.setData(*args, **kargs)
        
        
    def setData(self, *args, **kargs):
        """
        **Ordered Arguments:**
        
        * If there is only one unnamed argument, it will be interpreted like the 'spots' argument.
        * If there are two unnamed arguments, they will be interpreted as sequences of x and y values.
        
        ====================== ===============================================================================================
        **Keyword Arguments:**
        *spots*                Optional list of dicts. Each dict specifies parameters for a single spot:
                               {'pos': (x,y), 'size', 'pen', 'brush', 'symbol'}. This is just an alternate method
                               of passing in data for the corresponding arguments.
        *x*,*y*                1D arrays of x,y values.
        *pos*                  2D structure of x,y pairs (such as Nx2 array or list of tuples)
        *pxMode*               If True, spots are always the same size regardless of scaling, and size is given in px.
                               Otherwise, size is in scene coordinates and the spots scale with the view.
                               Default is True
        *identical*            If True, all spots are forced to look identical. 
                               This can result in performance enhancement.
                               Default is False
        *symbol*               can be one (or a list) of:
                               
                               * 'o'  circle (default)
                               * 's'  square
                               * 't'  triangle
                               * 'd'  diamond
                               * '+'  plus
        *pen*                  The pen (or list of pens) to use for drawing spot outlines.
        *brush*                The brush (or list of brushes) to use for filling spots.
        *size*                 The size (or list of sizes) of spots. If *pxMode* is True, this value is in pixels. Otherwise,
                               it is in the item's local coordinate system.
        *data*                 a list of python objects used to uniquely identify each spot.
        ====================== ===============================================================================================
        """
        
        self.clear()

        
        ## deal with non-keyword arguments
        if len(args) == 1:
            kargs['spots'] = args[0]
        elif len(args) == 2:
            kargs['x'] = args[0]
            kargs['y'] = args[1]
        elif len(args) > 2:
            raise Exception('Only accepts up to two non-keyword arguments.')
        
        ## convert 'pos' argument to 'x' and 'y'
        if 'pos' in kargs:
            pos = kargs['pos']
            if isinstance(pos, np.ndarray):
                kargs['x'] = pos[:,0]
                kargs['y'] = pos[:,1]
            else:
                x = []
                y = []
                for p in pos:
                    if isinstance(p, QtCore.QPointF):
                        x.append(p.x())
                        y.append(p.y())
                    else:
                        x.append(p[0])
                        y.append(p[1])
                kargs['x'] = x
                kargs['y'] = y
        
        ## determine how many spots we have
        if 'spots' in kargs:
            numPts = len(kargs['spots'])
        elif 'y' in kargs and kargs['y'] is not None:
            numPts = len(kargs['y'])
        else:
            kargs['x'] = []
            kargs['y'] = []
            numPts = 0
        
        ## create empty record array
        self.data = np.empty(numPts, dtype=[('x', float), ('y', float), ('size', float), ('symbol', 'S1'), ('pen', object), ('brush', object), ('spot', object)])
        self.data['size'] = -1  ## indicates use default size
        self.data['symbol'] = ''
        self.data['pen'] = None
        self.data['brush'] = None
        self.pointData = np.empty(numPts, dtype=object)
        self.pointData[:] = None
        
        if 'spots' in kargs:
            spots = kargs['spots']
            for i in xrange(len(spots)):
                spot = spots[i]
                for k in spot:
                    if k == 'pen':
                        self.data[i][k] = fn.mkPen(spot[k])
                    elif k == 'brush':
                        self.data[i][k] = fn.mkBrush(spot[k])
                    elif k == 'pos':
                        pos = spot[k]
                        if isinstance(pos, QtCore.QPointF):
                            x,y = pos.x(), pos.y()
                        else:
                            x,y = pos[0], pos[1]
                        self.data[i]['x'] = x
                        self.data[i]['y'] = y
                    elif k in ['x', 'y', 'size', 'symbol']:
                        self.data[i][k] = spot[k]
                    elif k == 'data':
                        self.pointData[i] = spot[k]
                    else:
                        raise Exception("Unknown spot parameter: %s" % k)
        elif 'y' in kargs:
            self.data['x'] = kargs['x']
            self.data['y'] = kargs['y']
        
        
        ## Set any extra parameters provided in keyword arguments
        for k in ['pxMode', 'identical', 'pen', 'brush', 'symbol', 'size']:
            if k in kargs:
                setMethod = getattr(self, 'set' + k[0].upper() + k[1:])
                setMethod(kargs[k])
                
        if 'data' in kargs:
            self.setPointData(kargs['data'])
            
        self.updateSpots()
        
        
        
        
        
        
        
        
        #pen = kargs.get('pen', (200,200,200))
        #brush = kargs.get('pen', (100,100,150))
        
        #if hasattr(pen, '__len__'):
            #pen = map(pg.mkPen(pen))
        #self.data['pen'] = pen
        
        #if hasattr(pen, '__len__'):
            #brush = map(pg.mkPen(pen))
        #self.data['brush'] = pen
        
        #self.data['size'] = kargs.get('size', 7)
        #self.data['symbol'] = kargs.get('symbol', 'o')
        
        
        
        #if spots is not None and len(spots) > 0:
            #spot = spots[0]
            #for k in spot:
                #self.data[k] = []
            #for spot in spots:
                #for k,v in spot.iteritems():
                    #self.data[k].append(v)
        
    def setPoints(self, *args, **kargs):
        ##Deprecated; use setData
        return self.setData(*args, **kargs)
        
    #def setPoints(self, spots=None, x=None, y=None, data=None):
        #"""
        #Remove all existing points in the scatter plot and add a new set.
        #Arguments:
            #spots - list of dicts specifying parameters for each spot
                    #[ {'pos': (x,y), 'pen': 'r', ...}, ...]
            #x, y -  arrays specifying location of spots to add. 
                    #all other parameters (pen, symbol, etc.) will be set to the default
                    #values for this scatter plot.
                    #these arguments are IGNORED if 'spots' is specified
            #data -  list of arbitrary objects to be assigned to spot.data for each spot
                    #(this is useful for identifying spots that are clicked on)
        #"""
        #self.clear()
        #self.bounds = [[0,0],[0,0]]
        #self.addPoints(spots, x, y, data)
        
    def implements(self, interface=None):
        ints = ['plotData']
        if interface is None:
            return ints
        return interface in ints
    
    def setPen(self, *args, **kargs):
        if len(args) == 1 and (isinstance(args[0], np.ndarray) or isinstance(args[0], list)):
            pens = args[0]
            if self.data is None:
                raise Exception("Must set data before setting multiple pens.")
            if len(pens) != len(self.data):
                raise Exception("Number of pens does not match number of points (%d != %d)" % (len(pens), len(self.data)))
            for i in xrange(len(pens)):
                self.data[i]['pen'] = fn.mkPen(pens[i])
        else:
            self.opts['pen'] = fn.mkPen(*args, **kargs)
        self.updateSpots()
        
    def setBrush(self, *args, **kargs):
        if len(args) == 1 and (isinstance(args[0], np.ndarray) or isinstance(args[0], list)):
            brushes = args[0]
            if self.data is None:
                raise Exception("Must set data before setting multiple brushes.")
            if len(brushes) != len(self.data):
                raise Exception("Number of brushes does not match number of points (%d != %d)" % (len(brushes), len(self.data)))
            for i in xrange(len(brushes)):
                self.data[i]['brush'] = fn.mkBrush(brushes[i], **kargs)
        else:
            self.opts['brush'] = fn.mkBrush(*args, **kargs)
        self.updateSpots()

    def setSymbol(self, symbol):
        if isinstance(symbol, np.ndarray) or isinstance(symbol, list):
            symbols = symbol
            if self.data is None:
                raise Exception("Must set data before setting multiple symbols.")
            if len(symbols) != len(self.data):
                raise Exception("Number of symbols does not match number of points (%d != %d)" % (len(symbols), len(self.data)))
            self.data['symbol'] = symbols
        else:
            self.opts['symbol'] = symbol
        self.updateSpots()
        
    def setSize(self, size):
        if isinstance(size, np.ndarray) or isinstance(size, list):
            sizes = size
            if self.data is None:
                raise Exception("Must set data before setting multiple sizes.")
            if len(sizes) != len(self.data):
                raise Exception("Number of sizes does not match number of points (%d != %d)" % (len(sizes), len(self.data)))
            self.data['size'] = sizes
        else:
            self.opts['size'] = size
        self.updateSpots()
        
    def setPointData(self, data):
        if isinstance(data, np.ndarray) or isinstance(data, list):
            if self.data is None:
                raise Exception("Must set xy data before setting meta data.")
            if len(data) != len(self.data):
                raise Exception("Length of meta data does not match number of points (%d != %d)" % (len(data), len(self.data)))
        self.pointData = data
        self.updateSpots()
        
        
    def setIdentical(self, ident):
        self.opts['identical'] = ident
        self.updateSpots()
        
    def setPxMode(self, mode):
        self.opts['pxMode'] = mode
        self.updateSpots()
        
    def updateSpots(self):
        self.spotsValid = False
        self.update()
        
    def clear(self):
        for i in self.spots:
            i.setParentItem(None)
            s = i.scene()
            if s is not None:
                s.removeItem(i)
        self.spots = []
        self.data = None
        self.spotsValid = False
        self.bounds = [None, None]
        

    def dataBounds(self, ax, frac=1.0):
        if frac >= 1.0 and self.bounds[ax] is not None:
            return self.bounds[ax]
        
        if self.data is None or len(self.data) == 0:
            return (None, None)
        
        if ax == 0:
            d = self.data['x']
        elif ax == 1:
            d = self.data['y']
            
        if frac >= 1.0:
            minIndex = np.argmin(d)
            maxIndex = np.argmax(d)
            minVal = d[minIndex]
            maxVal = d[maxIndex]
            if not self.opts['pxMode']:
                minVal -= self.data[minIndex]['size']
                maxVal += self.data[maxIndex]['size']
            self.bounds[ax] = (minVal, maxVal)
            return self.bounds[ax]
        elif frac <= 0.0:
            raise Exception("Value for parameter 'frac' must be > 0. (got %s)" % str(frac))
        else:
            return (scipy.stats.scoreatpercentile(d, 50 - (frac * 50)), scipy.stats.scoreatpercentile(d, 50 + (frac * 50)))
            
            

    
    def addPoints(self, *args, **kargs):
        """
        Add new points to the scatter plot. 
        Arguments are the same as setData()
        Note: this is expensive; plenty of room for optimization here.
        """
        if self.data is None:
            self.setData(*args, **kargs)
            return
            
        
        data1 = self.data[:]
        #range1 = [self.bounds[0][:], self.bounds[1][:]]
        self.setData(*args, **kargs)
        newData = np.empty(len(self.data) + len(data1), dtype=self.data.dtype)
        newData[:len(data1)] = data1
        newData[len(data1):] = self.data
        #self.bounds = [
            #[min(self.bounds[0][0], range1[0][0]), max(self.bounds[0][1], range1[0][1])],
            #[min(self.bounds[1][0], range1[1][0]), max(self.bounds[1][1], range1[1][1])],
        #]
        self.data = newData
        self.sigPlotChanged.emit(self)
    
    
    def generateSpots(self, clear=True):
        if clear:
            for spot in self.spots:
                self.scene().removeItem(spot)
            self.spots = []
        
        
        xmn = ymn = xmx = ymx = None
        
        ## apply defaults
        size = self.data['size'].copy()
        size[size<0] = self.opts['size']
        
        pen = self.data['pen'].copy()
        pen[pen<0] = self.opts['pen']  ## note pen<0 checks for pen==None
        
        brush = self.data['brush'].copy()
        brush[brush<0] = self.opts['brush']
        
        symbol = self.data['symbol'].copy()
        symbol[symbol==''] = self.opts['symbol']

        
        for i in xrange(len(self.data)):
            s = self.data[i]
            pos = Point(s['x'], s['y'])
            if self.opts['pxMode']:
                psize = 0
            else:
                psize = size[i]
                
            if self.pointData is None or self.pointData[i] is None:
                data = self.opts.get('data', None)
            else:
                data = self.pointData[i]
                
            #if xmn is None:
                #xmn = pos[0]-psize
                #xmx = pos[0]+psize
                #ymn = pos[1]-psize
                #ymx = pos[1]+psize
            #else:
                #xmn = min(xmn, pos[0]-psize)
                #xmx = max(xmx, pos[0]+psize)
                #ymn = min(ymn, pos[1]-psize)
                #ymx = max(ymx, pos[1]+psize)
                
            item = self.mkSpot(pos, size[i], self.opts['pxMode'], brush[i], pen[i], data, symbol=symbol[i], index=len(self.spots))
            self.spots.append(item)
            self.data[i]['spot'] = item
            #if self.optimize:
                #item.hide()
                #frag = QtGui.QPainter.PixmapFragment.create(pos, QtCore.QRectF(0, 0, size, size))
                #self.optimizeFragments.append(frag)
                
        #self.bounds = [[xmn, xmx], [ymn, ymx]]
        self.spotsValid = True
        self.sigPlotChanged.emit(self)
        
        
    #def setPointSize(self, size):
        #for s in self.spots:
            #s.size = size
        ##self.setPoints([{'size':s.size, 'pos':s.pos(), 'data':s.data} for s in self.spots])
        #self.setPoints()
                
    #def paint(self, p, *args):
        #if not self.optimize:
            #return
        ##p.setClipRegion(self.boundingRect())
        #p.drawPixmapFragments(self.optimizeFragments, self.optimizePixmap)

    def paint(self, *args):
        if not self.spotsValid:
            self.generateSpots()

    def spotPixmap(self):
        ## If all spots are identical, return the pixmap to use for all spots
        ## Otherwise return None
        
        if not self.opts['identical']:
            return None
        if self._spotPixmap is None:
            spot = SpotItem(size=self.opts['size'], pxMode=True, brush=self.opts['brush'], pen=self.opts['pen'], symbol=self.opts['symbol'])
            self._spotPixmap = spot.pixmap
        return self._spotPixmap

    def mkSpot(self, pos, size, pxMode, brush, pen, data, symbol=None, index=None):
        ## Make and return a SpotItem (or PixmapSpotItem if in pxMode)
        brush = fn.mkBrush(brush)
        pen = fn.mkPen(pen)
        if pxMode:
            img = self.spotPixmap()  ## returns None if not using identical mode
            #item = PixmapSpotItem(size, brush, pen, data, image=img, symbol=symbol, index=index)
            item = SpotItem(size, pxMode, brush, pen, data, symbol=symbol, image=img, index=index)
        else:
            item = SpotItem(size, pxMode, brush, pen, data, symbol=symbol, index=index)
        item.setParentItem(self)
        item.setPos(pos)
        #item.sigClicked.connect(self.pointClicked)
        return item
        
    def boundingRect(self):
        (xmn, xmx) = self.dataBounds(ax=0)
        (ymn, ymx) = self.dataBounds(ax=1)
        if xmn is None or xmx is None:
            xmn = 0
            xmx = 0
        if ymn is None or ymx is None:
            ymn = 0
            ymx = 0
        return QtCore.QRectF(xmn, ymn, xmx-xmn, ymx-ymn)
        
        #if xmn is None or xmx is None or ymn is None or ymx is None:
            #return QtCore.QRectF()
        #return QtCore.QRectF(xmn, ymn, xmx-xmn, ymx-ymn)
        #return QtCore.QRectF(xmn-1, ymn-1, xmx-xmn+2, ymx-ymn+2)
        
    #def pointClicked(self, point):
        #self.sigPointClicked.emit(self, point)

    def points(self):
        if not self.spotsValid:
            self.generateSpots()
        return self.spots[:]

    def pointsAt(self, pos):
        if not self.spotsValid:
            self.generateSpots()
        x = pos.x()
        y = pos.y()
        pw = self.pixelWidth()
        ph = self.pixelHeight()
        pts = []
        for s in self.spots:
            sp = s.pos()
            ss = s.size
            sx = sp.x()
            sy = sp.y()
            s2x = s2y = ss * 0.5
            if self.opts['pxMode']:
                s2x *= pw
                s2y *= ph
            if x > sx-s2x and x < sx+s2x and y > sy-s2y and y < sy+s2y:
                pts.append(s)
                #print "HIT:", x, y, sx, sy, s2x, s2y
            #else:
                #print "No hit:", (x, y), (sx, sy)
                #print "       ", (sx-s2x, sy-s2y), (sx+s2x, sy+s2y)
        pts.sort(lambda a,b: cmp(b.zValue(), a.zValue()))
        return pts
            

    #def mousePressEvent(self, ev):
        #QtGui.QGraphicsItem.mousePressEvent(self, ev)
        #if ev.button() == QtCore.Qt.LeftButton:
            #pts = self.pointsAt(ev.pos())
            #if len(pts) > 0:
                #self.mouseMoved = False
                #self.ptsClicked = pts
                #ev.accept()
            #else:
                ##print "no spots"
                #ev.ignore()
        #else:
            #ev.ignore()
        
    #def mouseMoveEvent(self, ev):
        #QtGui.QGraphicsItem.mouseMoveEvent(self, ev)
        #self.mouseMoved = True
        #pass
    
    #def mouseReleaseEvent(self, ev):
        #QtGui.QGraphicsItem.mouseReleaseEvent(self, ev)
        #if not self.mouseMoved:
            #self.sigClicked.emit(self, self.ptsClicked)

    def mouseClickEvent(self, ev):
        if ev.button() == QtCore.Qt.LeftButton:
            pts = self.pointsAt(ev.pos())
            if len(pts) > 0:
                self.ptsClicked = pts
                self.sigClicked.emit(self, self.ptsClicked)
                ev.accept()
            else:
                #print "no spots"
                ev.ignore()
        else:
            ev.ignore()
Example #12
0
from pyqtgraph.Qt import QtGui, QtCore
from pyqtgraph.Point import Point
import pyqtgraph.functions as fn
from GraphicsObject import GraphicsObject
import numpy as np
import scipy.stats

__all__ = ['ScatterPlotItem', 'SpotItem']


## Build all symbol paths
Symbols = {name: QtGui.QPainterPath() for name in ['o', 's', 't', 'd', '+']}

Symbols['o'].addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1))
Symbols['s'].addRect(QtCore.QRectF(-0.5, -0.5, 1, 1))
coords = {
    't': [(-0.5, -0.5), (0, 0.5), (0.5, -0.5)],
    'd': [(0., -0.5), (-0.4, 0.), (0, 0.5), (0.4, 0)],
    '+': [
        (-0.5, -0.05), (-0.5, 0.05), (-0.05, 0.05), (-0.05, 0.5),
        (0.05, 0.5), (0.05, 0.05), (0.5, 0.05), (0.5, -0.05), 
        (0.05, -0.05), (0.05, -0.5), (-0.05, -0.5), (-0.05, -0.05)
    ],
}
for k, c in coords.iteritems():
    Symbols[k].moveTo(*c[0])
    for x,y in c[1:]:
        Symbols[k].lineTo(x, y)
    Symbols[k].closeSubpath()

Example #13
0
 def animate(self):
     timer = QtCore.QTimer()
     timer.timeout.connect(self.update)
     timer.start(10)
     self.start()
Example #14
0
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
from custom_piechart_class import PieChartItem
from custom_qgraphicsscene import CustomQGraphicsScene
import signal


#suppress warnings temporarily:
QtCore.qInstallMsgHandler(lambda *args: None)

#Always start with this
app = QtGui.QApplication([])

#These don't seem to change anything, so I'll leave them at zero
scene_xmin = 0
scene_ymin = 0
scene_width = 200
scene_height = 200

scene = CustomQGraphicsScene(scene_xmin,scene_ymin,scene_width,scene_height)

p1 = PieChartItem(('first_piechart',scene_xmin+scene_width*0.5, scene_ymin+scene_height*0.5, 50, [ (0.5, 'b'), (0.25, 'r'), (0.25, 'g') ]))
p1.setDescript('this is a test string that should be a description of the project. in fact let\'s make it really long so we can test if text wraps around or not when we draw it.')
p2 = PieChartItem(('second_piechart',scene_xmin+scene_width*0.75, scene_ymin+scene_height*0.75, 10, [ (1., 'r') ]))
p2.setDescript('this is the red pie chart!!!!!!1!!!11!!!!one')

scene.addItem(p1)
scene.addItem(p2)

view = QtGui.QGraphicsView(scene)
#Enforce the view to align with upper-left corner
Example #15
0
 def boundingRect(self):
     return QtCore.QRectF(0, 0, 10, 10)
Example #16
0
class OIDataReaderThread(QtCore.QThread):
    signal      = QtCore.pyqtSignal('PyQt_PyObject')
    peSignal    = QtCore.pyqtSignal(dict)
    ceSignal    = QtCore.pyqtSignal(dict)
    niftySignal = QtCore.pyqtSignal(dict)
    niftyHeavyWeightsSgnl = QtCore.pyqtSignal('PyQt_PyObject')

    def __init__(self, expiry, heavyWeightsList):
        QtCore.QThread.__init__(self)
        log.log("OIDataReaderThread Started...")
        self.currentExpiry = expiry
        self.timePeriod    = 1
        self.todayDate = h.getDate()
        self.optionaTable = "OptionChain.db"
        self.NiftyTable = "Nifty50"
        self.heavyWeightsList = heavyWeightsList
        self.otmsToShow = []
        self.timer = 60

    def __del__(self): 
        log.log("OIDataReaderThread - exited") 

    def SetOtmsToShow(self, otms):
        log.log("OIDataReaderThread - SetOtmsToShow = " + str(otms))
        self.otmsToShow = otms.copy()
        self.ReadData()

    def UpdateOtmsToShow(self, otms):
        self.otmsToShow = otms.copy()

    def SetExpiry(self, expiry):
        log.log("OIDataReaderThread - SetExpiry = " + expiry)
        self.currentExpiry = expiry
        self.ReadData()

    def SetTimePeriod(self, timePeriod):
        log.log("OIDataReaderThread - SetTimePeriod = " + str(timePeriod))
        self.timePeriod = timePeriod      
        self.ReadData()

    def UpdateHeavyWeightsList(self, heavyList):
        log.log("OIDataReaderThread - UpdateHeavyWeightsList = " + str(heavyList))
        self.heavyWeightsList = heavyList
        self.ReadData()

    def ReadData(self):
        self.conn = sqlite3.connect("OptionChain.db")
        self.c = self.conn.cursor()
        tPeriod = self.timePeriod

        try:
            for strike in self.otmsToShow:        
                ce_data = {}
                pe_data = {}    
                query = "SELECT Time, OpenInterest, Underlying, TotVol FROM OptionChain WHERE Expiry = '" + self.currentExpiry + "' AND "
                query = query + " ContractType = 'CE' AND "  + " Strike = " + str(strike) + " AND Date = '" + self.todayDate + "'" 
                result = self.c.execute(query)
                ceRecords = result.fetchall()

                niftyPrc = []
                timeLst  = []
                OI       = []
                TotVolume= []
                i        = 0
                sz       = len(ceRecords)
                
                for record in ceRecords:
                    if 1 == tPeriod or 0 == i or i == sz-1 or 0 == (i % tPeriod):
                        timeLst.append(record[0])
                        OI.append(record[1]) 
                        niftyPrc.append(record[2])
                        TotVolume.append(record[3])

                ce_data['Strike'] = strike
                ce_data['Time'] = timeLst
                ce_data['OI'] = OI
                ce_data['Nifty']  = niftyPrc
                ce_data['Volume'] = TotVolume       
                self.ceSignal.emit(ce_data)

                query = "SELECT Time, OpenInterest, TotVol FROM OptionChain WHERE Expiry = '" + self.currentExpiry + "' AND "
                query = query + " ContractType = 'PE' AND "  + " Strike = " + str(strike) + " AND Date = '" + self.todayDate + "'"
                result = self.c.execute(query)
                peRecords = result.fetchall()

                timeLst  = []
                OI       = []
                TotVolume= []
                i  = 0
                sz = len(peRecords)
                for record in peRecords:
                    if 1 == tPeriod or 0 == i or i == sz-1 or 0 == (i % tPeriod):
                        timeLst.append(record[0])
                        OI.append(record[1]) 
                        TotVolume.append(record[2])
                    i = i+1

                pe_data['Strike'] = strike
                pe_data['Time'] = timeLst
                pe_data['OI'] = OI       
                pe_data['Volume'] = TotVolume       
                self.peSignal.emit(pe_data)

            query = "SELECT Time, Nifty50 FROM " + self.NiftyTable + " WHERE Date = '" + self.todayDate + "'" 
            result = self.c.execute(query)
            niftyRecords = result.fetchall()

            timeLst = []
            price   = []
            i  = 0
            sz = len(niftyRecords)
            for record in niftyRecords:
                if 1 == tPeriod or 0 == i or i == sz-1 or 0 == (i % tPeriod):
                    timeLst.append(record[0])
                    price.append(record[1]) 

            nifty_data = {}    
            nifty_data['Time'] = timeLst
            nifty_data['Price'] = price       
            self.niftySignal.emit(nifty_data)

            if [] != self.heavyWeightsList:
                subQry = "("
                for stk in self.heavyWeightsList:
                    subQry = subQry + "'" + stk + "',"
                subQry = subQry[:-1]
                subQry = subQry + ")"

                query = "SELECT Time, Symbol, LTP, TotVol, pChange FROM NiftyHeavyWeights WHERE Date = '" + self.todayDate + "'"
                query = query +  " and Symbol in " + subQry 

                result = self.c.execute(query)
                niftyRecords = result.fetchall()

                if [] != niftyRecords:
                    self.niftyHeavyWeightsSgnl.emit(niftyRecords)
            else:
                self.niftyHeavyWeightsSgnl.emit([])

        except:
            log.logException("OIDataReaderThread - exception during read data")

        self.c.close()
        self.conn.close()

    def run(self):
        while True:
            if [] == self.otmsToShow:
                log.log('OIDataReaderThread - OTMS to show is empty, retry')
                t.sleep(5)
                continue

            self.ReadData()
            t.sleep(self.timer)
Example #17
0
 def setup(self):
     
     
 
     self.winImage = pg.GraphicsLayoutWidget()
     self.winImage.setContentsMargins(0,0,0,0)
     self.winImage.setAspectLocked(True)
     self.winImage.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
     self.winImage.ci.setContentsMargins(0,0,0,0)
     
     vbox2=QVBoxLayout()
     vbox2.addWidget(self.winImage)
     vbox2.setContentsMargins(1,1,1,1)
     
 
     self.p1=self.winImage.addPlot()
     self.imh=pg.ImageItem()
     self.p1.addItem(self.imh)
     self.p1.setMouseEnabled(x=False,y=False)
     self.p1.setContentsMargins(0,0,0,0)
     self.p1.setAspectLocked(True,ratio=1)
     self.p1.showAxis('right',show=False)
     self.p1.showAxis('top',show=False)
     self.p1.showAxis('left',show=False)
     self.p1.showAxis('bottom',show=False)
     
     self.vLine = pg.InfiniteLine(angle=90, movable=False,pen='y')
     self.hLine = pg.InfiniteLine(angle=0, movable=False,pen='y')
     self.p1.addItem(self.vLine)
     self.p1.addItem(self.hLine, ignoreBounds=False)
     self.xc=int(self.confCCD.value(self.nbcam+"/xc"))
     self.yc=int(self.confCCD.value(self.nbcam+"/yc"))
     self.rx=int(self.confCCD.value(self.nbcam+"/rx"))
     self.ry=int(self.confCCD.value(self.nbcam+"/ry"))
     self.vLine.setPos(self.xc)
     self.hLine.setPos(self.yc)
     
     self.ro1=pg.EllipseROI([self.xc,self.yc],[self.rx,self.ry],pen='y',movable=False,maxBounds=QtCore.QRectF(0,0,self.rx,self.ry))
     self.ro1.setPos([self.xc-(self.rx/2),self.yc-(self.ry/2)])
     self.p1.addItem(self.ro1)
     
     self.hist = pg.HistogramLUTItem() 
     self.hist.setImageItem(self.imh)
     self.hist.autoHistogramRange()
     self.hist.gradient.loadPreset('flame')
     HLayout=QHBoxLayout()
 
     HLayout.addLayout(vbox2)
     self.setLayout(HLayout)
Example #18
0
 def boundingRect(self):
     #print "FlowchartGraphicsItem.boundingRect"
     return QtCore.QRectF()
Example #19
0
    def __init__(self, scanZ, recWidget=None, *args, **kwargs):

        super().__init__(*args, **kwargs)
        self.setMinimumSize(2, 350)

        self.mainRec = recWidget

        self.webcam = instruments.Webcam()

        self.z = scanZ
        self.z.zHostPosition = 'left'
        self.z.zobject.HostBackLashEnable = False

        self.setPoint = 0

        self.V = Q_(1, 'V')
        self.um = Q_(1, 'um')
        self.nm = Q_(1, 'nm')

        self.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Raised)

        self.scansPerS = 10
        self.ProcessData = ProcessData(self.webcam)

        # Focus lock widgets
        self.kpEdit = QtGui.QLineEdit('4')
        self.kpEdit.setFixedWidth(60)
        self.kpEdit.textChanged.connect(self.unlockFocus)
        self.kpLabel = QtGui.QLabel('kp')
        self.kiEdit = QtGui.QLineEdit('0.01')
        self.kiEdit.setFixedWidth(60)
        self.kiEdit.textChanged.connect(self.unlockFocus)
        self.kiLabel = QtGui.QLabel('ki')
        self.lockButton = QtGui.QPushButton('Lock')
        self.lockButton.setCheckable(True)
        self.lockButton.clicked.connect(self.toggleFocus)
        self.lockButton.setSizePolicy(QtGui.QSizePolicy.Preferred,
                                      QtGui.QSizePolicy.Expanding)
        moveLabel = QtGui.QLabel('Move [nm]')
        moveLabel.setAlignment(QtCore.Qt.AlignCenter)
        self.moveEdit = QtGui.QLineEdit('0')
        self.moveEdit.setFixedWidth(60)
        self.moveEdit.returnPressed.connect(self.zMoveEdit)

        self.focusDataBox = QtGui.QCheckBox('Save focus data')
        self.focusPropertiesDisplay = QtGui.QLabel(' st_dev = 0  max_dev = 0')

        self.graph = FocusLockGraph(self, self.mainRec)

        self.focusTime = 1000 / self.scansPerS
        self.focusTimer = QtCore.QTimer()
        self.focusTimer.timeout.connect(self.update)
        self.focusTimer.start(self.focusTime)

        self.locked = False
        self.n = 1
        self.max_dev = 0

        self.focusCalib = FocusCalibration(self)
        self.focusCalibThread = QtCore.QThread(self)
        self.focusCalib.moveToThread(self.focusCalibThread)
        self.focusCalibButton = QtGui.QPushButton('Calibrate')
        self.focusCalibButton.clicked.connect(self.focusCalib.start)
        self.focusCalibThread.start()

        try:
            prevCal = np.around(np.loadtxt('calibration')[0]/10)
            text = '1 px --> {} nm'.format(prevCal)
            self.calibrationDisplay = QtGui.QLineEdit(text)
        except:
            self.calibrationDisplay = QtGui.QLineEdit('0 px --> 0 nm')

        self.calibrationDisplay.setReadOnly(False)

        self.webcamgraph = WebcamGraph(self)

        dockArea = DockArea()
        graphDock = Dock("Focus laser graph", size=(400, 200))
        graphDock.addWidget(self.graph)
        dockArea.addDock(graphDock)
        webcamDock = Dock("Focus laser view", size=(200, 200))
        webcamDock.addWidget(self.webcamgraph)
        dockArea.addDock(webcamDock, 'right', graphDock)

        # GUI layout
        grid = QtGui.QGridLayout()
        self.setLayout(grid)
        grid.addWidget(dockArea, 0, 0, 1, 6)
        grid.addWidget(self.focusCalibButton, 1, 0)
        grid.addWidget(self.calibrationDisplay, 2, 0)
        grid.addWidget(self.focusDataBox, 1, 1)
        grid.addWidget(moveLabel, 1, 2)
        grid.addWidget(self.moveEdit, 2, 2)
        grid.addWidget(self.kpLabel, 1, 3)
        grid.addWidget(self.kpEdit, 1, 4)
        grid.addWidget(self.kiLabel, 2, 3)
        grid.addWidget(self.kiEdit, 2, 4)
        grid.addWidget(self.lockButton, 1, 5, 2, 1)
        grid.setColumnMinimumWidth(5, 170)
Example #20
0
    def all_plots(self):
        """
            Screen: all_plots
            Desc: Shows EMG, acceleration and orientation plots for raw
                MYO inputs. All channels for like-fields are shown on same
                plot (8 channels for EMG, 3 channels for acceleration and
                4 channels for orientation).
        """
        # Creates QtGUI widget and sets layout
        self.cw = QtGui.QWidget()
        self.setCentralWidget(self.cw)
        l = QtGui.QGridLayout()
        self.cw.setLayout(l)

        # Creates inner widget label for screen title
        titleLabel = QtGui.QLabel('Myo Data Acquirer')
        titleLabel.setFont(QtGui.QFont("Garamond", 16, QtGui.QFont.Bold))
        l.addWidget(titleLabel, 0, 0, 2, 1)

        # Creates button to go to EMG plots screen
        emg_button = QtGui.QPushButton('EMG Plots')
        emg_button.setToolTip('Open only EMG plots.')
        emg_button.clicked.connect(self.emg_plots)
        l.addWidget(emg_button, 0, 0, 1, 1)

        # Shows EMG plot
        self.emgplot = pg.PlotWidget(name='EMGplot')
        self.emgplot.setRange(
            QtCore.QRectF(0, -50, 1000, 400)
        )  # xRange=(0, 1000), yRange=(-50, 400))#QtCore.QRectF(-50,-200,1000,1400))
        self.emgplot.disableAutoRange()
        self.emgplot.setTitle("EMG")
        l.addWidget(self.emgplot, 0, 1, 1, 1)

        # Shows acceleration plot
        self.accplot = pg.PlotWidget(name='ACCplot')
        self.accplot.setRange(QtCore.QRectF(0, -2, 1000, 7))
        self.accplot.setTitle("Accelerometer")
        l.addWidget(self.accplot, 1, 1, 1, 1)

        # Creates button to go to acceleration plots screen
        acc_button = QtGui.QPushButton('Acceleration Plots')
        acc_button.setToolTip('Open only acceleration plots.')
        acc_button.clicked.connect(self.acc_plots)
        l.addWidget(acc_button, 1, 0, 1, 1)

        # Shows orientation plot
        self.oriplot = pg.PlotWidget(name='ORIplot')
        self.oriplot.setRange(QtCore.QRectF(0, -1, 1000, 8))
        self.oriplot.setTitle("Orientation")
        l.addWidget(self.oriplot, 2, 1, 1, 1)

        # Creates button to go to orientation plots screen
        ori_button = QtGui.QPushButton('Orientation Plots')
        ori_button.setToolTip('Open only orientation plots.')
        ori_button.clicked.connect(self.ori_plots)
        l.addWidget(ori_button, 2, 0, 1, 1)

        # Creates list for EMG recordings then starts recording buffer
        # readings. Plots EMG buffer data on plot as well.
        self.emgcurve = []
        for i in range(EMG_RANGE):
            c = self.emgplot.plot(pen=(i, 10))
            c.setPos(0, i * 50)
            self.emgcurve.append(c)

        # Creates list for acceleration recordings then starts recording buffer
        # readings. Plots acceleration buffer data on plot as well.
        self.oricurve = []
        for i in range(ORI_RANGE):
            c = self.oriplot.plot(pen=(i, 10))
            c.setPos(0, i * 2)
            self.oricurve.append(c)

        # Creates list for orientation recordings then starts recording buffer
        # readings. Plots acceleration buffer data on plot as well.
        self.acccurve = []
        for i in range(ACC_RANGE):
            c = self.accplot.plot(pen=(i, 10))
            c.setPos(0, i * 2)
            self.acccurve.append(c)

        # Gets current time so system knows when plots were updated
        self.lastUpdateTime = time.time()

        # Sets variable for selective plot updating
        self.window_type = 'all'

        # If first run, show plots
        if self.firstWin:
            self.show()
            self.start_listening()
            self.firstWin = False
Example #21
0
x = x * 100 - 10
y = -1 * (y * 100) + 10
z = -1 * (z * 1000 - 970)

N = max(x.shape)
pos = np.empty((N, 3))
pos[:, 0] = x
pos[:, 1] = z
pos[:, 2] = y

sp1 = gl.GLScatterPlotItem(pos=pos,
                           size=0.03,
                           color=(0.0, 0.0, 1.0, 0.5),
                           pxMode=False)
sp1.scale(0.18, 0.18, 0.18)
w.addItem(sp1)


def update():
    sp1.rotate(1.5, 0, 0, 1)


t = QtCore.QTimer()
t.timeout.connect(update)
t.start(50)

if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()
Example #22
0
 def update_xRange(self, xBegin, xEnd):
     self.emgrecord.setRange(QtCore.QRectF(xBegin, -200, xEnd, 700))
     self.accrecord.setRange(QtCore.QRectF(xBegin, -2, xEnd, 7))
     self.orirecord.setRange(QtCore.QRectF(xBegin, -1, xEnd, 8))
        set3 = self.state[10:13]
        ret = np.vstack((set1, set2, set3))
        return ret

    def get_flight_state(self):
        """
        :return: the actual flight state
         [va_mag, altitude, course_angle]
         of the uav.
        """
        flight_state = [
            self.flight_state.item(0), -self.state.item(2),
            self.flight_state.item(4)
        ]
        return flight_state


if __name__ == "__main__":
    app = app = QApplication(sys.argv)
    uav = MAVModel()

    startTimer = QtCore.QTimer()
    startTimer.timeout.connect(uav.calculate_loop)
    # startTimer.setSingleShot(True)
    timers.append(startTimer)
    startTimer.start(0.01)
    # startTimer.start(P.ts_simulation)

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()
Example #24
0
    def __init__(self, launch_command=None, plan=None, com_method=com.DDS_COM):
        super(Visualizer, self).__init__()
        self.com_method = com_method
        self.launch_command = launch_command
        self.plan = plan

        self.max_log_length = 500
        self.log_updated = False
        self.log_lines = 0
        self.log_text = ""
        self.data = dict()
        self.data_sizes = dict()
        for d in callbacks_list:
            self.data_sizes[d.time_key] = 0
            for k in d.callbacks.keys():
                self.data[k] = np.full(ARRAY_SIZE, 1e-10)

        self.columns = [[plot_types.CtlPosPlot, plot_types.PositionPlot, plot_types.FeatureCountPlot, plot_types.ConfidencePlot, \
                         plot_types.CommandStatusPlot], [plot_types.CtlRotPlot, plot_types.OrientationPlot, plot_types.CovPlot]]

        self.graphics_view = ParentGraphicsView()
        self.terminal_graphics_view = TerminalGraphicsView(self.graphics_view)
        self.terminal_view = TerminalView(self.terminal_graphics_view)

        self.layout = pg.GraphicsLayout(border=(100, 100, 100))
        self.layout.setBorder(None)
        self.layout.setZValue(-1)
        self.graphics_view.setCentralItem(self.layout)
        self.graphics_view.scene().addWidget(self.terminal_graphics_view)
        self.log_shown = False
        self.pmc_enabled = True
        self.setCentralWidget(self.graphics_view)
        self.create_menu()

        row = 0
        for c in range(len(self.columns)):
            l = self.layout.addLayout(0, c)
            l.setBorder(pg.mkColor(150, 150, 150))
            for r in range(len(self.columns[c])):
                t = self.columns[c][r]
                new_item = t()  # it gets added to l in constructor
                l.addItem(new_item)
                if r != len(self.columns[c]) - 1:
                    l.nextRow()
                else:
                    new_item.show_x_axis(True)
            self.layout.nextColumn()

        self.setWindowTitle('GNC Visualizer')

        self.settings = QtCore.QSettings("NASA", "gnc_visualizer")
        self.restoreGeometry(self.settings.value("geometry", "").toByteArray())
        self.restoreState(self.settings.value("windowState", "").toByteArray())

        QtGui.qApp.installEventFilter(self)
        # make sure initial window size includes menubar
        QtCore.QTimer.singleShot(0, self.menuBar().hide)

        self.started = False
        self.paused = False
        self.proc = None
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.tick)
        self.timer.start(40)
    def __init__(self, parent_widget):
        GraphicsLayoutWidget.__init__(self, parent_widget)
        self.parent_widget = parent_widget

        self.resize(1920, 1080)

        button_width = 19

        #######################################################################
        # Plot Channel Plotting Booleans
        #######################################################################

        self.plot_channel_one = True
        self.plot_channel_two = True
        self.plot_channel_three = True
        self.plot_channel_four = True
        self.plot_channel_five = True
        self.plot_channel_six = True
        self.plot_channel_seven = True
        self.plot_channel_eight = True

        # The position of this list corispond to the position of the sorted directory_of_frequency_channels keys
        self.plot_channel_key_booleans = [
            True for count in range(len(DICTIONARY_OF_CHANNEL_KEYS.keys()))
        ]

        #######################################################################
        # Init of linear region that can control all graphs at once
        #######################################################################
        self.linear_region = pg.LinearRegionItem([0, 3000])
        self.linear_region.setZValue(-10)

        #######################################################################
        # Init of all plot widgets
        #######################################################################

        self.frequency_plot_graph = self.addPlot(title='Frequency')
        if ADD_FREQUENCY_LEGEND:
            self.frequency_resistance_legend = self.frequency_plot_graph.addLegend(
            )
        self.nextRow()

        self.resistance_graph = self.addPlot(title='Resistance')
        self.nextRow()

        self.temperature_plot_graph = self.addPlot(title='Temperature')
        self.nextRow()

        self.pressure_plot_graph = self.addPlot(title='Pressure')
        self.nextRow()

        self.humidity_plot_graph = self.addPlot(title='Humidity')
        self.nextRow()

        self.overview_graph = self.addPlot(title='Overview')
        self.overview_graph.addItem(self.linear_region)

        self.frequency_plot_graph.showGrid(x=True, y=True)
        self.resistance_graph.showGrid(x=True, y=True)
        self.temperature_plot_graph.showGrid(x=True, y=True)
        self.pressure_plot_graph.showGrid(x=True, y=True)
        self.humidity_plot_graph.showGrid(x=True, y=True)
        self.overview_graph.showGrid(x=True, y=True)

        self.frequency_plot_graph.sigXRangeChanged.connect(
            self.update_frequency_region)
        self.resistance_graph.sigXRangeChanged.connect(
            self.update_resistance_region)
        self.temperature_plot_graph.sigXRangeChanged.connect(
            self.update_temperature_region)
        self.pressure_plot_graph.sigXRangeChanged.connect(
            self.update_pressure_region)
        self.humidity_plot_graph.sigXRangeChanged.connect(
            self.update_humidity_region)

        self.frequency_lines = []

        for position in range(0, len(DICTIONARY_OF_CHANNEL_KEYS.keys())):
            self.frequency_lines.append(
                self.frequency_plot_graph.plot(
                    x=[],
                    y=[],
                    pen=pg.mkPen(cosmetic=True,
                                 width=LINE_THICKNESS,
                                 color=LINE_COLORS[position]),
                    symbol='o',
                    symbolBrush=pg.mkBrush(LINE_COLORS[position]),
                    name='Channel %d' % position))

        self.resistance_line = self.resistance_graph.plot(
            x=[],
            y=[],
            pen=pg.mkPen(cosmetic=True,
                         width=LINE_THICKNESS,
                         color=LINE_COLORS[0]),
            symbol='o',
            symbolBrush=pg.mkBrush(LINE_COLORS[0]),
            name='Resistance')

        self.temperature_line = self.temperature_plot_graph.plot(
            x=[],
            y=[],
            pen=pg.mkPen(cosmetic=True,
                         width=LINE_THICKNESS,
                         color=LINE_COLORS[1]),
            symbol='o',
            symbolBrush=pg.mkBrush(LINE_COLORS[1]),
            name='Temperature')
        self.pressure_line = self.pressure_plot_graph.plot(
            x=[],
            y=[],
            pen=pg.mkPen(cosmetic=True,
                         width=LINE_THICKNESS,
                         color=LINE_COLORS[2]),
            symbol='o',
            symbolBrush=pg.mkBrush(LINE_COLORS[2]),
            name='Pressure')
        self.humidity_line = self.humidity_plot_graph.plot(
            x=[],
            y=[],
            pen=pg.mkPen(cosmetic=True,
                         width=LINE_THICKNESS,
                         color=LINE_COLORS[3]),
            symbol='o',
            symbolBrush=pg.mkBrush(LINE_COLORS[3]),
            name='Humidity')

        self.linear_region.sigRegionChanged.connect(
            self.update_plots_using_region)

        self.resistance_json_path = None
        self.file_path_json = SELECT_LATEST_FILE_JSON()

        #######################################################################
        # Data Processing Thread
        #######################################################################
        self.server_handler = Server_Handler(
            self, self.parent_widget.attempt_connection_button)
        self.server_handler.start()

        self.process_data_thread = Data_Processing_Stream_Thread(self)
        self.process_data_thread.start()

        #######################################################################
        # Timers
        #######################################################################
        self.plot_timer_frequency = QtCore.QTimer()
        self.plot_timer_frequency.timeout.connect(self.plot_frequency_data)
        self.plot_timer_frequency.start(1000)

        self.plot_timer_resistance = QtCore.QTimer()
        self.plot_timer_resistance.timeout.connect(self.plot_resistance_data)
        self.plot_timer_resistance.start(1000)

        self.plot_timer_temperature = QtCore.QTimer()
        self.plot_timer_temperature.timeout.connect(self.plot_temperature_data)
        self.plot_timer_temperature.start(1000)

        self.plot_timer_pressure = QtCore.QTimer()
        self.plot_timer_pressure.timeout.connect(self.plot_pressure_data)
        self.plot_timer_pressure.start(1000)

        self.plot_timer_humidity = QtCore.QTimer()
        self.plot_timer_humidity.timeout.connect(self.plot_humidity_data)
        self.plot_timer_humidity.start(1000)
Example #26
0
    name='Plot1'
)  ## giving the plots names allows us to link their axes together
l.addWidget(pw)
pw2 = pg.PlotWidget(name='Plot2')
l.addWidget(pw2)
pw3 = pg.PlotWidget()
l.addWidget(pw3)

mw.show()

## Create an empty plot curve to be filled later, set its pen
p1 = pw.plot()
p1.setPen((200, 200, 100))

## Add in some extra graphics
rect = QtGui.QGraphicsRectItem(QtCore.QRectF(0, 0, 1, 5e-11))
rect.setPen(pg.mkPen(100, 200, 100))
pw.addItem(rect)

pw.setLabel('left', 'Value', units='V')
pw.setLabel('bottom', 'Time', units='s')
pw.setXRange(0, 2)
pw.setYRange(0, 1e-10)


def rand(n):
    data = np.random.random(n)
    data[int(n * 0.1):int(n * 0.13)] += .5
    data[int(n * 0.18)] += 2
    data[int(n * 0.1):int(n * 0.13)] *= 5
    data[int(n * 0.18)] *= 20
## Create window with GraphicsView widget
win = pg.GraphicsLayoutWidget()
win.show()  ## show widget alone in its own window
win.setWindowTitle('pyqtgraph example: ImageItem')
view = win.addViewBox()

## lock the aspect ratio so pixels are always square
view.setAspectLocked(True)

## Create image item
img = pg.ImageItem(border='w')
view.addItem(img)

## Set initial view bounds
view.setRange(QtCore.QRectF(0, 0, 600, 600))

## Create random image
data = np.random.normal(size=(15, 600, 600), loc=1024,
                        scale=64).astype(np.uint16)
i = 0

updateTime = ptime.time()
fps = 0


def updateData():
    global img, data, i, updateTime, fps

    ## Display the data
    img.setImage(data[i])
Example #28
0
 def sizeHint(self):
     return QtCore.QSize(120, 0)
Example #29
0
 def boundingRect(self):
     return QtCore.QRectF(self.x0, self.y0, self.xsize, self.ysize)
Example #30
0
File: qt.py Project: pauloborges/tg
def initialize():
    global app
    app = QtGui.QApplication([])
    QtCore.pyqtRemoveInputHook()
    return app
Example #31
0
    def raiseContextMenu(self, ev):
        menu = self.getContextMenus()

        pos = ev.screenPos()
        menu.popup(QtCore.QPoint(pos.x(), pos.y()))
        return True
Example #32
0
def debug_trace():
    # http://stackoverflow.com/questions/1736015/debugging-a-pyqt4-app
    from ipdb import set_trace
    QtCore.pyqtRemoveInputHook()
    set_trace()
Example #33
0
    def __init__(self, audioPath, numTDOAs, gccPHATNLAlpha, gccPHATNLEnabled,
                 dictionariesW, dictionarySize, dictionarySizes,
                 dictionaryType, numHUpdates, localizationEnabled,
                 localizationWindowSize, gccPHATHistory, tdoaHistory,
                 inputSpectrogramHistory, outputSpectrogramHistory,
                 coefficientMaskHistories, togglePlayAudioProcessQueue,
                 togglePlayAudioProcessAck, togglePlayGCCNMFProcessQueue,
                 togglePlayGCCNMFProcessAck, tdoaParamsGCCNMFProcessQueue,
                 tdoaParamsGCCNMFProcessAck,
                 toggleSeparationGCCNMFProcessQueue,
                 toggleSeparationGCCNMFProcessAck):
        super(RealtimeGCCNMFInterfaceWindow, self).__init__()

        self.audioPath = audioPath
        logging.info('Loading interface with audio path: %s' % self.audioPath)
        self.initAudioFiles()

        self.numTDOAs = numTDOAs
        self.tdoaIndexes = np.arange(numTDOAs)
        self.dictionariesW = getVisualizedDictionariesW(dictionariesW)
        self.dictionaryTypes = self.dictionariesW.keys()

        self.dictionarySize = dictionarySize
        self.dictionarySizes = dictionarySizes
        self.dictionaryType = dictionaryType
        self.numHUpdates = numHUpdates
        self.targetTDOAIndex = self.numTDOAs / 2.0
        self.targetTDOAEpsilon = self.numTDOAs / 10.0
        self.gccPHATNLAlpha = gccPHATNLAlpha
        self.gccPHATNLEnabled = gccPHATNLEnabled
        self.localizationEnabled = localizationEnabled
        self.localizationWindowSize = localizationWindowSize

        self.gccPHATPlotTimer = QtCore.QTimer()
        self.gccPHATPlotTimer.timeout.connect(self.updateGCCPHATPlot)

        self.gccPHATHistory = gccPHATHistory
        self.gccPHATHistorySize = gccPHATHistory.size()
        self.tdoaHistory = tdoaHistory
        self.inputSpectrogramHistory = inputSpectrogramHistory
        self.outputSpectrogramHistory = outputSpectrogramHistory
        self.coefficientMaskHistories = coefficientMaskHistories

        self.togglePlayAudioProcessQueue = togglePlayAudioProcessQueue
        self.togglePlayAudioProcessAck = togglePlayAudioProcessAck
        self.togglePlayGCCNMFProcessQueue = togglePlayGCCNMFProcessQueue
        self.togglePlayGCCNMFProcessAck = togglePlayGCCNMFProcessAck
        self.tdoaParamsGCCNMFProcessQueue = tdoaParamsGCCNMFProcessQueue
        self.tdoaParamsGCCNMFProcessAck = tdoaParamsGCCNMFProcessAck
        self.toggleSeparationGCCNMFProcessQueue = toggleSeparationGCCNMFProcessQueue
        self.toggleSeparationGCCNMFProcessAck = toggleSeparationGCCNMFProcessAck

        self.playIconString = 'Play'
        self.pauseIconString = 'Pause'
        self.separationOffIconString = 'Disabled'
        self.separationOnIconString = 'Enabled'
        '''self.playIconString = u'\u23F5'
        self.pauseIconString = u'\u23F8'
        self.separationOffIconString = u'\u21F6 | \u21F6'
        self.separationOnIconString = u'\u21F6 | \u2192'''

        self.targetModeIconStrings = {
            TARGET_MODE_BOXCAR: u'\u168B',
            TARGET_MODE_MULTIPLE: u'\u168D',
            TARGET_MODE_WINDOW_FUNCTION: u'\u1109'
        }
        self.rollingImages = True

        self.initWindow()
        self.initControlWidgets()
        self.initVisualizationWidgets()
        self.initWindowLayout()

        self.localizationStateChanged()

        #self.show()
        self.showMaximized()