Exemplo n.º 1
0
    def update(self):
        ''' uodate the model of the MENU (header)'''
        N_index = len(self.df.index)
        
        for name in self.df.columns:
            # append a table with three columns (Name, Data-Type and N_NaNs). Each row represents
            # a data-column in the input dataframe object

            item_name = QtGui.QStandardItem(asUnicode('{0}'.format(name.encode('UTF-8'))))
            item_name.setCheckable(True)
            item_name.setEditable(False)
            item_name.setCheckState(Qt.Checked)

            dt = self.df[name].dtype if isinstance(self.df, pd.DataFrame) else self.df.dtype
            item_type = QtGui.QStandardItem(asUnicode('{0}'.format(dt)))
            item_type.setEditable(False)
            if dt == type(object):
                # if the data-type of the column is not numeric or datetime, then it's propabply has not been
                # loaded successfully. Therefore, explicitly change the font color to red, to inform the user.
                item_type.setForeground(RED_FONT)
            
            n_nans = N_index - (self.df[name].count() if isinstance(self.df, pd.DataFrame) else self.df.count())
            item_nans = QtGui.QStandardItem(asUnicode('{0}'.format(n_nans)))
            item_nans.setEditable(False)
            if n_nans > 0:
                item_nans.setForeground(RED_FONT)

            self.appendRow([item_name, item_type, item_nans])

        self.setHorizontalHeaderLabels(['Name', 'Data-type', 'Number of NaNs'])
        self.endResetModel()
Exemplo n.º 2
0
    def buildTree(self, data, parent, name='', hideRoot=False, path=()):
        if hideRoot:
            node = parent
        else:
            node = QtGui.QTreeWidgetItem([name, "", ""])
            parent.addChild(node)
        
        # record the path to the node so it can be retrieved later
        # (this is used by DiffTreeWidget)
        self.nodes[path] = node

        typeStr, desc, childs, widget = self.parse(data)
        node.setText(1, typeStr)
        node.setText(2, desc)
            
        # Truncate description and add text box if needed
        if len(desc) > 100:
            desc = desc[:97] + '...'
            if widget is None:
                widget = QtGui.QPlainTextEdit(asUnicode(data))
                widget.setMaximumHeight(200)
                widget.setReadOnly(True)
        
        # Add widget to new subnode
        if widget is not None:
            self.widgets.append(widget)
            subnode = QtGui.QTreeWidgetItem(["", "", ""])
            node.addChild(subnode)
            self.setItemWidget(subnode, 0, widget)
            self.setFirstItemColumnSpanned(subnode, True)
            
        # recurse to children
        for key, data in childs.items():
            self.buildTree(data, node, asUnicode(key), path=path+(key,))
Exemplo n.º 3
0
    def update(self):
        ''' uodate the model of the MENU (header)'''
        N_index = len(self.df.index)

        for name in self.df.columns:
            # append a table with three columns (Name, Data-Type and N_NaNs). Each row represents
            # a data-column in the input dataframe object

            item_name = QtGui.QStandardItem(
                asUnicode('{0}'.format(name.encode('UTF-8'))))
            item_name.setCheckable(True)
            item_name.setEditable(False)
            item_name.setCheckState(Qt.Checked)

            dt = self.df[name].dtype if isinstance(
                self.df, pd.DataFrame) else self.df.dtype
            item_type = QtGui.QStandardItem(asUnicode('{0}'.format(dt)))
            item_type.setEditable(False)
            if dt == type(object):
                # if the data-type of the column is not numeric or datetime, then it's propabply has not been
                # loaded successfully. Therefore, explicitly change the font color to red, to inform the user.
                item_type.setForeground(RED_FONT)

            n_nans = N_index - (self.df[name].count() if isinstance(
                self.df, pd.DataFrame) else self.df.count())
            item_nans = QtGui.QStandardItem(asUnicode('{0}'.format(n_nans)))
            item_nans.setEditable(False)
            if n_nans > 0:
                item_nans.setForeground(RED_FONT)

            self.appendRow([item_name, item_type, item_nans])

        self.setHorizontalHeaderLabels(['Name', 'Data-type', 'Number of NaNs'])
        self.endResetModel()
Exemplo n.º 4
0
 def updateDisplayLabel(self, value=None):
     """Update the display label to reflect the value of the parameter."""
     if value is None:
         value = self.param.value()
     opts = self.param.opts
     if isinstance(self.widget, QtGui.QAbstractSpinBox):
         text = asUnicode(self.widget.lineEdit().text())
     elif isinstance(self.widget, QtGui.QComboBox):
         text = self.widget.currentText()
     else:
         text = asUnicode(value)
     self.displayLabel.setText(text)
