def setPageSize(self): """Helper for setting document/page widget size. This is called by the widget after receiving updateControlItem """ s = self.widget.settings # get margins in pixels width = self.posn[2] - self.posn[0] height = self.posn[3] - self.posn[1] # set up fake painter containing veusz scalings helper = document.PaintHelper(self.pagesize, scaling=self.scaling, dpi=self.dpi) # convert to physical units width = s.get('width').convertInverse(width, helper) height = s.get('height').convertInverse(height, helper) # modify widget margins operations = ( document.OperationSettingSet(s.get('width'), width), document.OperationSettingSet(s.get('height'), height), ) self.widget.document.applyOperation( document.OperationMultiple(operations, descr=_('change page size')))
def setWidgetMargins(self): """A helpful routine for setting widget margins after moving or resizing. This is called by the widget after receiving updateControlItem """ s = self.widget.settings # get margins in pixels left = self.posn[0] - self.maxposn[0] right = self.maxposn[2] - self.posn[2] top = self.posn[1] - self.maxposn[1] bottom = self.maxposn[3] - self.posn[3] # set up fake painthelper containing veusz scalings helper = document.PaintHelper(self.pagesize, scaling=self.scaling, dpi=self.dpi) # convert to physical units left = s.get('leftMargin').convertInverse(left, helper) right = s.get('rightMargin').convertInverse(right, helper) top = s.get('topMargin').convertInverse(top, helper) bottom = s.get('bottomMargin').convertInverse(bottom, helper) # modify widget margins operations = (document.OperationSettingSet(s.get('leftMargin'), left), document.OperationSettingSet(s.get('rightMargin'), right), document.OperationSettingSet(s.get('topMargin'), top), document.OperationSettingSet(s.get('bottomMargin'), bottom)) self.widget.document.applyOperation( document.OperationMultiple(operations, descr=_('resize margins')))
def updateControlItem(self, cgi): """If control item is moved or resized, this is called.""" s = self.settings # calculate new position coordinate for item xpos, ypos = self._getGraphCoords(cgi.widgetposn, cgi.posn[0], cgi.posn[1]) if xpos is None or ypos is None: return xw = abs(cgi.dims[0] / (cgi.widgetposn[2] - cgi.widgetposn[0])) yw = abs(cgi.dims[1] / (cgi.widgetposn[1] - cgi.widgetposn[3])) # actually do the adjustment on the document xp, yp = list(s.xPos), list(s.yPos) w, h, r = list(s.width), list(s.height), list(s.rotate) xp[cgi.index] = xpos yp[cgi.index] = ypos w[min(cgi.index, len(w) - 1)] = xw h[min(cgi.index, len(h) - 1)] = yw r[min(cgi.index, len(r) - 1)] = cgi.angle operations = (document.OperationSettingSet(s.get('xPos'), xp), document.OperationSettingSet(s.get('yPos'), yp), document.OperationSettingSet(s.get('width'), w), document.OperationSettingSet(s.get('height'), h), document.OperationSettingSet(s.get('rotate'), r)) self.document.applyOperation( document.OperationMultiple(operations, descr=_('adjust shape')))
def actionEmbed(self): """Embed external image into veusz document.""" s = self.settings if s.filename == '{embedded}': print "Data already embedded" return # get data from external file try: f = open(s.filename, 'rb') data = f.read() f.close() except EnvironmentError: print "Could not find file. Not embedding." return # convert to base 64 to make it nicer in the saved file encoded = str(qt4.QByteArray(data).toBase64()) # now put embedded data in hidden setting ops = [ document.OperationSettingSet(s.get('filename'), '{embedded}'), document.OperationSettingSet(s.get('embeddedImageData'), encoded) ] self.document.applyOperation( document.OperationMultiple(ops, descr=_('embed image')))
def mouseReleaseEvent(self, event): """Update widget with position.""" qt4.QGraphicsRectItem.mouseReleaseEvent(self, event) highlight = self.checkHighlight() if highlight: # in a highlight zone so use highlight zone name to set position hp, vp = highlight hm, vm = 0., 0. else: # calculate the position of the box to work out Manual fractions rect = self.rect() rect.translate(self.pos()) pposn = self.params.parentposn hp, vp = 'manual', 'manual' hm = (rect.left() - pposn[0]) / (pposn[2] - pposn[0]) vm = (pposn[3] - rect.bottom()) / (pposn[3] - pposn[1]) # update widget with positions s = self.params.widget.settings operations = ( document.OperationSettingSet(s.get('horzPosn'), hp), document.OperationSettingSet(s.get('vertPosn'), vp), document.OperationSettingSet(s.get('horzManual'), hm), document.OperationSettingSet(s.get('vertManual'), vm), ) self.params.widget.document.applyOperation( document.OperationMultiple(operations, descr=_('move key')))
def actionSetStyleSheet(self): """Use the setting as the default in the stylesheet.""" # get name of stylesheet setting sslink = self.setting.getStylesheetLink() # apply operation to change it self.document.applyOperation( document.OperationMultiple( [ document.OperationSettingSet(sslink, self.setting.get()), document.OperationSettingSet(self.setting, self.setting.default) ], descr=_("make default style")) )
def actionEmbed(self): """Override Veusz ImageFile.actionEmbed in order """ s = self.settings self.updateCachedImage() # now put embedded data in hidden setting ops = [ document.OperationSettingSet(s.get('embeddedImageData'), self.cacheembeddata), document.OperationSettingSet(s.get('embeddedDataset'), s.dataset), document.OperationSettingSet(s.get('embeddedTarget'), s.target), document.OperationSettingSet(s.get('codec'), self.dec.comp) ] self.document.applyOperation( document.OperationMultiple(ops, descr='image reference'))
def resetToDefault(self, name): """Reset settings to default.""" ops = [] for s in self._settingsatlevel: setn = s.get(name) ops.append(document.OperationSettingSet(setn, setn.default)) self.document.applyOperation( document.OperationMultiple(ops, descr=_("reset to default")))
def slotWidgetHideShow(self, widgets, hideshow): """Hide or show selected widgets. hideshow is True for hiding, False for showing """ ops = [ document.OperationSettingSet(w.settings.get('hide'), hideshow) for w in widgets ] descr = ('show', 'hide')[hideshow] self.document.applyOperation( document.OperationMultiple(ops, descr=descr))
def updateControlItem(self, cgi): """Update position of point given new name and vals.""" s = self.settings pointsX = list(s.xPos) # make a copy here so original is not modifed pointsY = list(s.yPos) ind = cgi.index # calculate new position coordinate for item xpos, ypos = self._getGraphCoords(cgi.widgetposn, cgi.deltacrosspos[0] + cgi.posn[0], cgi.deltacrosspos[1] + cgi.posn[1]) if xpos is None or ypos is None: return pointsX[ind], pointsY[ind] = xpos, ypos operations = (document.OperationSettingSet(s.get('xPos'), pointsX), document.OperationSettingSet(s.get('yPos'), pointsY)) self.document.applyOperation( document.OperationMultiple(operations, descr=_('move label')))
def updateControlItem(self, cgi): """Update axis position from control item.""" s = self.settings p = cgi.maxposn if cgi.zoomed(): # zoom axis scale c1, c2 = self.plotterToGraphCoords( cgi.maxposn, N.array([cgi.minzoom, cgi.maxzoom])) if c1 > c2: c1, c2 = c2, c1 operations = ( document.OperationSettingSet(s.get('min'), float(c1)), document.OperationSettingSet(s.get('max'), float(c2)), ) self.document.applyOperation( document.OperationMultiple(operations, descr=_('zoom axis'))) elif cgi.moved(): # move axis # convert positions to fractions pt1, pt2, ppt1, ppt2 = ( (3, 1, 0, 2), (0, 2, 3, 1) ) [s.direction == 'horizontal'] minfrac = abs((cgi.minpos - p[pt1]) / (p[pt2] - p[pt1])) maxfrac = abs((cgi.maxpos - p[pt1]) / (p[pt2] - p[pt1])) axisfrac = abs((cgi.axispos - p[ppt1]) / (p[ppt2] - p[ppt1])) # swap if wrong way around if minfrac > maxfrac: minfrac, maxfrac = maxfrac, minfrac # update doc operations = ( document.OperationSettingSet(s.get('lowerPosition'), minfrac), document.OperationSettingSet(s.get('upperPosition'), maxfrac), document.OperationSettingSet(s.get('otherPosition'), axisfrac), ) self.document.applyOperation( document.OperationMultiple(operations, descr=_('adjust axis')))
def match_axes(self, first, second): """Add `first`-level axis to the list of matched axes of the `second`-level axis. The first-level axis will remain first-level. The `second` will become `second`-level and will be listed in every other first-level axis referenced in its match setting.""" logging.debug('matching', first, second) s = self.doc.resolveFullSettingPath(first + '/linked') s1 = self.doc.resolveFullSettingPath(first + '/linkedaxis') ops = [] if self.matching(first): print 'Unsetting', first, second ops.append(document.OperationSettingSet(s, False)) ops.append(document.OperationSettingSet(s1, '')) s.set(False) s1.set('') else: print 'Setting', first, second, self.matching(first) ops.append(document.OperationSettingSet(s, True)) ops.append(document.OperationSettingSet(s1, second.split('/')[-1])) self.doc.applyOperation( document.OperationMultiple(ops, descr=_('Axis match')))
def actionZeroMargins(self): """Zero margins of plots inside this grid.""" operations = [] for c in self.children: if isinstance(c, graph.Graph): s = c.settings for v in ('leftMargin', 'topMargin', 'rightMargin', 'bottomMargin'): operations.append( document.OperationSettingSet(s.get(v), '0cm')) self.document.applyOperation( document.OperationMultiple(operations, descr='zero margins'))
def onSettingChanged(self, control, setting, val): """Change setting in document.""" # construct list of operations to change each setting ops = [] sname = setting.name if self._root: sname = self._root + '/' + sname for w in self.widgets: s = self.document.resolveFullSettingPath(w.path + '/' + sname) if s.val != val: ops.append(document.OperationSettingSet(s, val)) # apply all operations if ops: self.document.applyOperation( document.OperationMultiple(ops, descr=_('change settings')))
def updateOutputLabel(self, ops, vals, chi2, dof): """Use best fit parameters to update text label.""" s = self.settings labelwidget = s.get('outLabel').findWidget() if labelwidget is not None: # build up a set of X=Y values loc = self.document.locale txt = [] for l, v in sorted(vals.iteritems()): val = utils.formatNumber(v, '%.4Vg', locale=loc) txt.append( '%s = %s' % (l, val) ) # add chi2 output txt.append( r'\chi^{2}_{\nu} = %s/%i = %s' % ( utils.formatNumber(chi2, '%.4Vg', locale=loc), dof, utils.formatNumber(chi2/dof, '%.4Vg', locale=loc) )) # update label with text text = r'\\'.join(txt) ops.append( document.OperationSettingSet( labelwidget.settings.get('label') , text ) )
def updateControlItem(self, cgi, pt1, pt2): """If control items are moved, update line.""" s = self.settings # calculate new position coordinate for item xpos, ypos = self._getGraphCoords(cgi.widgetposn, pt1[0], pt1[1]) if xpos is None or ypos is None: return x, y = list(s.xPos), list(s.yPos) x[min(cgi.index, len(x)-1)] = xpos y[min(cgi.index, len(y)-1)] = ypos operations = [ document.OperationSettingSet(s.get('xPos'), x), document.OperationSettingSet(s.get('yPos'), y), ] if s.mode == 'length-angle': # convert 2nd point to length, angle length = ( math.sqrt( (pt2[0]-pt1[0])**2 + (pt2[1]-pt1[1])**2 ) / (cgi.widgetposn[2]-cgi.widgetposn[0]) ) angle = ( (math.atan2( pt2[1]-pt1[1], pt2[0]-pt1[0] ) * 180. / math.pi) % 360. ) # update values l, a = list(s.length), list(s.angle) l[min(cgi.index, len(l)-1)] = length a[min(cgi.index, len(a)-1)] = angle operations += [ document.OperationSettingSet(s.get('length'), l), document.OperationSettingSet(s.get('angle'), a), ] else: xpos2, ypos2 = self._getGraphCoords(cgi.widgetposn, pt2[0], pt2[1]) if xpos is not None and ypos is not None: x2, y2 = list(s.xPos2), list(s.yPos2) x2[min(cgi.index, len(x2)-1)] = xpos2 y2[min(cgi.index, len(y2)-1)] = ypos2 operations += [ document.OperationSettingSet(s.get('xPos2'), x2), document.OperationSettingSet(s.get('yPos2'), y2) ] self.document.applyOperation( document.OperationMultiple(operations, descr=_('adjust lines')) )
def up_coord(self, oldx=None, oldy=None, xData=None, yData=None): """Place in the nearest point to the current x,y coord""" d = self.document s = self.settings xSet = self.parent.settings.get('xData') ySet = self.parent.settings.get('yData') if (xData is None): xData = xSet.getFloatArray(d) if (yData is None): yData = ySet.getFloatArray(d) N = len(xData) # Compute visible ranges xAx = searchWidgetName(s.parent, s.xAxis) yAx = searchWidgetName(s.parent, s.yAxis) if None in [xAx, yAx]: return False m = lambda v, alt: v if v != 'Auto' else alt xMax = m(xAx.settings.max, np.nanmax(xData)) xMin = m(xAx.settings.min, np.nanmin(xData)) xRange = xMax - xMin yMax = m(yAx.settings.max, np.nanmax(yData)) yMin = m(yAx.settings.min, np.nanmin(yData)) yRange = yMax - yMin # Calc new coord ox_set = s.get('xPos') oy_set = s.get('yPos') if oldx is None: oldx = ox_set.get() if oldy is None: oldy = oy_set.get() if (0 in [xRange, yRange]) or np.isnan(xRange) or np.isnan(yRange): logging.debug('ERROR: Datapoint divide for ranges', xRange, yRange) return # Store values in class attributes in order for other methods to find # them self.xData = xData self.yData = yData self.xRange = xRange self.yRange = yRange self.N = N type_of_point = self.settings.search self.point_index, self.x, self.y = { 'Nearest (Fixed X)': self.distance_fixed_x }.get(type_of_point, self.distance)(oldx, oldy) self.xMax = xMax self.xMin = xMin self.yMax = yMax self.yMin = yMin self.xAx = xAx self.yAx = yAx # perform nearest critical point search if type_of_point != 'Nearest (Fixed X)' and type_of_point != 'Nearest': self.critical_search(type_of_point) self.ops.append(document.OperationSettingSet(ox_set, float(self.x))) self.ops.append(document.OperationSettingSet(oy_set, float(self.y))) return True
def _setdataset(): self.doc.applyOperation( document.OperationSettingSet( path, self.doc.datasetName(dataset)))
def resetToDefault(self, name): """Reset setting to default.""" setn = self.settings.get(name) self.document.applyOperation( document.OperationSettingSet(setn, setn.default))
def actionUnlinkSetting(self): """Unlink the setting if it is a reference.""" self.document.applyOperation( document.OperationSettingSet(self.setting, self.setting.get()) )
def modify(widget=widget): """Modify the setting for the widget given.""" wpath = widget + setpath self.document.applyOperation( document.OperationSettingSet(wpath, self.setting.get()))
def onSettingChanged(self, control, setting, val): """Change setting in document.""" if setting.val != val: self.document.applyOperation( document.OperationSettingSet(setting, val))
class Fit(FunctionPlotter): """A plotter to fit a function to data.""" typename='fit' allowusercreation=True description=_('Fit a function to data') def __init__(self, parent, name=None): FunctionPlotter.__init__(self, parent, name=name) if type(self) == Fit: self.readDefaults() self.addAction( widget.Action('fit', self.actionFit, descr = _('Fit function'), usertext = _('Fit function')) ) @classmethod def addSettings(klass, s): """Construct list of settings.""" FunctionPlotter.addSettings(s) s.add( setting.FloatDict('values', {'a': 0.0, 'b': 1.0}, descr = _('Variables and fit values'), usertext=_('Parameters')), 1 ) s.add( setting.Dataset('xData', 'x', descr = _('Variable containing x data to fit'), usertext=_('X dataset')), 2 ) s.add( setting.Dataset('yData', 'y', descr = _('Variable containing y data to fit'), usertext=_('Y dataset')), 3 ) s.add( setting.Bool('fitRange', False, descr = _('Fit only the data between the ' 'minimum and maximum of the axis for ' 'the function variable'), usertext=_('Fit only range')), 4 ) s.add( setting.WidgetChoice( 'outLabel', '', descr=_('Write best fit parameters to this text label ' 'after fitting'), widgettypes=('label',), usertext=_('Output label')), 5 ) s.add( setting.Str('outExpr', '', descr = _('Output best fitting expression'), usertext=_('Output expression')), 6, readonly=True ) s.add( setting.Float('chi2', -1, descr = 'Output chi^2 from fitting', usertext=_('Fit χ<sup>2</sup>')), 7, readonly=True ) s.add( setting.Int('dof', -1, descr = _('Output degrees of freedom from fitting'), usertext=_('Fit d.o.f.')), 8, readonly=True ) s.add( setting.Float('redchi2', -1, descr = _('Output reduced-chi-squared from fitting'), usertext=_('Fit reduced χ<sup>2</sup>')), 9, readonly=True ) f = s.get('function') f.newDefault('a + b*x') f.descr = _('Function to fit') # modify description s.get('min').usertext=_('Min. fit range') s.get('max').usertext=_('Max. fit range') def providesAxesDependency(self): """This widget provides range information about these axes.""" s = self.settings return ( (s.xAxis, 'sx'), (s.yAxis, 'sy') ) def updateAxisRange(self, axis, depname, axrange): """Update range with range of data.""" dataname = {'sx': 'xData', 'sy': 'yData'}[depname] data = self.settings.get(dataname).getData(self.document) if data: drange = data.getRange() if drange: axrange[0] = min(axrange[0], drange[0]) axrange[1] = max(axrange[1], drange[1]) def initEnviron(self): """Copy data into environment.""" env = self.document.eval_context.copy() env.update( self.settings.values ) return env def updateOutputLabel(self, ops, vals, chi2, dof): """Use best fit parameters to update text label.""" s = self.settings labelwidget = s.get('outLabel').findWidget() if labelwidget is not None: # build up a set of X=Y values loc = self.document.locale txt = [] for l, v in sorted(vals.iteritems()): val = utils.formatNumber(v, '%.4Vg', locale=loc) txt.append( '%s = %s' % (l, val) ) # add chi2 output txt.append( r'\chi^{2}_{\nu} = %s/%i = %s' % ( utils.formatNumber(chi2, '%.4Vg', locale=loc), dof, utils.formatNumber(chi2/dof, '%.4Vg', locale=loc) )) # update label with text text = r'\\'.join(txt) ops.append( document.OperationSettingSet( labelwidget.settings.get('label') , text ) ) def actionFit(self): """Fit the data.""" s = self.settings # update function for fitting try: self.checker.check(s.function, s.variable) except RuntimeError, e: self.logEvalError(e) return # populate the input parameters names = s.values.keys() names.sort() params = N.array( [s.values[i] for i in names] ) # FIXME: loads of error handling!! d = self.document # choose dataset depending on fit variable if s.variable == 'x': xvals = d.getData(s.xData).data ydata = d.getData(s.yData) yvals = ydata.data yserr = ydata.serr else: xvals = d.getData(s.yData).data ydata = d.getData(s.xData) yvals = ydata.data yserr = ydata.serr # if there are no errors on data if yserr is None: if ydata.perr is not None and ydata.nerr is not None: print "Warning: Symmeterising positive and negative errors" yserr = N.sqrt( 0.5*(ydata.perr**2 + ydata.nerr**2) ) else: print "Warning: No errors on y values. Assuming 5% errors." yserr = yvals*0.05 yserr[yserr < 1e-8] = 1e-8 # if the fitRange parameter is on, we chop out data outside the # range of the axis if s.fitRange: # get ranges for axes if s.variable == 'x': drange = self.parent.getAxes((s.xAxis,))[0].getPlottedRange() mask = N.logical_and(xvals >= drange[0], xvals <= drange[1]) else: drange = self.parent.getAxes((s.yAxis,))[0].getPlottedRange() mask = N.logical_and(yvals >= drange[0], yvals <= drange[1]) xvals, yvals, yserr = xvals[mask], yvals[mask], yserr[mask] print "Fitting %s from %g to %g" % (s.variable, drange[0], drange[1]) # minimum set for fitting if s.min != 'Auto': if s.variable == 'x': mask = xvals >= s.min else: mask = yvals >= s.min xvals, yvals, yserr = xvals[mask], yvals[mask], yserr[mask] # maximum set for fitting if s.max != 'Auto': if s.variable == 'x': mask = xvals <= s.max else: mask = yvals <= s.max xvals, yvals, yserr = xvals[mask], yvals[mask], yserr[mask] if s.min != 'Auto' or s.max != 'Auto': print "Fitting %s between %s and %s" % (s.variable, s.min, s.max) # various error checks if len(xvals) == 0: sys.stderr.write(_('No data values. Not fitting.\n')) return if len(xvals) != len(yvals) or len(xvals) != len(yserr): sys.stderr.write(_('Fit data not equal in length. Not fitting.\n')) return if len(params) > len(xvals): sys.stderr.write(_('No degrees of freedom for fit. Not fitting\n')) return # actually do the fit, either via Minuit or our own LM fitter chi2 = 1 dof = 1 if minuit is not None: vals, chi2, dof = minuitFit(self.evalfunc, params, names, s.values, xvals, yvals, yserr) else: print _('Minuit not available, falling back to simple L-M fitting:') retn, chi2, dof = utils.fitLM(self.evalfunc, params, xvals, yvals, yserr) vals = {} for i, v in zip(names, retn): vals[i] = float(v) # list of operations do we can undo the changes operations = [] # populate the return parameters operations.append( document.OperationSettingSet(s.get('values'), vals) ) # populate the read-only fit quality params operations.append( document.OperationSettingSet(s.get('chi2'), float(chi2)) ) operations.append( document.OperationSettingSet(s.get('dof'), int(dof)) ) if dof <= 0: print _('No degrees of freedom in fit.\n') redchi2 = -1. else: redchi2 = float(chi2/dof) operations.append( document.OperationSettingSet(s.get('redchi2'), redchi2) ) # expression for fit expr = self.generateOutputExpr(vals) operations.append( document.OperationSettingSet(s.get('outExpr'), expr) ) self.updateOutputLabel(operations, vals, chi2, dof) # actually change all the settings d.applyOperation( document.OperationMultiple(operations, descr=_('fit')) )
def doZoomRect(self, endpos): """Take the zoom rectangle drawn by the user and do the zooming. endpos is a QPoint end point This is pretty messy - first we have to work out the graph associated to the first point Then we have to iterate over each of the plotters, identify their axes, and change the range of the axes to match the screen region selected. """ # safety net if self.grabpos is None or endpos is None: return # get points corresponding to corners of rectangle pt1 = self.grabpos pt2 = endpos # work out whether it's worthwhile to zoom: only zoom if there # are >=5 pixels movement if abs((pt2 - pt1).x()) < 10 or abs((pt2 - pt1).y()) < 10: return # try to work out in which widget the first point is in widget = self.painthelper.pointInWidgetBounds(pt1.x(), pt1.y(), widgets.Graph) if widget is None: return # convert points on plotter to points on axis for each axis xpts = N.array([pt1.x(), pt2.x()]) ypts = N.array([pt1.y(), pt2.y()]) # build up operation list to do zoom operations = [] axes = {} # iterate over children, to look for plotters for c in [ i for i in widget.children if isinstance(i, widgets.GenericPlotter) ]: # get axes associated with plotter caxes = c.parent.getAxes((c.settings.xAxis, c.settings.yAxis)) for a in caxes: if a: axes[a] = True # iterate over each axis, and update the ranges for axis in axes.iterkeys(): s = axis.settings if s.direction == 'horizontal': p = xpts else: p = ypts # convert points on plotter to axis coordinates # FIXME: Need To Trap Conversion Errors! try: r = axis.plotterToGraphCoords( self.painthelper.widgetBounds(axis), p) except KeyError: continue # invert if min and max are inverted if r[1] < r[0]: r[1], r[0] = r[0], r[1] # build up operations to change axis if s.min != r[0]: operations.append( document.OperationSettingSet(s.get('min'), float(r[0]))) if s.max != r[1]: operations.append( document.OperationSettingSet(s.get('max'), float(r[1]))) # finally change the axes self.document.applyOperation( document.OperationMultiple(operations, descr=_('zoom axes')))