def testSelection(self, data): """ Given a Parameters ---------- data : (N, 2) array Point coordinates """ if len(data) == 0: return numpy.zeros(0, dtype=bool) def contained(a, left, top, right, bottom): assert left <= right and bottom <= top x, y = a.T return (x >= left) & (x <= right) & (y <= top) & (y >= bottom) data = numpy.asarray(data) selected = numpy.zeros(len(data), dtype=bool) for p1, p2 in self.selection: r = QRectF(p1, p2).normalized() # Note the inverted top/bottom (Qt coordinate system) selected |= contained(data, r.left(), r.bottom(), r.right(), r.top()) return selected
def _fetch_tile_layer(self, timestamp, ims, transform, tile_nr, stack_id, ims_req, cache): """ Fetch a single tile from a layer (ImageSource). Parameters ---------- timestamp The timestamp at which ims_req was created ims The layer (image source) we're fetching from transform The transform to apply to the fetched data, before storing it in the cache tile_nr The ID of the fetched tile stack_id The stack ID of the tile we're fetching (e.g. which T-slice and Z-slice this tile belongs to) ims_req A request object (e.g. GrayscaleImageRequest) with a wait() method that produces an item of the appropriate type for the layer (i.e. either a QImage or a QGraphicsItem) cache The value of self._cache at the time the ims_req was created. (The cache can be replaced occasionally. See TileProvider._onSizeChanged().) """ try: try: with cache: layerTimestamp = cache.layerTimestamp(stack_id, ims, tile_nr) except KeyError: # May not be a timestamp yet (especially when prefetching) layerTimestamp = 0 tile_rect = QRectF( self.tiling.imageRects[tile_nr] ) if timestamp > layerTimestamp: img = ims_req.wait() if isinstance(img, QImage): img = img.transformed(transform) elif isinstance(img, QGraphicsItem): # FIXME: It *seems* like applying the same transform to QImages and QGraphicsItems # makes sense here, but for some strange reason it isn't right. # For QGraphicsItems, it seems obvious that this is the correct transform. # I do not understand the formula that produces 'transform', which is used for QImage tiles. img.setTransform(QTransform.fromTranslate(tile_rect.left(), tile_rect.top()), combine=True) img.setTransform(self.tiling.data2scene, combine=True) else: assert False, "Unexpected image type: {}".format( type(img) ) with cache: try: cache.updateTileIfNecessary(stack_id, ims, tile_nr, timestamp, img) except KeyError: pass if stack_id == self._current_stack_id \ and cache is self._cache: self.sceneRectChanged.emit( tile_rect ) except BaseException: sys.excepthook( *sys.exc_info() )
def inputLocation( self ): """ Returns the input location for this connection. :return <XConnectionLocation> """ if ( not self.autoCalculateInputLocation() ): return self._inputLocation # auto calculate directions based on the scene if ( self._outputNode ): outputRect = self._outputNode.sceneRect() else: y = self._outputPoint.y() outputRect = QRectF( self._outputPoint.x(), y, 0, 0 ) if ( self._inputNode ): inputRect = self._inputNode.sceneRect() else: y = self._inputPoint.y() inputRect = QRectF( self._inputPoint.x(), y, 0, 0 ) # use the input location as potential places where it can be iloc = self._inputLocation left = XConnectionLocation.Left right = XConnectionLocation.Right top = XConnectionLocation.Top bot = XConnectionLocation.Bottom if ( self._inputNode == self._outputNode ): if ( iloc & right ): return right elif ( iloc & left ): return left elif ( iloc & top ): return top else: return bot elif ( (iloc & left) and outputRect.right() < inputRect.left() ): return left elif ( (iloc & right) and inputRect.right() < outputRect.left() ): return right elif ( (iloc & top) and outputRect.bottom() < inputRect.top() ): return top elif ( (iloc & bot) ): return bot elif ( (iloc & left) ): return left elif ( (iloc & right) ): return right elif ( (iloc & top) ): return top else: return left
def outputLocation( self ): """ Returns the location for the output source position. :return <XConnectionLocation> """ if ( not self.autoCalculateOutputLocation() ): return self._outputLocation # auto calculate directions based on the scene if ( self._outputNode ): outputRect = self._outputNode.sceneRect() else: y = self._outputPoint.y() outputRect = QRectF( self._outputPoint.x(), y, 0, 0 ) if ( self._inputNode ): inputRect = self._inputNode.sceneRect() else: y = self._inputPoint.y() inputRect = QRectF( self._inputPoint.x(), y, 0, 0 ) oloc = self._outputLocation left = XConnectionLocation.Left right = XConnectionLocation.Right top = XConnectionLocation.Top bot = XConnectionLocation.Bottom if ( self._inputNode == self._outputNode ): if ( oloc & right ): return right elif ( oloc & left ): return left elif ( oloc & top ): return top else: return bot elif ( (oloc & right) and outputRect.right() < inputRect.left() ): return right elif ( (oloc & left) and inputRect.right() < outputRect.left() ): return left elif ( (oloc & bot) and outputRect.bottom() < inputRect.top() ): return bot elif ( (oloc & top) ): return top elif ( (oloc & right) ): return right elif ( (oloc & left) ): return left elif ( (oloc & bot) ): return bot else: return right
def toSearchRect(self, point): size = 20 rect = QRectF() rect.setLeft(point.x() - size) rect.setRight(point.x() + size) rect.setTop(point.y() - size) rect.setBottom(point.y() + size) transform = self.canvas.getCoordinateTransform() ll = transform.toMapCoordinates(rect.left(), rect.bottom()) ur = transform.toMapCoordinates(rect.right(), rect.top()) rect = QgsRectangle(ur, ll) return rect
class GradientLegendItem(QGraphicsObject, GraphicsWidgetAnchor): gradient_width = 20 def __init__(self, title, palette, values, parent): QGraphicsObject.__init__(self, parent) GraphicsWidgetAnchor.__init__(self) self.parent = self.legend = parent self.palette = palette self.values = values self.title = QGraphicsTextItem('%s:' % title, self) f = self.title.font() f.setBold(True) self.title.setFont(f) self.title_item = QGraphicsRectItem(self.title.boundingRect(), self) self.title_item.setPen(QPen(Qt.NoPen)) self.title_item.stackBefore(self.title) self.label_items = [QGraphicsTextItem(text, self) for text in values] for i in self.label_items: i.setTextWidth(50) self.rect = QRectF() self.gradient_item = QGraphicsRectItem(self) self.gradient = QLinearGradient() self.gradient.setStops([(v * 0.1, self.palette[v * 0.1]) for v in range(11)]) self.orientation = Qt.Horizontal self.set_orientation(Qt.Vertical) self.setFlag(QGraphicsItem.ItemIgnoresTransformations, True) self.setFlag(QGraphicsItem.ItemIsMovable, True) def set_orientation(self, orientation): return if self.orientation == orientation: return self.orientation = orientation if self.orientation == Qt.Vertical: height = max(item.boundingRect().height() for item in self.label_items) total_height = height * max(5, len(self.label_items)) interval = (total_height - self.label_items[-1].boundingRect().height() ) / (len(self.label_items) - 1) self.gradient_item.setRect(10, 0, self.gradient_width, total_height) self.gradient.setStart(10, 0) self.gradient.setFinalStop(10, total_height) self.gradient_item.setBrush(QBrush(self.gradient)) self.gradient_item.setPen(QPen(Qt.NoPen)) y = -20 # hja, no; dela --> pri boundingRect() zato pristejem +20 x = 0 move_item_xy(self.title, x, y, False) y = 10 x = 30 for item in self.label_items: move_item_xy(item, x, y, False) # self.parent.graph.animate_plot) y += interval self.rect = QRectF(10, 0, self.gradient_width + max(item.boundingRect().width() for item in self.label_items), self.label_items[0].boundingRect().height() * max(5, len(self.label_items))) else: # za horizontalno orientacijo nisem dodajal title-a width = 50 height = max(item.boundingRect().height() for item in self.label_items) total_width = width * max(5, len(self.label_items)) interval = (total_width - self.label_items[-1].boundingRect().width() ) / (len(self.label_items) - 1) self.gradient_item.setRect(0, 0, total_width, self.gradient_width) self.gradient.setStart(0, 0) self.gradient.setFinalStop(total_width, 0) self.gradient_item.setBrush(QBrush(self.gradient)) self.gradient_item.setPen(QPen(Qt.NoPen)) x = 0 y = 30 for item in self.label_items: move_item_xy(item, x, y, False) # self.parent.graph.animate_plot) x += interval self.rect = QRectF(0, 0, total_width, self.gradient_width + height) # noinspection PyPep8Naming def boundingRect(self): width = max(self.rect.width(), self.title_item.boundingRect().width()) height = self.rect.height() + self.title_item.boundingRect().height() return QRectF(self.rect.left(), self.rect.top(), width, height) def paint(self, painter, option, widget): pass
class ZoomSlider(QFrame): """ Two way slider representing narrowing of a view. The sliders coorespond to factors: a min value and a max value in the range [0, 1] Emits zoomValueChanged(float, float) whenever the markers are adjusted. (float, float) -> (min, max) """ def __init__(self, parent=None, horizontal=True): QFrame.__init__(self, parent) self.horizontal = horizontal if horizontal: self.setFrameShape(QFrame.HLine) self.setMinimumHeight(21) self.tilt = 90 else: self.setFrameShape(QFrame.VLine) self.setMinimumWidth(21) self.tilt = 180 self.setFrameShadow(QFrame.Sunken) self.setMidLineWidth(3) self.setMouseTracking(True) self.size = 12 self.min_value = 0.0 self.max_value = 1.0 self.setDefaultColors() self.button = Qt.NoButton self.selected_marker = 'none' def paintEvent(self, paint_event): QFrame.paintEvent(self, paint_event) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) w = self.width() h = self.height() if self.horizontal: self.min_marker = QRectF(w * self.min_value, 4, self.size, self.size) self.max_marker = QRectF(w * self.max_value - self.size - 1, 4, self.size, self.size) else: self.min_marker = QRectF(4, h - h * self.min_value - self.size - 1, self.size, self.size) self.max_marker = QRectF(4, h - h * self.max_value, self.size, self.size) pen = painter.pen() pen.setWidth(0) pen.setColor(QApplication.palette().background().color().dark()) painter.setPen(pen) painter.setBrush(self.min_marker_brush) painter.drawPie(self.min_marker, self.tilt * 16, 180 * 16) painter.setBrush(self.max_marker_brush) painter.drawPie(self.max_marker, self.tilt * 16, -180 * 16) def resizeEvent(self, resize_event): QFrame.resizeEvent(self, resize_event) def _getMinTestMarker(self): """Returns the "real" marker bounds. Adjusted for the missing part of an arc.""" if self.horizontal: return QRectF(self.min_marker.left(), self.min_marker.top(), self.min_marker.width() / 2.0, self.min_marker.height()) else: return QRectF( self.min_marker.left(), self.min_marker.top() + self.min_marker.height() / 2.0, self.min_marker.width(), self.min_marker.height() / 2.0) def _getMaxTestMarker(self): """Returns the "real" marker bounds. Adjusted for the missing part of an arc.""" if self.horizontal: return QRectF( self.max_marker.left() + self.max_marker.width() / 2.0, self.max_marker.top(), self.max_marker.width() / 2.0, self.max_marker.height()) else: return QRectF(self.max_marker.left(), self.max_marker.top(), self.max_marker.width(), self.max_marker.height() / 2.0) def mouseMoveEvent(self, mouse_event): """Dragging or highlighting the markers.""" self.setDefaultColors() min_test_marker = self._getMinTestMarker() if min_test_marker.contains( mouse_event.x(), mouse_event.y()) or self.selected_marker == 'min': self.min_marker_brush = self.getDefaultHighlightColor() if self.selected_marker == 'min': if self.horizontal: value = mouse_event.x() / float(self.width()) else: value = (self.height() - mouse_event.y()) / float( self.height()) self.setMinValue(value, False) max_test_marker = self._getMaxTestMarker() if max_test_marker.contains( mouse_event.x(), mouse_event.y()) or self.selected_marker == 'max': self.max_marker_brush = self.getDefaultHighlightColor() if self.selected_marker == 'max': if self.horizontal: value = mouse_event.x() / float(self.width()) else: value = (self.height() - mouse_event.y()) / float( self.height()) self.setMaxValue(value, False) self.update() def mousePressEvent(self, mouse_event): """Selecting a marker.""" if mouse_event.button() == Qt.LeftButton: min_test_marker = self._getMinTestMarker() if min_test_marker.contains(mouse_event.x(), mouse_event.y()): self.selected_marker = 'min' max_test_marker = self._getMaxTestMarker() if max_test_marker.contains(mouse_event.x(), mouse_event.y()): self.selected_marker = 'max' def mouseReleaseEvent(self, mouse_event): self.selected_marker = 'none' def leaveEvent(self, event): self.setDefaultColors() def getDefaultMarkerColor(self): return QApplication.palette().background().color().light(175) def getDefaultHighlightColor(self): return QApplication.palette().highlight().color() def setDefaultColors(self): self.min_marker_brush = self.getDefaultMarkerColor() self.max_marker_brush = self.getDefaultMarkerColor() self.update() def setMaxValue(self, max_value, update=True): """The the position of the max marker.""" if self.horizontal: m = float(self.width()) else: m = float(self.height()) marker_offset = (self.size + 1) / m if not self.max_value == max_value: self.max_value = max_value if self.max_value - marker_offset <= self.min_value: self.max_value = self.min_value + marker_offset if self.max_value > 1.0: self.max_value = 1 #print "max:", self.min_value, self.max_value self.emit(SIGNAL('zoomValueChanged(float, float)'), self.min_value, self.max_value) if update: self.update() def setMinValue(self, min_value, update=True): """The the position of the min marker.""" if self.horizontal: m = float(self.width()) else: m = float(self.height()) marker_offset = (self.size + 1) / m if not self.min_value == min_value: self.min_value = min_value if self.min_value + marker_offset >= self.max_value: self.min_value = self.max_value - marker_offset if self.min_value < 0.0: self.min_value = 0.0 #print "min:", self.min_value, self.max_value self.emit(SIGNAL('zoomValueChanged(float, float)'), self.min_value, self.max_value) if update: self.update()
def draw(self, painter, size=None): """ :Arguments: painter : QPainter Opened painter on which to draw """ bounding_rect = QRectF() position = self.position transfer_function = self.transfer_function font = QFont(self.font) text_color = self.text_color line_color = self.line_color line_thickness = self.line_thickness value_range = self.value_range if size is None: viewport = painter.viewport() # viewport rectangle mat, ok = painter.worldMatrix().inverted() if not ok: raise ValueError( "Transformation matrix of painter is singular.") viewport = mat.mapRect(viewport) else: viewport = size # First, prepare the gradient w = viewport.width() h = viewport.height() #print("Size of viewport: {0}x{1}".format(w, h)) gr = QLinearGradient() nb_values = ceil(w / 5.0) brush_color = QColor() for i in range(int(nb_values)): brush_color.setRgbF(*transfer_function.rgba(i / nb_values)) gr.setColorAt(i / nb_values, brush_color) # Second, find its position metric = QFontMetricsF(font, painter.device()) font_test = [str(i) * 5 for i in range(10)] lim_width = 0 lim_height = 0 for t in font_test: rect = metric.boundingRect(t) lim_width = max(lim_width, rect.width()) lim_height = max(lim_height, rect.height()) lim_height *= 3 length = self.scale_length shift_length = (1 - length) / 2 width = self.scale_width shift_width = self.scale_shift_width delta_value = value_range[1] - value_range[0] if position == "Top": scale_rect = QRectF(shift_length * w, shift_width * h, length * w, width * h) limit_rect(scale_rect, viewport, lim_width, lim_height) gr.setStart(scale_rect.left(), scale_rect.center().y()) gr.setFinalStop(scale_rect.right(), scale_rect.center().y()) start_pos = scale_rect.bottomLeft() end_pos = scale_rect.bottomRight() elif position == "Right": scale_rect = QRectF((1 - shift_width - width) * w, shift_length * h, width * w, length * h) limit_rect(scale_rect, viewport, lim_width, lim_height) gr.setStart(scale_rect.center().x(), scale_rect.bottom()) gr.setFinalStop(scale_rect.center().x(), scale_rect.top()) start_pos = scale_rect.bottomLeft() end_pos = scale_rect.topLeft() elif position == "Bottom": scale_rect = QRectF(shift_length * w, (1 - shift_width - width) * h, length * w, width * h) limit_rect(scale_rect, viewport, lim_width, lim_height) gr.setStart(scale_rect.left(), scale_rect.center().y()) gr.setFinalStop(scale_rect.right(), scale_rect.center().y()) start_pos = scale_rect.topLeft() end_pos = scale_rect.topRight() elif position == "Left": scale_rect = QRectF(shift_width * w, shift_length * h, width * w, length * h) limit_rect(scale_rect, viewport, lim_width, lim_height) gr.setStart(scale_rect.center().x(), scale_rect.bottom()) gr.setFinalStop(scale_rect.center().x(), scale_rect.top()) start_pos = scale_rect.bottomRight() end_pos = scale_rect.topRight() else: raise ValueError("Invalid scale position: %s" % position) shift_pos = (end_pos - start_pos) / delta_value if position in ["Left", "Right"]: is_vertical = True length = scale_rect.height() else: is_vertical = False length = scale_rect.width() # Get the ticks ticks = self.selectValues(length, is_vertical, painter) if len(ticks) == 0: return ticks_str, ticks_extra = self._tick2str(ticks) # Figure the shifts dist_to_bar = self.text_to_bar max_width = 0 max_height = 0 for t in ticks_str: rect = metric.boundingRect(t) max_width = max(rect.width(), max_width) max_height = max(rect.height(), max_height) if position == "Left": shift_left = dist_to_bar shift_top = None elif position == "Right": shift_left = -dist_to_bar - max_width shift_top = None elif position == "Top": shift_left = None shift_top = dist_to_bar else: shift_left = None shift_top = -dist_to_bar - max_height painter.save() painter.translate(viewport.topLeft()) #print("viewport.topLeft() = {0}x{1}".format(viewport.left(), viewport.top())) painter.setBrush(gr) line_pen = QPen(line_color) line_pen.setWidth(line_thickness) painter.setPen(line_pen) painter.drawRect(scale_rect) bounding_rect |= scale_rect #print("Scale rect: +{0}+{1}x{2}x{3}".format(scale_rect.left(), #scale_rect.top(), scale_rect.width(), scale_rect.height())) painter.setFont(font) painter.setPen(text_color) for ts, t in zip(ticks_str, ticks): r = metric.boundingRect(ts) pos = start_pos + shift_pos * (t - value_range[0]) if shift_left is None: pos.setX(pos.x() - r.width() / 2) else: pos.setX(pos.x() + shift_left) if shift_top is None: pos.setY(pos.y() - r.height() / 2) else: pos.setY(pos.y() + shift_top) r.moveTo(pos) real_rect = painter.drawText( r, Qt.TextDontClip | Qt.AlignVCenter | Qt.AlignHCenter, ts) bounding_rect |= real_rect if ticks_extra is not None or self.unit: unit = self.unit exp_width = width = space_width = 0 exp_txt = "" r = exp_r = unit_r = QRectF() exp_font = None if ticks_extra is not None: exp_txt = u"×10" r = metric.boundingRect(exp_txt) exp_font = QFont(font) exp_size = self.exp_size if exp_font.pixelSize() != -1: exp_font.setPixelSize(exp_size * exp_font.pixelSize()) else: exp_font.setPointSizeF(exp_size * exp_font.pointSizeF()) exp_metric = QFontMetricsF(exp_font, painter.device()) exp_r = exp_metric.boundingRect(ticks_extra) if unit: unit_r = metric.boundingRect(unit) total_width = r.width() + exp_r.width() + unit_r.width() total_height = max(r.height(), unit_r.height()) + exp_r.height() / 2 pos = scale_rect.topRight() log_debug("top right of scale bar = (%g,%g)" % (pos.x(), pos.y())) log_debug("Size of image = (%d,%d)" % (w, h)) log_debug("Size of text = (%g,%g)" % (total_width, total_height)) if position == "Bottom": pos.setY(pos.y() + scale_rect.height() + dist_to_bar) pos.setX(pos.x() - total_width) elif position == "Top": pos.setY(pos.y() - dist_to_bar - total_height) pos.setX(pos.x() - total_width) else: # position == "left" or "right" pos.setX(pos.x() - (scale_rect.width() + total_width) / 2) if pos.x() < 0: pos.setX(dist_to_bar) elif pos.x() + total_width + dist_to_bar > w: pos.setX(w - total_width - dist_to_bar) pos.setY(pos.y() - dist_to_bar - total_height) log_debug("Display unit at position: (%g,%g)" % (pos.x(), pos.y())) if ticks_extra is not None: r.moveTo(pos) real_rect = painter.drawText( r, Qt.TextDontClip | Qt.AlignVCenter | Qt.AlignHCenter, exp_txt) bounding_rect |= real_rect pos.setX(pos.x() + r.width()) pos.setY(pos.y() - metric.ascent() / 2) exp_r.moveTo(pos) painter.setFont(exp_font) real_rect = painter.drawText( exp_r, Qt.TextDontClip | Qt.AlignVCenter | Qt.AlignHCenter, ticks_extra) bounding_rect |= real_rect pos.setY(pos.y() + metric.ascent() / 2) if unit: pos.setX(pos.x() + space_width + exp_r.width()) unit_r.moveTo(pos) painter.setFont(font) real_rect = painter.drawText( unit_r, Qt.TextDontClip | Qt.AlignVCenter | Qt.AlignHCenter, unit) bounding_rect |= real_rect # Draw the ticks now painter.setPen(line_pen) tick_size = self.tick_size if is_vertical: width = scale_rect.width() * tick_size else: width = scale_rect.height() * tick_size pen_width = painter.pen().widthF() if pen_width == 0: pen_width = 1.0 for t in ticks: pos1 = start_pos + shift_pos * (t - value_range[0]) pos2 = QPointF(pos1) if is_vertical: pos1.setX(scale_rect.left() + pen_width) pos2.setX(pos1.x() + width - pen_width) painter.drawLine(pos1, pos2) pos1.setX(scale_rect.right() - pen_width) pos2.setX(pos1.x() - width + pen_width) painter.drawLine(pos1, pos2) else: pos1.setY(scale_rect.top() + pen_width) pos2.setY(pos1.y() + width - pen_width) painter.drawLine(pos1, pos2) pos1.setY(scale_rect.bottom() - pen_width) pos2.setY(pos1.y() - width + pen_width) painter.drawLine(pos1, pos2) painter.restore() bounding_rect = bounding_rect.adjusted(-pen_width, -pen_width, pen_width, pen_width) return bounding_rect
class ZoomSlider(QFrame): """ Two way slider representing narrowing of a view. The sliders coorespond to factors: a min value and a max value in the range [0, 1] Emits zoomValueChanged(float, float) whenever the markers are adjusted. (float, float) -> (min, max) """ def __init__(self, parent=None, horizontal=True): QFrame.__init__(self, parent) self.horizontal = horizontal if horizontal: self.setFrameShape(QFrame.HLine) self.setMinimumHeight(21) self.tilt = 90 else: self.setFrameShape(QFrame.VLine) self.setMinimumWidth(21) self.tilt = 180 self.setFrameShadow(QFrame.Sunken) self.setMidLineWidth(3) self.setMouseTracking(True) self.size = 12 self.min_value = 0.0 self.max_value = 1.0 self.setDefaultColors() self.button = Qt.NoButton self.selected_marker = 'none' def paintEvent(self, paint_event): QFrame.paintEvent(self, paint_event) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) w = self.width() h = self.height() if self.horizontal: self.min_marker = QRectF(w * self.min_value, 4, self.size, self.size) self.max_marker = QRectF(w * self.max_value - self.size - 1, 4, self.size, self.size) else: self.min_marker = QRectF(4, h - h * self.min_value - self.size - 1, self.size, self.size) self.max_marker = QRectF(4, h - h * self.max_value, self.size, self.size) pen = painter.pen() pen.setWidth(0) pen.setColor(QApplication.palette().background().color().dark()) painter.setPen(pen) painter.setBrush(self.min_marker_brush) painter.drawPie(self.min_marker, self.tilt * 16, 180 * 16) painter.setBrush(self.max_marker_brush) painter.drawPie(self.max_marker, self.tilt * 16, -180 * 16) def resizeEvent (self, resize_event): QFrame.resizeEvent(self, resize_event) def _getMinTestMarker(self): """Returns the "real" marker bounds. Adjusted for the missing part of an arc.""" if self.horizontal: return QRectF(self.min_marker.left(), self.min_marker.top(), self.min_marker.width() / 2.0, self.min_marker.height()) else: return QRectF(self.min_marker.left(), self.min_marker.top() + self.min_marker.height() / 2.0, self.min_marker.width(), self.min_marker.height() / 2.0) def _getMaxTestMarker(self): """Returns the "real" marker bounds. Adjusted for the missing part of an arc.""" if self.horizontal: return QRectF(self.max_marker.left() + self.max_marker.width() / 2.0, self.max_marker.top(), self.max_marker.width() / 2.0, self.max_marker.height()) else: return QRectF(self.max_marker.left(), self.max_marker.top(), self.max_marker.width(), self.max_marker.height() / 2.0) def mouseMoveEvent (self, mouse_event): """Dragging or highlighting the markers.""" self.setDefaultColors() min_test_marker = self._getMinTestMarker() if min_test_marker.contains(mouse_event.x(), mouse_event.y()) or self.selected_marker == 'min': self.min_marker_brush = self.getDefaultHighlightColor() if self.selected_marker == 'min': if self.horizontal: value = mouse_event.x() / float(self.width()) else: value = (self.height() - mouse_event.y()) / float(self.height()) self.setMinValue(value, False) max_test_marker = self._getMaxTestMarker() if max_test_marker.contains(mouse_event.x(), mouse_event.y()) or self.selected_marker == 'max': self.max_marker_brush = self.getDefaultHighlightColor() if self.selected_marker == 'max': if self.horizontal: value = mouse_event.x() / float(self.width()) else: value = (self.height() - mouse_event.y()) / float(self.height()) self.setMaxValue(value, False) self.update() def mousePressEvent (self, mouse_event): """Selecting a marker.""" if mouse_event.button() == Qt.LeftButton: min_test_marker = self._getMinTestMarker() if min_test_marker.contains(mouse_event.x(), mouse_event.y()): self.selected_marker = 'min' max_test_marker = self._getMaxTestMarker() if max_test_marker.contains(mouse_event.x(), mouse_event.y()): self.selected_marker = 'max' def mouseReleaseEvent (self, mouse_event): self.selected_marker = 'none' def leaveEvent (self, event): self.setDefaultColors() def getDefaultMarkerColor(self): return QApplication.palette().background().color().light(175) def getDefaultHighlightColor(self): return QApplication.palette().highlight().color() def setDefaultColors(self): self.min_marker_brush = self.getDefaultMarkerColor() self.max_marker_brush = self.getDefaultMarkerColor() self.update() def setMaxValue(self, max_value, update=True): """The the position of the max marker.""" if self.horizontal: m = float(self.width()) else: m = float(self.height()) marker_offset = (self.size + 1) / m if not self.max_value == max_value: self.max_value = max_value if self.max_value - marker_offset <= self.min_value: self.max_value = self.min_value + marker_offset if self.max_value > 1.0: self.max_value = 1 self.emit(SIGNAL('zoomValueChanged(float, float)'), self.min_value, self.max_value) if update: self.update() def setMinValue(self, min_value, update=True): """The the position of the min marker.""" if self.horizontal: m = float(self.width()) else: m = float(self.height()) marker_offset = (self.size + 1) / m if not self.min_value == min_value: self.min_value = min_value if self.min_value + marker_offset >= self.max_value: self.min_value = self.max_value - marker_offset if self.min_value < 0.0: self.min_value = 0.0 self.emit(SIGNAL('zoomValueChanged(float, float)'), self.min_value, self.max_value) if update: self.update()
def run_loader(self): filename = self.result try: self.retryObject = None # First, prepare the data by getting the images and computing how big they # should be f = open(filename) first_line = f.readline() f.close() if first_line.startswith("TRKR_VERSION"): result = Result(None) result.load(self.result, **self._loading_arguments) result_type = "Growth" else: result = TrackingData() result.load(self.result, **self._loading_arguments) result_type = "Data" self.result = result self.result_type = result_type if result_type == "Data": data = result images = data.images_name if data.cells: self.has_cells = True self.has_walls = True else: self.has_cells = False self.has_walls = False self.has_points = bool(data.cell_points) else: data = result.data images = result.images self.has_cells = False self.has_walls = False self.has_points = False self.images = images cache = image_cache.cache self.update_nb_images(len(result)) bbox = QRectF() ms = data.minScale() for i in range(len(result)): img_name = images[i] img_data = data[img_name] img = cache.image(data.image_path(img_name)) matrix = QTransform() matrix = img_data.matrix() sc = QTransform() sc.scale(1.0/ms, 1.0/ms) matrix *= sc r = QRectF(img.rect()) rbox = matrix.map(QPolygonF(r)).boundingRect() bbox |= rbox log_debug("Image '%s':\n\tSize = %gx%g\n\tTransformed = %gx%g %+g %+g\n\tGlobal bbox = %gx%g %+g %+g\n" % (img_name, r.width(), r.height(), rbox.width(), rbox.height(), rbox.left(), rbox.top(), bbox.width(), bbox.height(), bbox.left(), bbox.top())) log_debug("Matrix:\n%g\t%g\t%g\n%g\t%g\t%g\n" % (matrix.m11(), matrix.m12(), matrix.dx(), matrix.m21(), matrix.m22(), matrix.dy())) if result_type == "Growth": if result.cells[i]: self.has_cells = True if result.walls[i]: self.has_walls = True self.has_points = bool(result.data.cell_points) self.nextImage() translate = bbox.topLeft() translate *= -1 self.translate = translate size = bbox.size().toSize() self.img_size = size self._crop = QRect(QPoint(0,0), size) self.finished() self._loading_arguments = {} # All done, we don't need that anymore except RetryTrackingDataException as ex: ex.filename = filename self.retryObject = ex self.finished() return except Exception as ex: _, _, exceptionTraceback = sys.exc_info() self.abort(ex, traceback=exceptionTraceback) raise
def layout(self, scene, nodes, center=None, padX=None, padY=None, direction=None, animationGroup=None): """ Lays out the nodes for this scene based on a block layering algorithm. :param scene | <XNodeScene> nodes | [<XNode>, ..] center | <QPointF> || None padX | <int> || None padY | <int> || None direction | <Qt.Direction> animationGroup | <QAnimationGroup> || None :return {<XNode>: <QRectF>, ..} | new rects per affected node """ nodes = filter(lambda x: x is not None and x.isVisible(), nodes) # make sure we have at least 1 node, otherwise, it is already laid out if not nodes or len(nodes) == 1: return {} # calculate the default padding based on the scene if padX == None: if direction == Qt.Vertical: padX = 2 * scene.cellWidth() else: padX = 4 * scene.cellWidth() if padY == None: if direction == Qt.Vertical: padY = 4 * scene.cellHeight() else: padY = 2 * scene.cellWidth() # step 1: create a mapping of the connections connection_map = self.connectionMap(scene, nodes) # step 2: organize the nodes into layers based on their connection chain layers = self.generateLayers(scene, nodes, connection_map) layers = list(reversed(layers)) # step 3: calculate the total dimensions for the layout bounds = QRectF() # step 3.1: compare the nodes together that have common connections layer_widths = [] layer_heights = [] node_heights = {} node_widths = {} for layer_index, layer in enumerate(layers): layer_w = 0 layer_h = 0 layer_node_w = [] layer_node_h = [] self.organizeLayer(layer, connection_map) for node in layer: rect = node.rect() layer_node_w.append(rect.width()) layer_node_h.append(rect.height()) if direction == Qt.Vertical: layer_w += rect.width() layer_h = max(rect.height(), layer_h) else: layer_w = max(rect.width(), layer_w) layer_h += rect.height() # update the bounding area if direction == Qt.Vertical: layer_w += padX * 1 - len(layer) bounds.setWidth(max(layer_w, bounds.width())) bounds.setHeight(bounds.height() + layer_h) else: layer_h += padY * 1 - len(layer) bounds.setWidth(bounds.width() + layer_w) bounds.setHeight(max(layer_h, bounds.height())) node_widths[layer_index] = layer_node_w node_heights[layer_index] = layer_node_h layer_widths.append(layer_w) layer_heights.append(layer_h) if not center: center = scene.sceneRect().center() w = bounds.width() h = bounds.height() bounds.setX(center.x() - bounds.width() / 2.0) bounds.setY(center.y() - bounds.height() / 2.0) bounds.setWidth(w) bounds.setHeight(h) # step 4: assign positions for each node by layer processed_nodes = {} layer_grps = [(i, layer) for i, layer in enumerate(layers)] layer_grps.sort(key=lambda x: len(x[1])) for layer_index, layer in reversed(layer_grps): layer_width = layer_widths[layer_index] layer_height = layer_heights[layer_index] # determine the starting point for this layer if direction == Qt.Vertical: offset = layer_index * padY + sum(layer_heights[:layer_index]) point = QPointF(bounds.x(), offset + bounds.y()) else: offset = layer_index * padX + sum(layer_widths[:layer_index]) point = QPointF(offset + bounds.x(), bounds.y()) # assign node positions based on existing connections for node_index, node in enumerate(layer): max_, min_ = (None, None) inputs, outputs = connection_map[node] for connected_node in inputs + outputs: if not connected_node in processed_nodes: continue npos = processed_nodes[connected_node] nrect = connected_node.rect() rect = QRectF(npos.x(), npos.y(), nrect.width(), nrect.height()) if direction == Qt.Vertical: if min_ is None: min_ = rect.left() min_ = min(rect.left(), min_) max_ = max(rect.right(), max_) else: if min_ is None: min_ = rect.top() min_ = min(rect.top(), min_) max_ = max(rect.bottom(), max_) if direction == Qt.Vertical: off_x = 0 off_y = (layer_height - node.rect().height()) / 2.0 start_x = (bounds.width() - layer_width) start_y = 0 else: off_x = (layer_width - node.rect().width()) / 2.0 off_y = 0 start_x = 0 start_y = (bounds.height() - layer_height) # align against existing nodes if not None in (min_, max): if direction == Qt.Vertical: off_x = (max_ - min_) / 2.0 - node.rect().width() / 2.0 point_x = min_ + off_x point_y = point.y() + off_y else: off_y = (max_ - min_) / 2.0 - node.rect().height() / 2.0 point_x = point.x() + off_x point_y = min_ + off_y # otherwise, align based on its position in the layer else: if direction == Qt.Vertical: off_x = sum(node_widths[layer_index][:node_index]) off_x += node_index * padX off_x += start_x point_x = point.x() + off_x point_y = point.y() + off_y else: off_y = sum(node_heights[layer_index][:node_index]) off_y += node_index * padY off_y += start_y point_x = point.x() + off_x point_y = point.y() + off_y if not animationGroup: node.setPos(point_x, point_y) else: anim = XNodeAnimation(node, 'setPos') anim.setStartValue(node.pos()) anim.setEndValue(QPointF(point_x, point_y)) animationGroup.addAnimation(anim) processed_nodes[node] = QPointF(point_x, point_y) if self._testing: QApplication.processEvents() time.sleep(1) return processed_nodes
class TreeNode(object): """store data for a class and manage hierarchy""" def __init__(self, classid,name,rect=None,color=None,pos=None,accu = 1.0,parent=None): super(TreeNode, self).__init__() self.classid = classid self.name = name self.parent = parent self.accu = accu self.rect = QRectF(*rect) if rect else rect self.color = QColor(color) if color else color self.pos = QPointF(*pos) if pos else pos self.children = [] if parent: parent.children.append(self) # def __init__(self,jsonobj): # self.__init__(jsonobj['id'],jsonobj['name'],jsonobj['rect'],jsonobj['color'],jsonobj['pos']) # for child in jsonobj['children']: # childItem = TreeNode(child) # self.children.append(childItem) # childItem.parent = self def isLeaf(self): if self.children: return False else: return True def isRoot(self): if self.parent: return False else: return True def toJSON(self): jsonObj = {} jsonObj['id']= self.classid jsonObj['name'] = self.name jsonObj['rect'] = [self.rect.left(),self.rect.top(),self.rect.width(),self.rect.height()] if self.rect else self.rect jsonObj['color'] = str(self.color.name()) if self.color else self.color jsonObj['pos'] = [self.pos.x(),self.pos.y()] if isinstance(self.pos,QPointF) else self.pos jsonObj['accu'] = self.accu jsonObj['children'] = [] for child in self.children: jsonObj['children'].append(child.toJSON()) return jsonObj def __str__(self): tempstr = "%s %s\n" % (self.classid,self.name) for child in self.children: tempstr+= ' '+str(child) return tempstr def findNode(self,id): if self.classid == id: return self for child in self.children: result = child.findNode(id) if result: return result return None def toplogicalDistance(self,id1,id2): if id1==id2: return 0 else: node1 = self.findNode(id1) node2 = self.findNode(id2) if node1 and node2: if node1.parent == node2.parent: return 1 else: return 2 return 0 def matrixDistance(self,id1,id2): if id1==id2: return 0 else: node1 = self.findNode(id1) node2 = self.findNode(id2) if node1 and node2: pos1 = node1.pos + node1.parent.pos pos2 = node2.pos + node2.parent.pos return QLineF(pos1,pos2).length() return 2000.0 def transactionPossibility(self,id1,id2): node1 = self.findNode(id1) node2 = self.findNode(id2) if node1 and node2: return node1.accu * node2.accu else: return 1.0
def run_loader(self): filename = self.result try: self.retryObject = None # First, prepare the data by getting the images and computing how big they # should be f = open(filename) first_line = f.readline() f.close() if first_line.startswith("TRKR_VERSION"): result = Result(None) result.load(self.result, **self._loading_arguments) result_type = "Growth" else: result = TrackingData() result.load(self.result, **self._loading_arguments) result_type = "Data" self.result = result self.result_type = result_type if result_type == "Data": data = result images = data.images_name if data.cells: self.has_cells = True self.has_walls = True else: self.has_cells = False self.has_walls = False self.has_points = bool(data.cell_points) else: data = result.data images = result.images self.has_cells = False self.has_walls = False self.has_points = False self.images = images cache = image_cache.cache self.update_nb_images(len(result)) bbox = QRectF() ms = data.minScale() for i in range(len(result)): img_name = images[i] img_data = data[img_name] img = cache.image(data.image_path(img_name)) matrix = QTransform() matrix = img_data.matrix() sc = QTransform() sc.scale(1.0 / ms, 1.0 / ms) matrix *= sc r = QRectF(img.rect()) rbox = matrix.map(QPolygonF(r)).boundingRect() bbox |= rbox log_debug( "Image '%s':\n\tSize = %gx%g\n\tTransformed = %gx%g %+g %+g\n\tGlobal bbox = %gx%g %+g %+g\n" % (img_name, r.width(), r.height(), rbox.width(), rbox.height(), rbox.left(), rbox.top(), bbox.width(), bbox.height(), bbox.left(), bbox.top())) log_debug("Matrix:\n%g\t%g\t%g\n%g\t%g\t%g\n" % (matrix.m11(), matrix.m12(), matrix.dx(), matrix.m21(), matrix.m22(), matrix.dy())) if result_type == "Growth": if result.cells[i]: self.has_cells = True if result.walls[i]: self.has_walls = True self.has_points = bool(result.data.cell_points) self.nextImage() translate = bbox.topLeft() translate *= -1 self.translate = translate size = bbox.size().toSize() self.img_size = size self._crop = QRect(QPoint(0, 0), size) self.finished() self._loading_arguments = { } # All done, we don't need that anymore except RetryTrackingDataException as ex: ex.filename = filename self.retryObject = ex self.finished() return except Exception as ex: _, _, exceptionTraceback = sys.exc_info() self.abort(ex, traceback=exceptionTraceback) raise
def draw(self, painter, size = None): """ :Arguments: painter : QPainter Opened painter on which to draw """ bounding_rect = QRectF() position = self.position transfer_function = self.transfer_function font = QFont(self.font) text_color = self.text_color line_color = self.line_color line_thickness = self.line_thickness value_range = self.value_range if size is None: viewport = painter.viewport() # viewport rectangle mat, ok = painter.worldMatrix().inverted() if not ok: raise ValueError("Transformation matrix of painter is singular.") viewport = mat.mapRect(viewport) else: viewport = size # First, prepare the gradient w = viewport.width() h = viewport.height() #print("Size of viewport: {0}x{1}".format(w, h)) gr = QLinearGradient() nb_values = ceil(w/5.0) brush_color = QColor() for i in range(int(nb_values)): brush_color.setRgbF(*transfer_function.rgba(i/nb_values)) gr.setColorAt(i/nb_values, brush_color) # Second, find its position metric = QFontMetricsF(font, painter.device()) font_test = [ str(i)*5 for i in range(10) ] lim_width = 0 lim_height = 0 for t in font_test: rect = metric.boundingRect(t) lim_width = max(lim_width, rect.width()) lim_height = max(lim_height, rect.height()) lim_height *= 3 length = self.scale_length shift_length = (1-length)/2 width = self.scale_width shift_width = self.scale_shift_width delta_value = value_range[1]-value_range[0] if position == "Top": scale_rect = QRectF(shift_length*w, shift_width*h, length*w, width*h) limit_rect(scale_rect, viewport, lim_width, lim_height) gr.setStart(scale_rect.left(), scale_rect.center().y()) gr.setFinalStop(scale_rect.right(), scale_rect.center().y()) start_pos = scale_rect.bottomLeft() end_pos = scale_rect.bottomRight() elif position == "Right": scale_rect = QRectF((1-shift_width-width)*w, shift_length*h, width*w, length*h) limit_rect(scale_rect, viewport, lim_width, lim_height) gr.setStart(scale_rect.center().x(), scale_rect.bottom()) gr.setFinalStop(scale_rect.center().x(), scale_rect.top()) start_pos = scale_rect.bottomLeft() end_pos = scale_rect.topLeft() elif position == "Bottom": scale_rect = QRectF(shift_length*w, (1-shift_width-width)*h, length*w, width*h) limit_rect(scale_rect, viewport, lim_width, lim_height) gr.setStart(scale_rect.left(), scale_rect.center().y()) gr.setFinalStop(scale_rect.right(), scale_rect.center().y()) start_pos = scale_rect.topLeft() end_pos = scale_rect.topRight() elif position == "Left": scale_rect = QRectF(shift_width*w, shift_length*h, width*w, length*h) limit_rect(scale_rect, viewport, lim_width, lim_height) gr.setStart(scale_rect.center().x(), scale_rect.bottom()) gr.setFinalStop(scale_rect.center().x(), scale_rect.top()) start_pos = scale_rect.bottomRight() end_pos = scale_rect.topRight() else: raise ValueError("Invalid scale position: %s" % position) shift_pos = (end_pos-start_pos)/delta_value if position in ["Left", "Right"]: is_vertical = True length = scale_rect.height() else: is_vertical = False length = scale_rect.width() # Get the ticks ticks = self.selectValues(length, is_vertical, painter) if len(ticks) == 0: return ticks_str, ticks_extra = self._tick2str(ticks) # Figure the shifts dist_to_bar = self.text_to_bar max_width = 0 max_height = 0 for t in ticks_str: rect = metric.boundingRect(t) max_width = max(rect.width(), max_width) max_height = max(rect.height(), max_height) if position == "Left": shift_left = dist_to_bar shift_top = None elif position == "Right": shift_left = -dist_to_bar-max_width shift_top = None elif position == "Top": shift_left = None shift_top = dist_to_bar else: shift_left = None shift_top = -dist_to_bar-max_height painter.save() painter.translate(viewport.topLeft()) #print("viewport.topLeft() = {0}x{1}".format(viewport.left(), viewport.top())) painter.setBrush(gr) line_pen = QPen(line_color) line_pen.setWidth(line_thickness) painter.setPen(line_pen) painter.drawRect(scale_rect) bounding_rect |= scale_rect #print("Scale rect: +{0}+{1}x{2}x{3}".format(scale_rect.left(), #scale_rect.top(), scale_rect.width(), scale_rect.height())) painter.setFont(font) painter.setPen(text_color) for ts,t in zip(ticks_str, ticks): r = metric.boundingRect(ts) pos = start_pos+shift_pos*(t-value_range[0]) if shift_left is None: pos.setX( pos.x() - r.width()/2 ) else: pos.setX( pos.x() + shift_left ) if shift_top is None: pos.setY( pos.y() - r.height()/2) else: pos.setY( pos.y() + shift_top ) r.moveTo(pos) real_rect = painter.drawText(r, Qt.TextDontClip | Qt.AlignVCenter | Qt.AlignHCenter, ts) bounding_rect |= real_rect if ticks_extra is not None or self.unit: unit = self.unit exp_width = width = space_width = 0 exp_txt = "" r = exp_r = unit_r = QRectF() exp_font = None if ticks_extra is not None: exp_txt = u"×10" r = metric.boundingRect(exp_txt) exp_font = QFont(font) exp_size = self.exp_size if exp_font.pixelSize() != -1: exp_font.setPixelSize(exp_size*exp_font.pixelSize()) else: exp_font.setPointSizeF(exp_size*exp_font.pointSizeF()) exp_metric = QFontMetricsF(exp_font, painter.device()) exp_r = exp_metric.boundingRect(ticks_extra) if unit: unit_r = metric.boundingRect(unit) total_width = r.width()+exp_r.width()+unit_r.width() total_height = max(r.height(),unit_r.height())+exp_r.height()/2 pos = scale_rect.topRight() log_debug("top right of scale bar = (%g,%g)" % (pos.x(), pos.y())) log_debug("Size of image = (%d,%d)" % (w,h)) log_debug("Size of text = (%g,%g)" % (total_width, total_height)) if position == "Bottom": pos.setY(pos.y() + scale_rect.height() + dist_to_bar) pos.setX(pos.x() - total_width) elif position == "Top": pos.setY(pos.y() - dist_to_bar - total_height) pos.setX(pos.x() - total_width) else: # position == "left" or "right" pos.setX(pos.x() - (scale_rect.width() + total_width)/2) if pos.x() < 0: pos.setX(dist_to_bar) elif pos.x()+total_width+dist_to_bar > w: pos.setX(w - total_width - dist_to_bar) pos.setY(pos.y() - dist_to_bar - total_height) log_debug("Display unit at position: (%g,%g)" % (pos.x(), pos.y())) if ticks_extra is not None: r.moveTo(pos) real_rect = painter.drawText(r, Qt.TextDontClip | Qt.AlignVCenter | Qt.AlignHCenter, exp_txt) bounding_rect |= real_rect pos.setX( pos.x() + r.width() ) pos.setY( pos.y() - metric.ascent()/2 ) exp_r.moveTo(pos) painter.setFont(exp_font) real_rect = painter.drawText(exp_r, Qt.TextDontClip | Qt.AlignVCenter | Qt.AlignHCenter, ticks_extra) bounding_rect |= real_rect pos.setY(pos.y() + metric.ascent()/2) if unit: pos.setX(pos.x() + space_width + exp_r.width()) unit_r.moveTo(pos) painter.setFont(font) real_rect = painter.drawText(unit_r, Qt.TextDontClip | Qt.AlignVCenter | Qt.AlignHCenter, unit) bounding_rect |= real_rect # Draw the ticks now painter.setPen(line_pen) tick_size = self.tick_size if is_vertical: width = scale_rect.width()*tick_size else: width = scale_rect.height()*tick_size pen_width = painter.pen().widthF() if pen_width == 0: pen_width = 1.0 for t in ticks: pos1 = start_pos + shift_pos*(t-value_range[0]) pos2 = QPointF(pos1) if is_vertical: pos1.setX(scale_rect.left() + pen_width) pos2.setX(pos1.x() + width - pen_width) painter.drawLine(pos1, pos2) pos1.setX(scale_rect.right() - pen_width) pos2.setX(pos1.x() - width + pen_width) painter.drawLine(pos1, pos2) else: pos1.setY(scale_rect.top() + pen_width) pos2.setY(pos1.y() + width - pen_width) painter.drawLine(pos1, pos2) pos1.setY(scale_rect.bottom() - pen_width) pos2.setY(pos1.y() - width + pen_width) painter.drawLine(pos1, pos2) painter.restore() bounding_rect = bounding_rect.adjusted(-pen_width, -pen_width, pen_width, pen_width) return bounding_rect
class DrawManager(QObject): """ DEPRECATED. Will be replaced with BrushingModel, BrushingControler, BrushStroke. """ brushSizeChanged = pyqtSignal(int) brushColorChanged = pyqtSignal(QColor) minBrushSize = 1 maxBrushSize = 61 defaultBrushSize = 3 defaultDrawnNumber = 1 defaultColor = Qt.white erasingColor = Qt.black def __init__(self): QObject.__init__(self) self.shape = None self.bb = QRect() #bounding box enclosing the drawing self.brushSize = self.defaultBrushSize self.drawColor = self.defaultColor self.drawnNumber = self.defaultDrawnNumber self.penVis = QPen(self.drawColor, self.brushSize, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) self.penDraw = QPen(self.drawColor, self.brushSize, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) self.pos = None self.erasing = False #on which layer do we want to draw when self.drawingEnabled? self.drawOnto = None #an empty scene, where we add all drawn line segments #a QGraphicsLineItem, and which we can use to then #render to an image self.scene = QGraphicsScene() def growBoundingBox(self): self.bb.setLeft( max(0, self.bb.left()-self.brushSize-1)) self.bb.setTop( max(0, self.bb.top()-self.brushSize-1 )) self.bb.setRight( min(self.shape[0], self.bb.right()+self.brushSize+1)) self.bb.setBottom(min(self.shape[1], self.bb.bottom()+self.brushSize+1)) def toggleErase(self): self.erasing = not(self.erasing) def setErasing(self): self.erasing = True self.brushColorChanged.emit(self.erasingColor) def disableErasing(self): self.erasing = False self.brushColorChanged.emit(self.drawColor) def setBrushSize(self, size): self.brushSize = size self.penVis.setWidth(size) self.penDraw.setWidth(size) self.brushSizeChanged.emit(self.brushSize) def setDrawnNumber(self, num): self.drawnNumber = num self.drawnNumberChanged.emit(num) def getBrushSize(self): return self.brushSize def brushSmaller(self): b = self.brushSize if b > self.minBrushSize: self.setBrushSize(b-1) def brushBigger(self): b = self.brushSize if self.brushSize < self.maxBrushSize: self.setBrushSize(b+1) def setBrushColor(self, color): self.drawColor = color self.penVis.setColor(color) self.emit.brushColorChanged(self.drawColor) def beginDrawing(self, pos, shape): self.shape = shape self.bb = QRectF(0, 0, self.shape[0], self.shape[1]) self.scene.clear() if self.erasing: self.penVis.setColor(self.erasingColor) else: self.penVis.setColor(self.drawColor) self.pos = QPointF(pos.x()+0.0001, pos.y()+0.0001) line = self.moveTo(pos) return line def endDrawing(self, pos): self.moveTo(pos) self.growBoundingBox() tempi = QImage(QSize(self.bb.width(), self.bb.height()), QImage.Format_ARGB32_Premultiplied) #TODO: format tempi.fill(0) painter = QPainter(tempi) self.scene.render(painter, QRectF(QPointF(0,0), self.bb.size()), self.bb) return (self.bb.left(), self.bb.top(), tempi) #TODO: hackish, probably return a class ?? def dumpDraw(self, pos): res = self.endDrawing(pos) self.beginDrawing(pos, self.shape) return res def moveTo(self, pos): lineVis = QGraphicsLineItem(self.pos.x(), self.pos.y(), pos.x(), pos.y()) lineVis.setPen(self.penVis) line = QGraphicsLineItem(self.pos.x(), self.pos.y(), pos.x(), pos.y()) line.setPen(self.penDraw) self.scene.addItem(line) self.pos = pos x = pos.x() y = pos.y() #update bounding Box : if x > self.bb.right(): self.bb.setRight(x) if x < self.bb.left(): self.bb.setLeft(x) if y > self.bb.bottom(): self.bb.setBottom(y) if y < self.bb.top(): self.bb.setTop(y) return lineVis