Exemplo n.º 5
0
 def parse(self, data):
     """
     Given any python object, return:
     * type
     * a short string representation
     * a dict of sub-objects to be parsed
     * optional widget to display as sub-node
     """
     # defaults for all objects
     typeStr = type(data).__name__
     if typeStr == 'instance':
         typeStr += ": " + data.__class__.__name__
     widget = None
     desc = ""
     childs = {}
     
     # type-specific changes
     if isinstance(data, dict):
         desc = "length=%d" % len(data)
         if isinstance(data, OrderedDict):
             childs = data
         else:
             childs = OrderedDict(sorted(data.items()))
     elif isinstance(data, (list, tuple)):
         desc = "length=%d" % len(data)
         childs = OrderedDict(enumerate(data))
     elif HAVE_METAARRAY and (hasattr(data, 'implements') and data.implements('MetaArray')):
         childs = OrderedDict([
             ('data', data.view(np.ndarray)),
             ('meta', data.infoCopy())
         ])
     elif isinstance(data, np.ndarray):
         desc = "shape=%s dtype=%s" % (data.shape, data.dtype)
         table = TableWidget()
         table.setData(data)
         table.setMaximumHeight(200)
         widget = table
     elif isinstance(data, types.TracebackType):  ## convert traceback to a list of strings
         frames = list(map(str.strip, traceback.format_list(traceback.extract_tb(data))))
         #childs = OrderedDict([
             #(i, {'file': child[0], 'line': child[1], 'function': child[2], 'code': child[3]})
             #for i, child in enumerate(frames)])
         #childs = OrderedDict([(i, ch) for i,ch in enumerate(frames)])
         widget = QtGui.QPlainTextEdit(asUnicode('\n'.join(frames)))
         widget.setMaximumHeight(200)
         widget.setReadOnly(True)
     else:
         desc = asUnicode(data)
     
     return typeStr, desc, childs, widget
     
Exemplo n.º 6
0
    def on_selectFile_valueChanged(self, value):
        button  = self.param('Select File').items.items()[0][0].button
        fname = asUnicode(self.param('Select File').value()).encode('UTF-8')
        self._parent.sigUIStateChanged.emit(self)

        if fname is not None and os.path.isfile(fname):
            #print fname, type(fname)
            #print u'File is selected: {0}'.format(fname)
            #print type(asUnicode('File is selected: {0}'.format(fname)))
            button.setToolTip(asUnicode('File is selected: {0}'.format(fname)))
            button.setStatusTip(asUnicode('File is selected: {0}'.format(fname)))
        else:
            button.setToolTip(asUnicode('Select File'))
            button.setStatusTip(asUnicode('Select File'))
Exemplo n.º 7
0
 def fileSaveFinished(self, fileName):
     fileName = asUnicode(fileName)
     global LastExportDirectory
     LastExportDirectory = os.path.split(fileName)[0]
     
     ## If file name does not match selected extension, append it now
     ext = os.path.splitext(fileName)[1].lower().lstrip('.')
     selectedExt = re.search(r'\*\.(\w+)\b', asUnicode(self.fileDialog.selectedNameFilter()))
     if selectedExt is not None:
         selectedExt = selectedExt.groups()[0].lower()
         if ext != selectedExt:
             fileName = fileName + '.' + selectedExt.lstrip('.')
     
     self.export(fileName=fileName, **self.fileDialog.opts)
Exemplo n.º 8
0
 def fileSaveFinished(self, fileName):
     fileName = asUnicode(fileName)
     global LastExportDirectory
     LastExportDirectory = os.path.split(fileName)[0]
     
     ## If file name does not match selected extension, append it now
     ext = os.path.splitext(fileName)[1].lower().lstrip('.')
     selectedExt = re.search(r'\*\.(\w+)\b', asUnicode(self.fileDialog.selectedNameFilter()))
     if selectedExt is not None:
         selectedExt = selectedExt.groups()[0].lower()
         if ext != selectedExt:
             fileName = fileName + '.' + selectedExt.lstrip('.')
     
     self.export(fileName=fileName, **self.fileDialog.opts)
Exemplo n.º 9
0
    def labelString(self):
        if self.labelUnits == "":
            if self.scale == 1.0:
                units = ""
            else:
                units = asUnicode("(x%g)") % (1.0 / self.scale)
        else:
            # print repr(self.labelUnitPrefix), repr(self.labelUnits)
            units = asUnicode("(%s%s)") % (self.labelUnitPrefix, self.labelUnits)

        s = asUnicode("%s %s") % (self.labelText, units)

        style = ";".join(["%s: %s" % (k, self.labelStyle[k]) for k in self.labelStyle])

        return asUnicode("<span style='%s'>%s</span>") % (style, s)
