def generateDrawSpecs(self, p): """ Calls tickValues() and tickStrings() to determine where and how ticks should be drawn, then generates from this a set of drawing commands to be interpreted by drawPicture(). """ # bounds = self.boundingRect() self.fullBoundingRect = QtCore.QRectF(0, 0, self._bounds["radial"][1] * 2, self._bounds["radial"][1] * 2) self.fullBoundingRect.moveCenter(Point(0, 0)) bounds = self.mapRectFromParent(self.geometry()) linkedView = self.linkedView() tickBounds = linkedView.mapRectToItem(self, linkedView.boundingRect()) span = (bounds.bottomLeft(), bounds.topRight()) tickStart = 0 tickStop = max(bounds.width(), bounds.height()) tickDir = 0 axis = 0 # print tickStart, tickStop, span ## determine size of this item in pixels points = list(map(self.mapToDevice, span)) if None in points: return lengthInPixels = Point(points[1] - points[0]).length() if lengthInPixels == 0: return # Determine major / minor / subminor axis ticks if self._tickLevels is None: rng = self.linkedView().viewRange() if self.pOrientation is "radial": tickLevels = self.tickValues( self.range[0], np.sqrt(max(np.abs(rng[0]))**2 + max(np.abs(rng[1]))**2), lengthInPixels) else: # todo undo hardcoded angles tickLevels = self.tickValues(self._bounds["azimuthal"][0], self._bounds["azimuthal"][1], lengthInPixels) tickStrings = None else: ## parse self.tickLevels into the formats returned by tickLevels() and tickStrings() tickLevels = [] tickStrings = [] for level in self._tickLevels: values = [] strings = [] tickLevels.append((None, values)) tickStrings.append(strings) for val, strn in level: values.append(val) strings.append(strn) # Not needed for radial plots xScale = 1 offset = 0 if self.pOrientation is "radial": rng = self.linkedView().viewRange() xRange = [0] + [abs(x * xScale - offset) for x in self.range] xMin = self._bounds["radial"][0] xMax = np.sqrt(max(np.abs(rng[0]))**2 + max(np.abs(rng[1]))**2) xMax = max(xMax, self._bounds["radial"][0]) else: xRange = [x * xScale - offset for x in self.range] xMin = self._bounds["azimuthal"][0] xMax = self._bounds["azimuthal"][1] tickPositions = [] # remembers positions of previously drawn ticks ## compute coordinates to draw ticks ## draw three different intervals, long ticks first tickSpecs = [] for i in range(len(tickLevels)): tickPositions.append([]) ticks = tickLevels[i][1] ## length of tick tickLength = self.style['tickLength'] / ((i * 0.5) + 1.0) lineAlpha = 255 / (i + 3) if self.grid is not False: lineAlpha *= self.grid / 255. * np.clip( (0.05 * lengthInPixels / (len(ticks) + 1)), 0., 1.) for v in ticks: ## determine actual position to draw this tick x = (v * xScale) - offset if x < xMin or x >= xMax: ## last check to make sure no out-of-bounds ticks are drawn tickPositions[i].append(None) continue tickPositions[i].append(x) if self.pOrientation is "radial": p1 = [x, x] p2 = [x, x] p1[axis] = tickStart p2[axis] = tickStop if self.grid is False: p2[axis] += tickLength * tickDir else: p1, p2 = self.getAzimuthalLines(self.boundingRect(), x) tickPen = self.pen() color = tickPen.color() color.setAlpha(lineAlpha) tickPen.setColor(color) tickSpecs.append((tickPen, Point(p1), Point(p2))) if self.style['stopAxisAtTick'][0] is True: stop = max(span[0].y(), min(map(min, tickPositions))) if axis == 0: span[0].setY(stop) else: span[0].setX(stop) if self.style['stopAxisAtTick'][1] is True: stop = min(span[1].y(), max(map(max, tickPositions))) if axis == 0: span[1].setY(stop) else: span[1].setX(stop) axisSpec = (self.pen(), span[0], span[1]) textOffset = self.style['tickTextOffset'][ axis] ## spacing between axis and text # if self.style['autoExpandTextSpace'] is True: # textWidth = self.textWidth # textHeight = self.textHeight # else: # textWidth = self.style['tickTextWidth'] ## space allocated for horizontal text # textHeight = self.style['tickTextHeight'] ## space allocated for horizontal text textSize2 = 0 textRects = [] textSpecs = [] ## list of draw # If values are hidden, return early if not self.style['showValues']: return (axisSpec, tickSpecs, textSpecs) # For appropriate sizing of boxes if self.tickFont is None: return (axisSpec, tickSpecs, textSpecs) p.setFont(self.tickFont) for i in range(min(len(tickLevels), self.style['maxTextLevel'] + 1)): ## Get the list of strings to display for this level if tickStrings is None: spacing, values = tickLevels[i] strings = self.tickStrings(values, self.autoSIPrefixScale * self.scale, spacing) else: strings = tickStrings[i] if len(strings) == 0: continue ## ignore strings belonging to ticks that were previously ignored for j in range(len(strings)): if tickPositions[i][j] is None: strings[j] = None ## Measure density of text; decide whether to draw this level rects = [] for s in strings: if s is None: rects.append(None) else: br = p.boundingRect(QtCore.QRectF(0, 0, 100, 100), QtCore.Qt.AlignCenter, s) ## boundingRect is usually just a bit too large ## (but this probably depends on per-font metrics?) br.setHeight(br.height() * 0.8) rects.append(br) textRects.append(rects[-1]) if len(textRects) > 0: ## measure all text, make sure there's enough room if axis == 0: textSize = np.sum([r.height() for r in textRects]) textSize2 = np.max([r.width() for r in textRects]) else: textSize = np.sum([r.width() for r in textRects]) textSize2 = np.max([r.height() for r in textRects]) else: textSize = 0 textSize2 = 0 if i > 0: ## always draw top level ## If the strings are too crowded, stop drawing text now. ## We use three different crowding limits based on the number ## of texts drawn so far. textFillRatio = float(textSize) / lengthInPixels finished = False for nTexts, limit in self.style['textFillLimits']: if len(textSpecs) >= nTexts and textFillRatio >= limit: finished = True break if finished: break # spacing, values = tickLevels[best] # strings = self.tickStrings(values, self.scale, spacing) # Determine exactly where tick text should be drawn for j in range(len(strings)): vstr = strings[j] if vstr is None: ## this tick was ignored because it is out of bounds continue x = tickPositions[i][j] # textRect = p.boundingRect(QtCore.QRectF(0, 0, 100, 100), QtCore.Qt.AlignCenter, vstr) textRect = rects[j] height = textRect.height() width = textRect.width() # self.textHeight = height offset = max(0, self.style['tickLength']) + textOffset textFlags = QtCore.Qt.TextDontClip | QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter if self.pOrientation is "azimuthal": # Originally wanted to put the text on the outside of # the viewbox, but that was breaking things, especially # because it wasn't handling intialization correctly, when the # viewbox is first being setup. #pp = tickSpecs[j][-1] # Take the outside coordinate pp = self._bounds["radial"][1] if np.isfinite( self._bounds["radial"][1]) else tickSpecs[j][-1] pp = Point(pp * np.cos(x * 3.14159 / 180), -pp * np.sin(x * 3.14159 / 180)) rect = QtCore.QRectF(0, 0, width, height) rect.moveCenter(Point(0, 0)) xm = x % 360 if xm == 0: rect.moveLeft(pp.x()) # print("00pp", pp) elif 0 < xm < 90: rect.moveBottomLeft(pp) elif xm == 90: rect.moveBottom(pp.y()) # print("90pp", pp) elif 90 < xm < 180: rect.moveBottomRight(pp) elif xm == 180: rect.moveRight(pp.x()) elif 180 < xm < 270: rect.moveTopRight(pp) elif xm == 270: rect.moveTop(pp.y()) elif 270 < xm < 360: rect.moveTopLeft(pp) elif self.pOrientation is "radial": rect = QtCore.QRectF(0, 0, width, height) # rect.moveCenter(Point(0, 0)) rect.moveBottomLeft( Point( x * np.cos(self._radialLabelPosition * np.pi / 180), # Don't forget to flip the axis x * np.sin(-self._radialLabelPosition * np.pi / 180))) self.fullBoundingRect |= rect # p.setPen(self.pen()) # p.drawText(rect, textFlags, vstr) textSpecs.append((rect, textFlags, vstr)) ## update max text size if needed. self._updateMaxTextSize(textSize2) return (axisSpec, tickSpecs, textSpecs)
def scaleBy(self, s=None, center=None, x=None, y=None): if s is not None: scale = Point(s) else: scale = [x, y] affect = [True, True] if scale[0] is None and scale[1] is None: return elif scale[0] is None: affect[0] = False scale[0] = 1.0 elif scale[1] is None: affect[1] = False scale[1] = 1.0 scale = Point(scale) if self.state['aspectLocked'] is not False: scale[0] = scale[1] """Edit""" vr = self.viewRect() """End of Edit""" if center is None: center = Point(vr.center()) else: center = Point(center) tl = center + (vr.topLeft() - center) * scale br = center + (vr.bottomRight() - center) * scale """Addition""" yMax = self.state['limits']['yLimits'][1] xMax = self.state['limits']['xLimits'][1] xMin = self.state['limits']['xLimits'][0] yMin = self.state['limits']['yLimits'][0] scale_limit = [] if yMax is not None and affect[0]: if (br.y() > yMax): scale_limit.append((yMax - center.y()) / (1.0 * vr.bottomRight().y() - center.y())) print 'yMax scale is :' + str(scale_limit[-1]) if xMax is not None: if (br.x() > xMax) and affect[1]: scale_limit.append((xMax - center.x()) / (1.0 * vr.bottomRight().x() - center.x())) print 'xMax scale is :' + str(scale_limit[-1]) if xMin is not None: if (tl.x() < xMin) and affect[1]: scale_limit.append((xMin - center.x()) / (1.0 * vr.topLeft().x() - center.x())) print 'xMin scale is :' + str(scale_limit[-1]) if yMin is not None: if (tl.y() < yMin) and affect[0]: scale_limit.append((yMin - center.y()) / (1.0 * vr.topLeft().y() - center.y())) print 'yMin scale is :' + str(scale_limit[-1]) if self.state['aspectLocked'] is not False and len(scale_limit) > 0: min_scale_limit = min(scale_limit) scale[1] = min_scale_limit scale[0] = scale[1] tl = center + (vr.topLeft() - center) * scale br = center + (vr.bottomRight() - center) * scale """End of Addition""" if not affect[0]: self.setYRange(tl.y(), br.y(), padding=0) elif not affect[1]: self.setXRange(tl.x(), br.x(), padding=0) else: self.setRange(QtCore.QRectF(tl, br), padding=0)