def getPreviewPixmap(self, ds): """Get a preview pixmap for a dataset.""" size = (140, 70) if ds.dimensions != 1 or ds.datatype != "numeric": return None pixmap = qt4.QPixmap(*size) pixmap.fill(qt4.Qt.transparent) p = qt4.QPainter(pixmap) p.setRenderHint(qt4.QPainter.Antialiasing) # calculate data points try: if len(ds.data) < size[1]: y = ds.data else: intvl = len(ds.data) / size[1] + 1 y = ds.data[::intvl] x = N.arange(len(y)) # plot data points on image minval, maxval = N.nanmin(y), N.nanmax(y) y = (y - minval) / (maxval - minval) * size[1] finite = N.isfinite(y) x, y = x[finite], y[finite] x = x * (1. / len(x)) * size[0] poly = qt4.QPolygonF() utils.addNumpyToPolygonF(poly, x, size[1] - y) p.setPen(qt4.QPen(qt4.Qt.blue)) p.drawPolyline(poly) # draw x axis if span 0 p.setPen(qt4.QPen(qt4.Qt.black)) if minval <= 0 and maxval > 0: y0 = size[1] - (0 - minval) / (maxval - minval) * size[1] p.drawLine(x[0], y0, x[-1], y0) else: p.drawLine(x[0], size[1], x[-1], size[1]) p.drawLine(x[0], 0, x[0], size[1]) except (ValueError, ZeroDivisionError): # zero sized array after filtering or min == max, so return None p.end() return None p.end() return pixmap
def drawKeySymbol(self, number, painter, x, y, width, height): """Draw the plot symbol and/or line.""" painter.save() cliprect = qt4.QRectF(qt4.QPointF(x, y), qt4.QPointF(x + width, y + height)) painter.setClipRect(cliprect) # draw sample error bar s = self.settings size = s.get('markerSize').convert(painter) style = s.errorStyle # make some fake error bar data to plot xv = s.get('xData').getData(self.document) yv = s.get('yData').getData(self.document) yp = y + height / 2 xpts = N.array([x - width, x + width / 2, x + 2 * width]) ypts = N.array([yp, yp, yp]) # size of error bars in key errorsize = height * 0.4 # make points for error bars (if any) if xv and xv.hasErrors(): xneg = N.array( [x - width, x + width / 2 - errorsize, x + 2 * width]) xpos = N.array( [x - width, x + width / 2 + errorsize, x + 2 * width]) else: xneg = xpos = xpts if yv and yv.hasErrors(): yneg = N.array([yp - errorsize, yp - errorsize, yp - errorsize]) ypos = N.array([yp + errorsize, yp + errorsize, yp + errorsize]) else: yneg = ypos = ypts # plot error bar painter.setPen(s.ErrorBarLine.makeQPenWHide(painter)) for function in _errorBarFunctionMap[style]: function(style, xneg, xpos, yneg, ypos, xpts, ypts, s, painter, cliprect) # draw line if not s.PlotLine.hide: painter.setPen(s.PlotLine.makeQPen(painter)) painter.drawLine(qt4.QPointF(x, yp), qt4.QPointF(x + width, yp)) # draw marker if not s.MarkerLine.hide or not s.MarkerFill.hide: if not s.MarkerFill.hide: painter.setBrush(s.MarkerFill.makeQBrush()) if not s.MarkerLine.hide: painter.setPen(s.MarkerLine.makeQPen(painter)) else: painter.setPen(qt4.QPen(qt4.Qt.NoPen)) utils.plotMarker(painter, x + width / 2, yp, s.marker, size) painter.restore()
def render(self): """Render the text.""" if self.calcbounds is None: self.getBounds() p = self.painter p.save() if self.mmldoc is not None: p.translate(self.xi, self.yi) p.rotate(self.angle) # is drawn from bottom of box, not top p.translate(0, -self.size.height()) p.scale(self.drawscale, self.drawscale) self.record.play(p) else: # display an error - must be a better way to do this p.setFont(qt4.QFont()) p.setPen(qt4.QPen(qt4.QColor("red"))) p.drawText( qt4.QRectF(self.xi, self.yi, 200, 200), qt4.Qt.AlignLeft | qt4.Qt.AlignTop | qt4.Qt.TextWordWrap, self.error) p.restore() return self.calcbounds
def __init__(self, params): qt4.QGraphicsRectItem.__init__(self, params.posn[0], params.posn[1], params.dims[0], params.dims[1]) self.params = params self.setCursor(qt4.Qt.SizeAllCursor) self.setZValue(1.) self.setFlag(qt4.QGraphicsItem.ItemIsMovable) self.highlightpen = qt4.QPen(qt4.Qt.red, 2, qt4.Qt.DotLine) pposn, dims = params.parentposn, params.dims th = params.textheight # special places on the plot xposn = { 'left': pposn[0] + th, 'centre': pposn[0] + 0.5*(pposn[2]-pposn[0]-dims[0]), 'right': pposn[2] - th - dims[0] } yposn = { 'top': pposn[1] + th, 'centre': pposn[1] + 0.5*(pposn[3]-pposn[1]-dims[1]), 'bottom': pposn[3] - th - dims[1] } # these are special places where the key is aligned self.highlightpoints = {} for xname, xval in xposn.iteritems(): for yname, yval in yposn.iteritems(): self.highlightpoints[(xname, yname)] = qt4.QPointF(xval, yval) self.updatePen()
def __init__(self, parent, rotator=False): qt4.QGraphicsRectItem.__init__(self, parent) if rotator: self.setBrush(qt4.QBrush(setting.settingdb.color('cntrlline'))) self.setRect(-3, -3, 6, 6) else: self.setBrush(qt4.QBrush(setting.settingdb.color('cntrlcorner'))) self.setRect(-5, -5, 10, 10) self.setPen(qt4.QPen(qt4.Qt.NoPen)) self.setFlag(qt4.QGraphicsItem.ItemIsMovable) self.setZValue(3.)
def brushExtFillPath(painter, extbrush, path, ignorehide=False, stroke=None): """Use an BrushExtended settings object to fill a path on painter. If ignorehide is True, ignore the hide setting on the brush object. stroke is an optional QPen for stroking outline of path """ if extbrush.hide and not ignorehide: if stroke is not None: painter.strokePath(path, stroke) return style = extbrush.style if style in _fillcnvt: # standard fill: use Qt styles for painting color = qt4.QColor(extbrush.color) color.setAlphaF((100 - extbrush.transparency) / 100.) brush = qt4.QBrush(color, _fillcnvt[style]) if stroke is None: painter.fillPath(path, brush) else: painter.save() painter.setPen(stroke) painter.setBrush(brush) painter.drawPath(path) painter.restore() elif style in _hatchmap: # fill with hatching if not extbrush.backhide: # background brush color = qt4.QColor(extbrush.backcolor) color.setAlphaF((100 - extbrush.backtransparency) / 100.) brush = qt4.QBrush(color) painter.fillPath(path, brush) color = qt4.QColor(extbrush.color) color.setAlphaF((100 - extbrush.transparency) / 100.) width = extbrush.get('linewidth').convert(painter) lstyle, dashpattern = extbrush.get('linestyle')._linecnvt[ extbrush.linestyle] pen = qt4.QPen(color, width, lstyle) if dashpattern: pen.setDashPattern(dashpattern) # do hatching with spacing spacing = extbrush.get('patternspacing').convert(painter) if spacing > 0: _hatcher(painter, pen, path, spacing, _hatchmap[style]) if stroke is not None: painter.strokePath(path, stroke)
def makeQPen(self, painthelper): '''Make a QPen from the description. This currently ignores the hide attribute ''' color = qt4.QColor(self.color) color.setAlphaF((100 - self.transparency) / 100.) width = self.get('width').convert(painthelper) style, dashpattern = setting.LineStyle._linecnvt[self.style] pen = qt4.QPen(color, width, style) if dashpattern: pen.setDashPattern(dashpattern) return pen
def render(self, state): initx = state.x # draw material under bar Part.render(self, state) # draw line over text with 0.5pt thickness painter = state.painter height = state.fontMetrics().ascent() painter.save() penw = state.getPixelsPerPt() * 0.5 painter.setPen(qt4.QPen(painter.pen().brush(), penw)) painter.drawLine(qt4.QPointF(initx, state.y - height + penw), qt4.QPointF(state.x, state.y - height + penw)) painter.restore()
def render(self, state): initx = state.x # draw material under bar Part.render(self, state) # draw circle over text with 1pt radius painter = state.painter height = state.fontMetrics().ascent() painter.save() circsize = state.getPixelsPerPt() painter.setBrush(qt4.QBrush(painter.pen().color())) painter.setPen(qt4.QPen(qt4.Qt.NoPen)) x = 0.5 * (initx + state.x) y = state.y - height + circsize painter.drawEllipse( qt4.QRectF(qt4.QPointF(x - circsize, y - circsize), qt4.QPointF(x + circsize, y + circsize))) painter.restore()
def begin(self, paintdevice): """Start painting.""" self.device = paintdevice self.pen = qt4.QPen() self.brush = qt4.QBrush() self.clippath = None self.clipnum = 0 self.existingclips = {} self.matrix = qt4.QMatrix() # svg root element for qt defaults self.rootelement = SVGElement( None, 'svg', ('width="%spx" height="%spx" version="1.1"\n' ' xmlns="http://www.w3.org/2000/svg"\n' ' xmlns:xlink="http://www.w3.org/1999/xlink"') % (fltStr( self.width * dpi * scale), fltStr(self.height * dpi * scale))) SVGElement(self.rootelement, 'desc', '', 'Veusz output document') # definitions, for clips, etc. self.defs = SVGElement(self.rootelement, 'defs', '') # this is where all the drawing goes self.celement = SVGElement( self.rootelement, 'g', 'stroke-linejoin="bevel" stroke-linecap="square" ' 'stroke="#000000" fill-rule="evenodd"') # previous transform, stroke and clip states self.oldstate = [None, None, None] # cache paths to avoid duplication self.pathcache = {} self.pathcacheidx = 0 return True
def draw(self, posn, phelper, outerbounds=None): """Plot the key on a plotter.""" s = self.settings d = self.document if s.hide: return # get positions of shapes width = s.get('width').getFloatArray(d) height = s.get('height').getFloatArray(d) rotate = s.get('rotate').getFloatArray(d) if width is None or height is None or rotate is None: return # translate coordinates from axes or relative values xpos, ypos = self._getPlotterCoords(posn) if xpos is None or ypos is None: # we can't calculate coordinates return # if a dataset is used, we can't use control items isnotdataset = (not s.get('xPos').isDataset(d) and not s.get('yPos').isDataset(d) and not s.get('width').isDataset(d) and not s.get('height').isDataset(d) and not s.get('rotate').isDataset(d)) controlgraphitems = [] clip = None if s.clip: clip = qt4.QRectF(qt4.QPointF(posn[0], posn[1]), qt4.QPointF(posn[2], posn[3])) painter = phelper.painter(self, posn, clip=clip) # drawing settings for shape if not s.Border.hide: painter.setPen(s.get('Border').makeQPen(painter)) else: painter.setPen(qt4.QPen(qt4.Qt.NoPen)) # iterate over positions index = 0 dx, dy = posn[2] - posn[0], posn[3] - posn[1] for x, y, w, h, r in itertools.izip(xpos, ypos, itertools.cycle(width), itertools.cycle(height), itertools.cycle(rotate)): wp, hp = dx * w, dy * h painter.save() painter.translate(x, y) if r != 0: painter.rotate(r) self.drawShape(painter, qt4.QRectF(-wp * 0.5, -hp * 0.5, wp, hp)) painter.restore() if isnotdataset: cgi = controlgraph.ControlResizableBox(self, [x, y], [wp, hp], r, allowrotate=True) cgi.index = index cgi.widgetposn = posn index += 1 controlgraphitems.append(cgi) phelper.setControlGraph(self, controlgraphitems)
def makeQPenWHide(self, painthelper): """Make a pen, taking account of hide attribute.""" if self.hide: return qt4.QPen(qt4.Qt.NoPen) else: return self.makeQPen(painthelper)
def makeQPen(self): """ Return a qt4.QPen object for the font pen """ return qt4.QPen(qt4.QColor(self.color))
def draw(self, parentposn, phelper, outerbounds=None): """Plot the data on a plotter.""" posn = GenericPlotter.draw(self, parentposn, phelper, outerbounds=outerbounds) x1, y1, x2, y2 = posn s = self.settings # exit if hidden if s.hide: return # get data doc = self.document xv = s.get('xData').getData(doc) yv = s.get('yData').getData(doc) text = s.get('labels').getData(doc, checknull=True) scalepoints = s.get('scalePoints').getData(doc) colorpoints = s.Color.get('points').getData(doc) # if a missing dataset, make a fake dataset for the second one # based on a row number if xv and not yv and s.get('yData').isEmpty(): # use index for y data length = xv.data.shape[0] yv = document.DatasetRange(length, (1, length)) elif yv and not xv and s.get('xData').isEmpty(): # use index for x data length = yv.data.shape[0] xv = document.DatasetRange(length, (1, length)) if not xv or not yv: # no valid dataset, so exit return # if text entered, then multiply up to get same number of values # as datapoints if text: length = min(len(xv.data), len(yv.data)) text = text * (length / len(text)) + text[:length % len(text)] # get axes widgets axes = self._fetchAxes() if not axes: # no valid axes, so exit return # clip data within bounds of plotter cliprect = self.clipAxesBounds(axes, posn) painter = phelper.painter(self, posn, clip=cliprect) # loop over chopped up values for xvals, yvals, tvals, ptvals, cvals in ( document.generateValidDatasetParts(xv, yv, text, scalepoints, colorpoints)): #print "Calculating coordinates" # calc plotter coords of x and y points xplotter = axes[0].dataToPlotterCoords(posn, xvals.data) yplotter = axes[1].dataToPlotterCoords(posn, yvals.data) #print "Painting plot line" # plot data line (and/or filling above or below) if not s.PlotLine.hide or not s.FillAbove.hide or not s.FillBelow.hide: if s.PlotLine.bezierJoin and hasqtloops: self._drawBezierLine(painter, xplotter, yplotter, posn, xvals, yvals) else: self._drawPlotLine(painter, xplotter, yplotter, posn, xvals, yvals, cliprect) # shift points if in certain step modes if s.PlotLine.steps != 'off': steps = s.PlotLine.steps if s.PlotLine.steps == 'right-shift-points': xplotter[1:] = 0.5 * (xplotter[:-1] + xplotter[1:]) elif s.PlotLine.steps == 'left-shift-points': xplotter[:-1] = 0.5 * (xplotter[:-1] + xplotter[1:]) #print "Painting error bars" # plot errors bars self._plotErrors(posn, painter, xplotter, yplotter, axes, xvals, yvals, cliprect) # plot the points (we do this last so they are on top) markersize = s.get('markerSize').convert(painter) if not s.MarkerLine.hide or not s.MarkerFill.hide: #print "Painting marker fill" if not s.MarkerFill.hide: # filling for markers painter.setBrush(s.MarkerFill.makeQBrush()) else: # no-filling brush painter.setBrush(qt4.QBrush()) #print "Painting marker lines" if not s.MarkerLine.hide: # edges of markers painter.setPen(s.MarkerLine.makeQPen(painter)) else: # invisible pen painter.setPen(qt4.QPen(qt4.Qt.NoPen)) # thin datapoints as required if s.thinfactor <= 1: xplt, yplt = xplotter, yplotter else: xplt, yplt = (xplotter[::s.thinfactor], yplotter[::s.thinfactor]) # whether to scale markers scaling = colorvals = cmap = None if ptvals: scaling = ptvals.data if s.thinfactor > 1: scaling = scaling[::s.thinfactor] # color point individually if cvals: colorvals = utils.applyScaling(cvals.data, s.Color.scaling, s.Color.min, s.Color.max) if s.thinfactor > 1: colorvals = colorvals[::s.thinfactor] cmap = self.document.getColormap( s.MarkerFill.colorMap, s.MarkerFill.colorMapInvert) # actually plot datapoints utils.plotMarkers(painter, xplt, yplt, s.marker, markersize, scaling=scaling, clip=cliprect, cmap=cmap, colorvals=colorvals) # finally plot any labels if tvals and not s.Label.hide: self.drawLabels(painter, xplotter, yplotter, tvals, markersize)
def controlLinePen(): """Get pen for lines around shapes.""" return qt4.QPen(setting.settingdb.color('cntrlline'), 2, qt4.Qt.DotLine)
def __init__(self, document, parent, menu=None): """Initialise the window. menu gives a menu to add any menu items to """ qt4.QGraphicsView.__init__(self, parent) self.setBackgroundRole(qt4.QPalette.Dark) self.scene = qt4.QGraphicsScene() self.setScene(self.scene) # this graphics scene item is the actual graph pixmap = qt4.QPixmap(1, 1) self.dpi = (pixmap.logicalDpiX(), pixmap.logicalDpiY()) self.pixmapitem = self.scene.addPixmap(pixmap) # whether full screen mode self.isfullscreen = False # set to be parent's actions self.vzactions = None # for controlling plot elements g = self.controlgraphgroup = qt4.QGraphicsItemGroup() g.setHandlesChildEvents(False) self.scene.addItem(g) # zoom rectangle for zooming into graph (not shown normally) self.zoomrect = self.scene.addRect(0, 0, 100, 100, qt4.QPen(qt4.Qt.DotLine)) self.zoomrect.setZValue(2.) self.zoomrect.hide() # picker graphicsitem for marking the picked point self.pickeritem = PickerCrosshairItem() self.scene.addItem(self.pickeritem) self.pickeritem.setZValue(2.) self.pickeritem.hide() # all the widgets that picker key-navigation might cycle through self.pickerwidgets = [] # the picker state self.pickerinfo = widgets.PickInfo() # set up so if document is modified we are notified self.document = document self.docchangeset = -100 self.oldpagenumber = -1 self.connect(self.document, qt4.SIGNAL("sigModified"), self.slotDocModified) # state of last plot from painthelper self.painthelper = None self.lastwidgetsselected = [] self.oldzoom = -1. self.zoomfactor = 1. self.pagenumber = 0 self.ignoreclick = False # for rendering plots in separate threads self.rendercontrol = RenderControl(self) self.connect(self.rendercontrol, qt4.SIGNAL("renderfinished"), self.slotRenderFinished) # mode for clicking self.clickmode = 'select' self.currentclickmode = None # wheel zooming accumulator self.sumwheeldelta = 0 # set up redrawing timer self.timer = qt4.QTimer(self) self.connect(self.timer, qt4.SIGNAL('timeout()'), self.checkPlotUpdate) # for drag scrolling self.grabpos = None self.scrolltimer = qt4.QTimer(self) self.scrolltimer.setSingleShot(True) # for turning clicking into scrolling after a period self.connect(self.scrolltimer, qt4.SIGNAL('timeout()'), self.slotBecomeScrollClick) # get plot view updating policy # -1: update on document changes # 0: never update automatically # >0: check for updates every x ms self.interval = setting.settingdb['plot_updatepolicy'] # if using a time-based document update checking, start timer if self.interval > 0: self.timer.start(self.interval) # load antialias settings self.antialias = setting.settingdb['plot_antialias'] # allow window to get focus, to allow context menu self.setFocusPolicy(qt4.Qt.StrongFocus) # get mouse move events if mouse is not pressed self.setMouseTracking(True) # create toolbar in main window (urgh) self.createToolbar(parent, menu)
def render(self, state): if len(self.children) != 2: return font = state.font painter = state.painter # make font half size size = font.pointSizeF() font.setPointSizeF(size * 0.5) painter.setFont(font) # keep track of width above and below line if not state.actually_render: self.widths = [] initx = state.x inity = state.y # render bottom of fraction if state.actually_render and len(self.widths) == 2: # centre line state.x = initx + (max(self.widths) - self.widths[0]) * 0.5 self.children[1].render(state) if not state.actually_render: # get width if not rendering self.widths.append(state.x - initx) # render top of fraction m = state.fontMetrics() state.y -= (m.ascent() + m.descent()) if state.actually_render and len(self.widths) == 2: # centre line state.x = initx + (max(self.widths) - self.widths[1]) * 0.5 else: state.x = initx self.children[0].render(state) if not state.actually_render: self.widths.append(state.x - initx) state.x = initx + max(self.widths) state.y = inity # restore font font.setPointSizeF(size) painter.setFont(font) height = state.fontMetrics().ascent() # draw line between lines with 0.5pt thickness painter.save() pen = painter.pen() painter.setPen( qt4.QPen(painter.pen().brush(), state.getPixelsPerPt() * 0.5)) painter.setPen(pen) painter.drawLine( qt4.QPointF(initx, inity - height / 2.), qt4.QPointF(initx + max(self.widths), inity - height / 2)) painter.restore()
def draw(self, posn, phelper, outerbounds = None): """Plot the key on a plotter.""" s = self.settings d = self.document if s.hide: return # if a dataset is used, we can't use control items isnotdataset = ( not s.get('xPos').isDataset(d) and not s.get('yPos').isDataset(d) ) if s.mode == 'length-angle': isnotdataset = ( isnotdataset and not s.get('length').isDataset(d) and not s.get('angle').isDataset(d) ) else: isnotdataset = ( isnotdataset and not s.get('xPos2').isDataset(d) and not s.get('yPos2').isDataset(d) ) # now do the drawing clip = None if s.clip: clip = qt4.QRectF( qt4.QPointF(posn[0], posn[1]), qt4.QPointF(posn[2], posn[3]) ) painter = phelper.painter(self, posn, clip=clip) # adjustable positions for the lines arrowsize = s.get('arrowSize').convert(painter) # drawing settings for line if not s.Line.hide: painter.setPen( s.get('Line').makeQPen(painter) ) else: painter.setPen( qt4.QPen(qt4.Qt.NoPen) ) # settings for fill if not s.Fill.hide: painter.setBrush( s.get('Fill').makeQBrush() ) else: painter.setBrush( qt4.QBrush() ) # iterate over positions scaling = posn[2]-posn[0] if s.mode == 'length-angle': lines = self._computeLinesLengthAngle(posn, scaling) else: lines = self._computeLinesPointToPoint(posn) if lines is None: return controlgraphitems = [] for index, (x, y, l, a) in enumerate(lines): utils.plotLineArrow(painter, x, y, l, a, arrowsize=arrowsize, arrowleft=s.arrowleft, arrowright=s.arrowright) if isnotdataset: cgi = controlgraph.ControlLine( self, x, y, x + l*math.cos(a/180.*math.pi), y + l*math.sin(a/180.*math.pi)) cgi.index = index cgi.widgetposn = posn controlgraphitems.append(cgi) phelper.setControlGraph(self, controlgraphitems)