Exemplo n.º 10
0
 def labelString(self):
     if self.labelUnits == '':
         if self.scale == 1.0:
             units = ''
         else:
             units = asUnicode('(x%g)') % (1.0/self.scale)
     else:
         #print repr(self.labelUnitPrefix), repr(self.labelUnits)
         units = asUnicode('(%s%s)') % (self.labelUnitPrefix, self.labelUnits)
         
     s = asUnicode('%s %s') % (self.labelText, units)
     
     style = ';'.join(['%s: %s' % (k, self.labelStyle[k]) for k in self.labelStyle])
     
     return asUnicode("<span style='%s'>%s</span>") % (style, s)
Exemplo n.º 11
0
 def setViewList(self, views):
     names = ['']
     self.viewMap.clear()
     
     ## generate list of views to show in the link combo
     for v in views:
         name = v.name
         if name is None:  ## unnamed views do not show up in the view list (although they are linkable)
             continue
         names.append(name)
         self.viewMap[name] = v
         
     for i in [0,1]:
         c = self.ctrl[i].linkCombo
         current = asUnicode(c.currentText())
         c.blockSignals(True)
         changed = True
         try:
             c.clear()
             for name in names:
                 c.addItem(name)
                 if name == current:
                     changed = False
                     c.setCurrentIndex(c.count()-1)
         finally:
             c.blockSignals(False)
             
         if changed:
             c.setCurrentIndex(0)
             c.currentIndexChanged.emit(c.currentIndex())
Exemplo n.º 12
0
 def headerData(self, section, orientation, role=Qt.DisplayRole):
     if role == Qt.DisplayRole:
         if orientation == Qt.Horizontal:
             return self.df.columns[section]
         elif orientation == Qt.Vertical:
             return asUnicode('{0} | {1}'.format(section, self.df.index.tolist()[section]))
     return QtCore.QVariant()
Exemplo n.º 13
0
 def iteratorFn(self, data):
     ## Return 1) a function that will provide an iterator for data and 2) a list of header strings
     if isinstance(data, list) or isinstance(data, tuple):
         return lambda d: d.__iter__(), None
     elif isinstance(data, dict):
         return lambda d: iter(d.values()), list(map(
             asUnicode, data.keys()))
     elif (hasattr(data, 'implements') and data.implements('MetaArray')):
         if data.axisHasColumns(0):
             header = [
                 asUnicode(data.columnName(0, i))
                 for i in range(data.shape[0])
             ]
         elif data.axisHasValues(0):
             header = list(map(asUnicode, data.xvals(0)))
         else:
             header = None
         return self.iterFirstAxis, header
     elif isinstance(data, np.ndarray):
         return self.iterFirstAxis, None
     elif isinstance(data, np.void):
         return self.iterate, list(map(asUnicode, data.dtype.names))
     elif data is None:
         return (None, None)
     elif np.isscalar(data):
         return self.iterateScalar, None
     else:
         msg = "Don't know how to iterate over data type: {!s}".format(
             type(data))
         raise TypeError(msg)
Exemplo n.º 14
0
 def setMinimum(self, m, update=True):
     """Set the minimum allowed value (or None for no limit)"""
     if m is not None:
         m = D(asUnicode(m))
     self.opts['bounds'][0] = m
     if update:
         self.setValue()
Exemplo n.º 15
0
 def setMinimum(self, m, update=True):
     """Set the minimum allowed value (or None for no limit)"""
     if m is not None:
         m = D(asUnicode(m))
     self.opts['bounds'][0] = m
     if update:
         self.setValue()
Exemplo n.º 16
0
    def setViewList(self, views):
        names = ['']
        self.viewMap.clear()

        ## generate list of views to show in the link combo
        for v in views:
            name = v.name
            if name is None:  ## unnamed views do not show up in the view list (although they are linkable)
                continue
            names.append(name)
            self.viewMap[name] = v

        for i in [0, 1]:
            c = self.ctrl[i].linkCombo
            current = asUnicode(c.currentText())
            c.blockSignals(True)
            changed = True
            try:
                c.clear()
                for name in names:
                    c.addItem(name)
                    if name == current:
                        changed = False
                        c.setCurrentIndex(c.count() - 1)
            finally:
                c.blockSignals(False)

            if changed:
                c.setCurrentIndex(0)
                c.currentIndexChanged.emit(c.currentIndex())
    def labelString(self):
        if self.labelUnits == "":
            if not self.autoSIPrefix or self.autoSIPrefixScale == 1.0:
                units = ""
            else:
                units = asUnicode("[x%g]") % (1.0/self.autoSIPrefixScale)
        else:
            units = asUnicode("[%s%s]") % (asUnicode(self.labelUnitPrefix), asUnicode(self.labelUnits))

        s_label = asUnicode("%s %s") % (asUnicode(self.labelText), asUnicode(units))
        s_style = ";".join(["%s: %s" % (k, self.labelStyle[k]) for k in self.labelStyle])

        return asUnicode("<span style='%s'>%s</span>") % (s_style, asUnicode(s_label))
