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_()
def resizeEvent(self, event): super(ParentGraphicsView, self).resizeEvent(event) if event != None: self.emit(QtCore.SIGNAL("resize"), event)
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),
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'
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()
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
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()
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()
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()
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()
def animate(self): timer = QtCore.QTimer() timer.timeout.connect(self.update) timer.start(10) self.start()
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
def boundingRect(self): return QtCore.QRectF(0, 0, 10, 10)
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)
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)
def boundingRect(self): #print "FlowchartGraphicsItem.boundingRect" return QtCore.QRectF()
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)
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
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_()
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_()
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)
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])
def sizeHint(self): return QtCore.QSize(120, 0)
def boundingRect(self): return QtCore.QRectF(self.x0, self.y0, self.xsize, self.ysize)
def initialize(): global app app = QtGui.QApplication([]) QtCore.pyqtRemoveInputHook() return app
def raiseContextMenu(self, ev): menu = self.getContextMenus() pos = ev.screenPos() menu.popup(QtCore.QPoint(pos.x(), pos.y())) return True
def debug_trace(): # http://stackoverflow.com/questions/1736015/debugging-a-pyqt4-app from ipdb import set_trace QtCore.pyqtRemoveInputHook() set_trace()
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()