Exemplo n.º 18
0
 def execCmd(self):
     cmd = asUnicode(self.text())
     if len(self.history) == 1 or cmd != self.history[1]:
         self.history.insert(1, cmd)
     #self.lastCmd = cmd
     self.history[0] = ""
     self.setHistory(0)
     self.sigExecuteCmd.emit(cmd)
Exemplo n.º 19
0
    def labelString(self):
        if self.labelUnits == '':
            if self.scale == 1.0:
                units = ''
            else:
                units = asUnicode('(x%g)') % (1.0 / self.scale)
        else:
            #print repr(self.labelUnitPrefix), repr(self.labelUnits)
            units = asUnicode('(%s%s)') % (self.labelUnitPrefix,
                                           self.labelUnits)

        s = asUnicode('%s %s') % (self.labelText, units)

        style = ';'.join(
            ['%s: %s' % (k, self.labelStyle[k]) for k in self.labelStyle])

        return asUnicode("<span style='%s'>%s</span>") % (style, s)
Exemplo n.º 20
0
    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()
Exemplo n.º 21
0
 def addReagent(self, item):
     names = self.db.reagents.names()
     i = 0
     while True:
         name = 'new_reagent_%d' % i
         if name not in names:
             break
         i += 1
     self.db.reagents.add(name=name, group=asUnicode(item.text(0)))
Exemplo n.º 22
0
    def labelString(self):
#         if self.labelUnits == '':
#             if not self.autoSIPrefix or self.autoSIPrefixScale == 1.0:
#                 units = ''
#             else:
#                 units = asUnicode('(x%g)') % (1.0/self.autoSIPrefixScale)
#         else:
#             #print repr(self.labelUnitPrefix), repr(self.labelUnits)
#             units = asUnicode('(%s%s)') % (asUnicode(self.labelUnitPrefix), asUnicode(self.labelUnits))
            
#        s = asUnicode('%s %s') % (asUnicode(self.labelText), asUnicode(units))
        s = asUnicode(self.labelText)
        
        style = ';'.join(['%s: %s' % (k, self.labelStyle[k]) for k in self.labelStyle])
        
        return asUnicode("<span style='%s'>%s</span>") % (style, asUnicode(s))
       
        
Exemplo n.º 23
0
 def headerData(self, section, orientation, role=Qt.DisplayRole):
     if role == Qt.DisplayRole:
         if orientation == Qt.Horizontal:
             return self.df.columns[section]
         elif orientation == Qt.Vertical:
             return asUnicode('{0} | {1}'.format(
                 section,
                 self.df.index.tolist()[section]))
     return QtCore.QVariant()
Exemplo n.º 24
0
    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()
Exemplo n.º 25
0
 def addChanged(self):
     """Called when "add new" combo is changed
     The parameter MUST have an 'addNew' method defined.
     """
     if self.addWidget.currentIndex() == 0:
         return
     typ = asUnicode(self.addWidget.currentText())
     self.param.addNew(typ)
     self.addWidget.setCurrentIndex(0)
Exemplo n.º 26
0
 def format(self):
     if callable(self._format):
         return self._format(self)
     if isinstance(self.value, (float, np.floating)):
         if self._format is None:
             return self._defaultFormat % self.value
         else:
             return self._format % self.value
     else:
         return asUnicode(self.value)
Exemplo n.º 27
0
 def value(self):
     #vals = self.param.opts['limits']
     key = asUnicode(self.widget.currentText())
     #if isinstance(vals, dict):
         #return vals[key]
     #else:
         #return key
     #print key, self.forward
     
     return self.forward.get(key, None)
Exemplo n.º 28
0
    def validate(self, 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', '')
                if len(suff) > 0 and asUnicode(strn)[-len(suff):] != suff:
                    #print '"%s" != "%s"' % (unicode(strn)[-len(suff):], suff)
                    ret = QtGui.QValidator.Invalid

                ## 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)
Exemplo n.º 29
0
 def validate(self, 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', '')
             if len(suff) > 0 and asUnicode(strn)[-len(suff):] != suff:
                 #print '"%s" != "%s"' % (unicode(strn)[-len(suff):], suff)
                 ret = QtGui.QValidator.Invalid
                 
             ## 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)
Exemplo n.º 30
0
    def export(self, fileName=None, toBytes=False, copy=False):
        if toBytes is False and copy is False and fileName is None:
            self.fileSaveDialog(filter="Scalable Vector Graphics (*.svg)")
            return
        #self.svg = QtSvg.QSvgGenerator()
        #self.svg.setFileName(fileName)
        #dpi = QtGui.QDesktopWidget().physicalDpiX()
        ### not really sure why this works, but it seems to be important:
        #self.svg.setSize(QtCore.QSize(self.params['width']*dpi/90., self.params['height']*dpi/90.))
        #self.svg.setResolution(dpi)
        ##self.svg.setViewBox()
        #targetRect = QtCore.QRect(0, 0, self.params['width'], self.params['height'])
        #sourceRect = self.getSourceRect()
        
        #painter = QtGui.QPainter(self.svg)
        #try:
            #self.setExportMode(True)
            #self.render(painter, QtCore.QRectF(targetRect), sourceRect)
        #finally:
            #self.setExportMode(False)
        #painter.end()

        ## Workaround to set pen widths correctly
        #data = open(fileName).readlines()
        #for i in range(len(data)):
            #line = data[i]
            #m = re.match(r'(<g .*)stroke-width="1"(.*transform="matrix\(([^\)]+)\)".*)', line)
            #if m is not None:
                ##print "Matched group:", line
                #g = m.groups()
                #matrix = list(map(float, g[2].split(',')))
                ##print "matrix:", matrix
                #scale = max(abs(matrix[0]), abs(matrix[3]))
                #if scale == 0 or scale == 1.0:
                    #continue
                #data[i] = g[0] + ' stroke-width="%0.2g" ' % (1.0/scale) + g[1] + '\n'
                ##print "old line:", line
                ##print "new line:", data[i]
        #open(fileName, 'w').write(''.join(data))
        
        ## Qt's SVG generator is not complete. (notably, it lacks clipping)
        ## Instead, we will use Qt to generate SVG for each item independently,
        ## then manually reconstruct the entire document.
        xml = generateSvg(self.item)
        
        if toBytes:
            return xml.encode('UTF-8')
        elif copy:
            md = QtCore.QMimeData()
            md.setData('image/svg+xml', QtCore.QByteArray(xml.encode('UTF-8')))
            QtGui.QApplication.clipboard().setMimeData(md)
        else:
            with open(fileName, 'wb') as fh:
                fh.write(asUnicode(xml).encode('utf-8'))
Exemplo n.º 31
0
    def setModelData(self, editor, model, col):
        fname, ftype = self.fields.items()[col]

        # string / float types need to be handled differently
        if ftype.kind in 'uif':
            v = float(str(editor.text()))
            self.setText(col, '%0.2f' % v)
        else:
            v = asUnicode(editor.text())
            self.setText(col, v)
        self.sigChanged.emit(self, fname, v)
Exemplo n.º 32
0
    def export(self, fileName=None, toBytes=False, copy=False):
        if toBytes is False and copy is False and fileName is None:
            self.fileSaveDialog(filter="Scalable Vector Graphics (*.svg)")
            return
        #self.svg = QtSvg.QSvgGenerator()
        #self.svg.setFileName(fileName)
        #dpi = QtGui.QDesktopWidget().physicalDpiX()
        ### not really sure why this works, but it seems to be important:
        #self.svg.setSize(QtCore.QSize(self.params['width']*dpi/90., self.params['height']*dpi/90.))
        #self.svg.setResolution(dpi)
        ##self.svg.setViewBox()
        #targetRect = QtCore.QRect(0, 0, self.params['width'], self.params['height'])
        #sourceRect = self.getSourceRect()

        #painter = QtGui.QPainter(self.svg)
        #try:
        #self.setExportMode(True)
        #self.render(painter, QtCore.QRectF(targetRect), sourceRect)
        #finally:
        #self.setExportMode(False)
        #painter.end()

        ## Workaround to set pen widths correctly
        #data = open(fileName).readlines()
        #for i in range(len(data)):
        #line = data[i]
        #m = re.match(r'(<g .*)stroke-width="1"(.*transform="matrix\(([^\)]+)\)".*)', line)
        #if m is not None:
        ##print "Matched group:", line
        #g = m.groups()
        #matrix = list(map(float, g[2].split(',')))
        ##print "matrix:", matrix
        #scale = max(abs(matrix[0]), abs(matrix[3]))
        #if scale == 0 or scale == 1.0:
        #continue
        #data[i] = g[0] + ' stroke-width="%0.2g" ' % (1.0/scale) + g[1] + '\n'
        ##print "old line:", line
        ##print "new line:", data[i]
        #open(fileName, 'w').write(''.join(data))

        ## Qt's SVG generator is not complete. (notably, it lacks clipping)
        ## Instead, we will use Qt to generate SVG for each item independently,
        ## then manually reconstruct the entire document.
        xml = generateSvg(self.item)

        if toBytes:
            return xml.encode('UTF-8')
        elif copy:
            md = QtCore.QMimeData()
            md.setData('image/svg+xml', QtCore.QByteArray(xml.encode('UTF-8')))
            QtGui.QApplication.clipboard().setMimeData(md)
        else:
            with open(fileName, 'wb') as fh:
                fh.write(asUnicode(xml).encode('utf-8'))
Exemplo n.º 33
0
    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()
Exemplo n.º 34
0
    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()
Exemplo n.º 35
0
    def serialize(self, useSelection=False):
        """Convert entire table (or just selected area) into tab-separated text values"""
        if useSelection:
            selection = self.selectedRanges()[0]
            rows = list(range(selection.topRow(),
                              selection.bottomRow() + 1))
            columns = list(range(selection.leftColumn(),
                                 selection.rightColumn() + 1))        
        else:
            rows = list(range(self.rowCount()))
            columns = list(range(self.columnCount()))


        data = []
        if self.horizontalHeadersSet:
            row = []
            if self.verticalHeadersSet:
                row.append(asUnicode(''))
            
            for c in columns:
                row.append(asUnicode(self.horizontalHeaderItem(c).text()))
            data.append(row)
        
        for r in rows:
            row = []
            if self.verticalHeadersSet:
                row.append(asUnicode(self.verticalHeaderItem(r).text()))
            for c in columns:
                item = self.item(r, c)
                if item is not None:
                    row.append(asUnicode(item.value))
                else:
                    row.append(asUnicode(''))
            data.append(row)
            
        s = ''
        for row in data:
            s += ('\t'.join(row) + '\n')
        return s
Exemplo n.º 36
0
    def serialize(self, useSelection=False):
        """Convert entire table (or just selected area) into tab-separated text values"""
        if useSelection:
            selection = self.selectedRanges()[0]
            rows = list(range(selection.topRow(), selection.bottomRow() + 1))
            columns = list(
                range(selection.leftColumn(),
                      selection.rightColumn() + 1))
        else:
            rows = list(range(self.rowCount()))
            columns = list(range(self.columnCount()))

        data = []
        if self.horizontalHeadersSet:
            row = []
            if self.verticalHeadersSet:
                row.append(asUnicode(''))

            for c in columns:
                row.append(asUnicode(self.horizontalHeaderItem(c).text()))
            data.append(row)

        for r in rows:
            row = []
            if self.verticalHeadersSet:
                row.append(asUnicode(self.verticalHeaderItem(r).text()))
            for c in columns:
                item = self.item(r, c)
                if item is not None:
                    row.append(asUnicode(item.value))
                else:
                    row.append(asUnicode(''))
            data.append(row)

        s = ''
        for row in data:
            s += ('\t'.join(row) + '\n')
        return s
Exemplo n.º 37
0
 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)
Exemplo n.º 38
0
 def mapping(limits):
     ## Return forward and reverse mapping dictionaries given a limit specification
     forward = OrderedDict()  ## name: value
     reverse = OrderedDict()  ## value: name
     if isinstance(limits, dict):
         for k, v in limits.items():
             forward[k] = v
             reverse[v] = k
     else:
         for v in limits:
             n = asUnicode(v)
             forward[n] = v
             reverse[v] = n
     return forward, reverse
Exemplo n.º 39
0
 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)
Exemplo n.º 40
0
 def keyPressEvent(self, ev):
     #print "press:", ev.key(), QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, QtCore.Qt.Key_Enter
     if ev.key() == QtCore.Qt.Key_Up and self.ptr < len(self.history) - 1:
         self.setHistory(self.ptr+1)
         ev.accept()
         return
     elif ev.key() ==  QtCore.Qt.Key_Down and self.ptr > 0:
         self.setHistory(self.ptr-1)
         ev.accept()
         return
     elif ev.key() == QtCore.Qt.Key_Return:
         self.execCmd()
     else:
         QtGui.QLineEdit.keyPressEvent(self, ev)
         self.history[0] = asUnicode(self.text())
Exemplo n.º 41
0
 def mapping(limits):
     ## Return forward and reverse mapping objects given a limit specification
     forward = OrderedDict()  ## {name: value, ...}
     reverse = ([], [])       ## ([value, ...], [name, ...])
     if isinstance(limits, dict):
         for k, v in limits.items():
             forward[k] = v
             reverse[0].append(v)
             reverse[1].append(k)
     else:
         for v in limits:
             n = asUnicode(v)
             forward[n] = v
             reverse[0].append(v)
             reverse[1].append(n)
     return forward, reverse
Exemplo n.º 42
0
def test_types():
    paramSpec = [
        dict(name='float', type='float'),
        dict(name='int', type='int'),
        dict(name='str', type='str'),
        dict(name='list', type='list', values=['x','y','z']),
        dict(name='dict', type='list', values={'x':1, 'y':3, 'z':7}),
        dict(name='bool', type='bool'),
        dict(name='color', type='color'),
    ]
    
    param = pt.Parameter.create(name='params', type='group', children=paramSpec)
    tree = pt.ParameterTree()
    tree.setParameters(param)

    all_objs = {
        'int0': 0, 'int':7, 'float': -0.35, 'bigfloat': 1e129, 'npfloat': np.float(5), 
        'npint': np.int(5),'npinf': np.inf, 'npnan': np.nan, 'bool': True, 
        'complex': 5+3j, 'str': 'xxx', 'unicode': asUnicode('µ'), 
        'list': [1,2,3], 'dict': {'1': 2}, 'color': pg.mkColor('k'), 
        'brush': pg.mkBrush('k'), 'pen': pg.mkPen('k'), 'none': None
    }
    if hasattr(QtCore, 'QString'):
        all_objs['qstring'] = QtCore.QString('xxxµ')

    # float
    types = ['int0', 'int', 'float', 'bigfloat', 'npfloat', 'npint', 'npinf', 'npnan', 'bool']
    check_param_types(param.child('float'), float, float, 0.0, all_objs, types)

    # int
    types = ['int0', 'int', 'float', 'bigfloat', 'npfloat', 'npint', 'bool']
    inttyps = int if sys.version[0] >= '3' else (int, long) 
    check_param_types(param.child('int'), inttyps, int, 0, all_objs, types)
    
    # str  (should be able to make a string out of any type)
    types = all_objs.keys()
    strtyp = str if sys.version[0] >= '3' else unicode
    check_param_types(param.child('str'), strtyp, asUnicode, '', all_objs, types)
    
    # bool  (should be able to make a boolean out of any type?)
    types = all_objs.keys()
    check_param_types(param.child('bool'), bool, bool, False, all_objs, types)

    # color
    types = ['color', 'int0', 'int', 'float', 'npfloat', 'npint', 'list']
    init = QtGui.QColor(128, 128, 128, 255)
    check_param_types(param.child('color'), QtGui.QColor, pg.mkColor, init, all_objs, types)
Exemplo n.º 43
0
def test_types():
    paramSpec = [
        dict(name='float', type='float'),
        dict(name='int', type='int'),
        dict(name='str', type='str'),
        dict(name='list', type='list', values=['x','y','z']),
        dict(name='dict', type='list', values={'x':1, 'y':3, 'z':7}),
        dict(name='bool', type='bool'),
        dict(name='color', type='color'),
    ]
    
    param = pt.Parameter.create(name='params', type='group', children=paramSpec)
    tree = pt.ParameterTree()
    tree.setParameters(param)

    all_objs = {
        'int0': 0, 'int':7, 'float': -0.35, 'bigfloat': 1e129, 'npfloat': np.float(5), 
        'npint': np.int(5),'npinf': np.inf, 'npnan': np.nan, 'bool': True, 
        'complex': 5+3j, 'str': 'xxx', 'unicode': asUnicode('µ'), 
        'list': [1,2,3], 'dict': {'1': 2}, 'color': pg.mkColor('k'), 
        'brush': pg.mkBrush('k'), 'pen': pg.mkPen('k'), 'none': None
    }
    if hasattr(QtCore, 'QString'):
        all_objs['qstring'] = QtCore.QString('xxxµ')

    # float
    types = ['int0', 'int', 'float', 'bigfloat', 'npfloat', 'npint', 'npinf', 'npnan', 'bool']
    check_param_types(param.child('float'), float, float, 0.0, all_objs, types)

    # int
    types = ['int0', 'int', 'float', 'bigfloat', 'npfloat', 'npint', 'bool']
    inttyps = int if sys.version[0] >= '3' else (int, long) 
    check_param_types(param.child('int'), inttyps, int, 0, all_objs, types)
    
    # str  (should be able to make a string out of any type)
    types = all_objs.keys()
    strtyp = str if sys.version[0] >= '3' else unicode
    check_param_types(param.child('str'), strtyp, asUnicode, '', all_objs, types)
    
    # bool  (should be able to make a boolean out of any type?)
    types = all_objs.keys()
    check_param_types(param.child('bool'), bool, bool, False, all_objs, types)

    # color
    types = ['color', 'int0', 'int', 'float', 'npfloat', 'npint', 'list']
    init = QtGui.QColor(128, 128, 128, 255)
    check_param_types(param.child('color'), QtGui.QColor, pg.mkColor, init, all_objs, types)
Exemplo n.º 44
0
    def __init__(self, reagent):
        class SigProxy(QtCore.QObject):
            sigChanged = QtCore.Signal(object, object,
                                       object)  # self, field, value

        self._sigprox = SigProxy()
        self.sigChanged = self._sigprox.sigChanged

        self.reagent = reagent
        fields = reagent.fields
        self.fields = OrderedDict([(f, fields[f]) for f in fields
                                   if f != 'group'])

        strs = [asUnicode(reagent[f]) for f in self.fields]
        QtGui.QTreeWidgetItem.__init__(self, strs)
        self.setFlags(self.flags() | QtCore.Qt.ItemIsEnabled
                      | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsSelectable)
Exemplo n.º 45
0
 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
Exemplo n.º 46
0
    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
Exemplo n.º 47
0
    def export(self, fileName=None, toBytes=False, copy=False):
        if toBytes is False and copy is False and fileName is None:
            self.fileSaveDialog(filter="Scalable Vector Graphics (*.svg)")
            return

        ## Qt's SVG generator is not complete. (notably, it lacks clipping)
        ## Instead, we will use Qt to generate SVG for each item independently,
        ## then manually reconstruct the entire document.
        xml = generateSvg(self.item)

        if toBytes:
            return xml.encode('UTF-8')
        elif copy:
            md = QtCore.QMimeData()
            md.setData('image/svg+xml', QtCore.QByteArray(xml.encode('UTF-8')))
            QtGui.QApplication.clipboard().setMimeData(md)
        else:
            with open(fileName, 'wb') as fh:
                fh.write(asUnicode(xml).encode('utf-8'))
Exemplo n.º 48
0
    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
Exemplo n.º 49
0
 def siEval(s):
     """
     Convert a value written in SI notation to its equivalent prefixless value
     
     Example::
     
         siEval("100 μV")  # returns 0.0001
     """
     
     s = asUnicode(s)
     m = re.match(r'(-?((\d+(\.\d*)?)|(\.\d+))([eE][-+]?\d+)?)\s*([u' + SI_PREFIXES + r']?).*$', s)
     if m is None:
         raise Exception("Can't convert string '%s' to number." % s)
     v = float(m.groups()[0])
     p = m.groups()[6]
     #if p not in SI_PREFIXES:
         #raise Exception("Can't convert string '%s' to number--unknown prefix." % s)
     if p ==  '':
         n = 0
     elif p == 'u':
         n = -2
     else:
         n = SI_PREFIXES.index(p) - 8
     return v * 1000**n
Exemplo n.º 50
0
 def makeWidget(self):
     """
     Return a single widget that should be placed in the second tree column.
     The widget must be given three attributes:
     
     ==========  ============================================================
     sigChanged  a signal that is emitted when the widget's value is changed
     value       a function that returns the value
     setValue    a function that sets the value
     ==========  ============================================================
         
     This is a good function to override in subclasses.
     """
     opts = self.param.opts
     t = opts['type']
     if t == 'int':
         defs = {
             'value': 0, 'min': None, 'max': None, 'int': True, 
             'step': 1.0, 'minStep': 1.0, 'dec': False, 
             'siPrefix': False, 'suffix': ''
         } 
         defs.update(opts)
         if 'limits' in opts:
             defs['bounds'] = opts['limits']
         w = SpinBox()
         w.setOpts(**defs)
         w.sigChanged = w.sigValueChanged
         w.sigChanging = w.sigValueChanging
     elif t == 'float':
         defs = {
             'value': 0, 'min': None, 'max': None, 
             'step': 1.0, 'dec': False, 
             'siPrefix': False, 'suffix': ''
         }
         defs.update(opts)
         if 'limits' in opts:
             defs['bounds'] = opts['limits']
         w = SpinBox()
         w.setOpts(**defs)
         w.sigChanged = w.sigValueChanged
         w.sigChanging = w.sigValueChanging
     elif t == 'bool':
         w = QtGui.QCheckBox()
         w.sigChanged = w.toggled
         w.value = w.isChecked
         w.setValue = w.setChecked
         self.hideWidget = False
     elif t == 'str':
         w = QtGui.QLineEdit()
         w.sigChanged = w.editingFinished
         w.value = lambda: asUnicode(w.text())
         w.setValue = lambda v: w.setText(asUnicode(v))
         w.sigChanging = w.textChanged
     elif t == 'color':
         w = ColorButton()
         w.sigChanged = w.sigColorChanged
         w.sigChanging = w.sigColorChanging
         w.value = w.color
         w.setValue = w.setColor
         self.hideWidget = False
         w.setFlat(True)
     elif t == 'colormap':
         from pyqtgraph.widgets.GradientWidget import GradientWidget ## need this here to avoid import loop
         w = GradientWidget(orientation='bottom')
         w.sigChanged = w.sigGradientChangeFinished
         w.sigChanging = w.sigGradientChanged
         w.value = w.colorMap
         w.setValue = w.setColorMap
         self.hideWidget = False
     else:
         raise Exception("Unknown type '%s'" % asUnicode(t))
     return w
Exemplo n.º 51
0
    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)
Exemplo n.º 52
0
    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'])