def generateArrows(self): x, y = self.size().width() - 4, self.size().height() - 4 pen = QtGui.QPen(QtGui.QColor(QtCore.Qt.black)) brush = QtGui.QBrush(pen.color().darker(150)) points_list = [[ QtCore.QPoint(x / 2, 0), QtCore.QPoint(x / 2 + 5, 5), QtCore.QPoint(x / 2 - 5, 5) ], [ QtCore.QPoint(x / 2, y), QtCore.QPoint(x / 2 + 5, y - 5), QtCore.QPoint(x / 2 - 5, y - 5) ], [ QtCore.QPoint(0, y / 2), QtCore.QPoint(5, y / 2 + 5), QtCore.QPoint(5, y / 2 - 5) ], [ QtCore.QPoint(x, y / 2), QtCore.QPoint(x - 5, y / 2 + 5), QtCore.QPoint(x - 5, y / 2 - 5) ]] for points in points_list: poly = QtGui.QPolygonF(points) path = QPainterPath() path.addPolygon(poly) self.rArrow = self.scene().addPolygon(poly, pen) self.scene().addPath(path, pen, brush)
def _paintPolygon(self, painter, polygon): path = QPainterPath() for line in polygon: ring = QPolygonF() for point in line: cur = self.toCanvasCoordinates(point) - self.pos() ring.append(cur) ring.append(ring[0]) path.addPolygon(ring) painter.drawPath(path)
def generateArrows(self): x, y = self.size().width()-4, self.size().height()-4 pen = QtGui.QPen(QtGui.QColor(QtCore.Qt.black)) brush = QtGui.QBrush(pen.color().darker(150)) points_list = [[QtCore.QPoint(x/2, 0), QtCore.QPoint(x/2 + 5, 5), QtCore.QPoint(x/2 - 5, 5)], [QtCore.QPoint(x/2, y), QtCore.QPoint(x/2 + 5, y-5), QtCore.QPoint(x/2 - 5, y-5)], [QtCore.QPoint(0, y/2), QtCore.QPoint(5, y/2+5), QtCore.QPoint(5, y/2-5)], [QtCore.QPoint(x, y/2), QtCore.QPoint(x-5, y/2+5), QtCore.QPoint(x-5, y/2-5)]] for points in points_list: poly = QtGui.QPolygonF(points) path = QPainterPath() path.addPolygon(poly) self.rArrow = self.scene().addPolygon(poly, pen) self.scene().addPath(path, pen, brush)
def updateShape(self, point=None, pos=None): # Main arrow direction params = parameters.instance scale = self.scale ms = min(scale) head_size = params.arrow_head_size * ms width = params.arrow_line_size * ms self.prepareGeometryChange() if point == self.source: p1 = pos else: p1 = self.source.pos() self.setPos(p1) if point == self.target: p2 = pos else: p2 = self.target.pos() tip = p2 - p1 if abs(tip.x()) > 0.001 or abs(tip.y()) > 0.001: # Normalised #ntip = tip/stip ntip = tip # Arrow head base orth = QPointF(ntip.y(), -ntip.x()) * (head_size * 0.5) base_center = tip * (1 - 2 * head_size) base1 = base_center + orth base2 = base_center - orth base_center = tip * (1 - head_size * 1.50) else: ntip = tip base_center = tip base1 = tip base2 = tip self.tip = tip self.base_center = base_center self.base1 = base1 self.base2 = base2 path = QPainterPath() path.lineTo(base_center) path.addPolygon(QPolygonF([base_center, base1, tip, base2])) path.closeSubpath() self.rect = path.controlPointRect().adjusted(-width, -width, 2 * width, 2 * width) self.path = path self.update()
def arrow_path_concave(line, width): """ Return a :class:`QPainterPath` of a pretty looking arrow. """ path = QPainterPath() p1, p2 = line.p1(), line.p2() if p1 == p2: return path baseline = QLineF(line) # Require some minimum length. baseline.setLength(max(line.length() - width * 3, width * 3)) start, end = baseline.p1(), baseline.p2() mid = (start + end) / 2.0 normal = QLineF.fromPolar(1.0, baseline.angle() + 90).p2() path.moveTo(start) path.lineTo(start + (normal * width / 4.0)) path.quadTo(mid + (normal * width / 4.0), end + (normal * width / 1.5)) path.lineTo(end - (normal * width / 1.5)) path.quadTo(mid - (normal * width / 4.0), start - (normal * width / 4.0)) path.closeSubpath() arrow_head_len = width * 4 arrow_head_angle = 50 line_angle = line.angle() - 180 angle_1 = line_angle - arrow_head_angle / 2.0 angle_2 = line_angle + arrow_head_angle / 2.0 points = [p2, p2 + QLineF.fromPolar(arrow_head_len, angle_1).p2(), baseline.p2(), p2 + QLineF.fromPolar(arrow_head_len, angle_2).p2(), p2] poly = QPolygonF(points) path_head = QPainterPath() path_head.addPolygon(poly) path = path.united(path_head) return path
def updateShape(self, point=None, pos=None): # Main arrow direction params = parameters.instance scale = self.scale ms = min(scale) head_size = params.arrow_head_size*ms width = params.arrow_line_size*ms self.prepareGeometryChange() if point == self.source: p1 = pos else: p1 = self.source.pos() self.setPos(p1) if point == self.target: p2 = pos else: p2 = self.target.pos() tip = p2-p1 if abs(tip.x()) > 0.001 or abs(tip.y()) > 0.001: # Normalised #ntip = tip/stip ntip = tip # Arrow head base orth = QPointF(ntip.y(), -ntip.x())*(head_size*0.5) base_center = tip*(1-2*head_size) base1 = base_center + orth base2 = base_center - orth base_center = tip*(1-head_size*1.50) else: ntip = tip base_center = tip base1 = tip base2 = tip self.tip = tip self.base_center = base_center self.base1 = base1 self.base2 = base2 path = QPainterPath() path.lineTo(base_center) path.addPolygon(QPolygonF([base_center, base1, tip, base2])) path.closeSubpath() self.rect = path.controlPointRect().adjusted(-width, -width, 2*width, 2*width) self.path = path self.update()
def arrow_path_concave(line, width): """ Return a :class:`QPainterPath` of a pretty looking arrow. """ path = QPainterPath() p1, p2 = line.p1(), line.p2() if p1 == p2: return path baseline = QLineF(line) # Require some minimum length. baseline.setLength(max(line.length() - width * 3, width * 3)) start, end = baseline.p1(), baseline.p2() mid = (start + end) / 2.0 normal = QLineF.fromPolar(1.0, baseline.angle() + 90).p2() path.moveTo(start) path.lineTo(start + (normal * width / 4.0)) path.quadTo(mid + (normal * width / 4.0), end + (normal * width / 1.5)) path.lineTo(end - (normal * width / 1.5)) path.quadTo(mid - (normal * width / 4.0), start - (normal * width / 4.0)) path.closeSubpath() arrow_head_len = width * 4 arrow_head_angle = 50 line_angle = line.angle() - 180 angle_1 = line_angle - arrow_head_angle / 2.0 angle_2 = line_angle + arrow_head_angle / 2.0 points = [ p2, p2 + QLineF.fromPolar(arrow_head_len, angle_1).p2(), baseline.p2(), p2 + QLineF.fromPolar(arrow_head_len, angle_2).p2(), p2 ] poly = QPolygonF(points) path_head = QPainterPath() path_head.addPolygon(poly) path = path.united(path_head) return path
def arrow_path_plain(line, width): """ Return an :class:`QPainterPath` of a plain looking arrow. """ path = QPainterPath() p1, p2 = line.p1(), line.p2() if p1 == p2: return path baseline = QLineF(line) # Require some minimum length. baseline.setLength(max(line.length() - width * 3, width * 3)) path.moveTo(baseline.p1()) path.lineTo(baseline.p2()) stroker = QPainterPathStroker() stroker.setWidth(width) path = stroker.createStroke(path) arrow_head_len = width * 4 arrow_head_angle = 50 line_angle = line.angle() - 180 angle_1 = line_angle - arrow_head_angle / 2.0 angle_2 = line_angle + arrow_head_angle / 2.0 points = [ p2, p2 + QLineF.fromPolar(arrow_head_len, angle_1).p2(), p2 + QLineF.fromPolar(arrow_head_len, angle_2).p2(), p2, ] poly = QPolygonF(points) path_head = QPainterPath() path_head.addPolygon(poly) path = path.united(path_head) return path
def arrow_path_plain(line, width): """ Return an :class:`QPainterPath` of a plain looking arrow. """ path = QPainterPath() p1, p2 = line.p1(), line.p2() if p1 == p2: return path baseline = QLineF(line) # Require some minimum length. baseline.setLength(max(line.length() - width * 3, width * 3)) path.moveTo(baseline.p1()) path.lineTo(baseline.p2()) stroker = QPainterPathStroker() stroker.setWidth(width) path = stroker.createStroke(path) arrow_head_len = width * 4 arrow_head_angle = 50 line_angle = line.angle() - 180 angle_1 = line_angle - arrow_head_angle / 2.0 angle_2 = line_angle + arrow_head_angle / 2.0 points = [ p2, p2 + QLineF.fromPolar(arrow_head_len, angle_1).p2(), p2 + QLineF.fromPolar(arrow_head_len, angle_2).p2(), p2 ] poly = QPolygonF(points) path_head = QPainterPath() path_head.addPolygon(poly) path = path.united(path_head) return path
def rebuild( self ): """ Rebuilds the dependency path for this item. """ scene = self.scene() if ( not scene ): return sourcePos = self.sourceItem().viewItem().pos() sourceRect = self.sourceItem().viewItem().rect() targetPos = self.targetItem().viewItem().pos() targetRect = self.targetItem().viewItem().rect() cellWidth = scene.ganttWidget().cellWidth() startX = sourcePos.x() + sourceRect.width() - (cellWidth / 2.0) startY = sourcePos.y() + (sourceRect.height() / 2.0) endX = targetPos.x() - 2 endY = targetPos.y() + (targetRect.height() / 2.0) path = QPainterPath() path.moveTo(startX, startY) path.lineTo(startX, endY) path.lineTo(endX, endY) a = QPointF(endX - 10, endY - 3) b = QPointF(endX, endY) c = QPointF(endX - 10, endY + 3) self._polygon = QPolygonF([a, b, c, a]) path.addPolygon(self._polygon) self.setPath(path)
def shape(self): path = QPainterPath() path.addPolygon( EDraw.DefaultPolygon(self.boundingRect().width(), self.boundingRect().height())) return path
def _write(self, shapes, svg_file, width, height, stroke_colour = None, stroke_width = None, background_colour = None): svg_file.write('<?xml version="1.0" standalone="no"?>\n' '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"\n' ' "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n') svg_file.write('<svg width="%.6fcm" height="%.6fcm" ' % (width, height)) svg_file.write('viewBox="%.6f %.6f %.6f %.6f" ' 'xmlns="http://www.w3.org/2000/svg" ' 'version="1.1">\n' % (0.0, 0.0, width, height)) if stroke_width is None: stroke_width = "0.1%" if background_colour is not None: svg_file.write('<polygon fill="%s" ' % background_colour) svg_file.write('points="%.6f,%.6f %.6f,%.6f %.6f,%.6f %.6f,%.6f" />\n' % ( 0.0, 0.0, width, 0.0, width, height, 0.0, height)) path = QPainterPath() shapes.reverse() new_shapes = {} for points, polygon in shapes: rgb = self.parts.colours.get(polygon.colour, "#ffffff") new_points = [] for x, y in points: new_points.append(QPointF(x + width/2.0, height/2.0 - y)) new_polygon = QPolygonF(new_points) new_path = QPainterPath() new_path.addPolygon(new_polygon) if path.contains(new_path): continue inter = path.intersected(new_path) remaining = new_path.subtracted(inter) # Combine the new path with the accumulated path and simplify # the result. path = path.united(new_path) path = path.simplified() piece_dict = new_shapes.setdefault(polygon.piece, {}) colour_path = piece_dict.get(polygon.colour, QPainterPath()) piece_dict[polygon.colour] = colour_path.united(remaining) for piece, piece_dict in new_shapes.items(): svg_file.write('<g>\n') for colour, colour_path in piece_dict.items(): if colour_path.isEmpty(): continue rgb = self.parts.colours.get(colour, "#ffffff") shape = '<path style="fill:%s; opacity:%f" d="' % ( rgb, self._opacity_from_colour(colour)) i = 0 in_path = False while i < colour_path.elementCount(): p = colour_path.elementAt(i) if p.type == QPainterPath.MoveToElement: if in_path: shape += 'Z ' shape += 'M %.6f %.6f ' % (p.x, p.y) in_path = True elif p.type == QPainterPath.LineToElement: shape += 'L %.6f %.6f ' % (p.x, p.y) i += 1 if in_path: shape += 'Z' shape += '" />\n' svg_file.write(shape) svg_file.write('</g>\n') svg_file.write("</svg>\n") svg_file.close()
def setGeometry(self): """ Read the parameters that define the geometry of the point """ params = parameters.instance self.setEditable(params.is_cell_editable) points = self.points polygon_id = self.polygon_id polygon = [points[pt].pos() if pt in points else None for pt in polygon_id] non_none_cnt = len([p for p in polygon if p is not None]) if non_none_cnt == 0: self.rect = QRectF() self.bounding_rect = QRectF() self.setVisible(False) return self.setVisible(True) # First, check if this is a "simple" cell walls = self.walls real_polygon = [pid for pid in polygon_id if pid in points] #sides = [ walls[real_polygon[i], real_polygon[(i+1)%len(real_polygon)]] for i in range(len(real_polygon)) ] sides = [None] * len(polygon) self.sides = sides real_scale_x = self.scale[0]/self.glob_scale real_scale_y = self.scale[1]/self.glob_scale for i in range(len(polygon)): if polygon[i] is not None: # Find the next j = (i+1) % len(polygon) while polygon[j] is None: j = (j+1) % len(polygon) w = [QPointF(p.x()*real_scale_x, p.y()*real_scale_y) for p in walls[polygon_id[i], polygon_id[j]]] sides[i] = [polygon[i]] + w + [polygon[j]] prev = real_polygon[-1] polygon_shape = [] for i in range(len(polygon)): if polygon[i]: polygon_shape.append(polygon[i]) polygon_shape.extend(sides[i]) # Now add the dummy points .. starts at the first non-None point if non_none_cnt > 2: start = None for i in range(len(polygon)): if polygon[i] is not None: start = i break prev = start cur = start+1 if start+1 < len(polygon) else 0 log_debug("Polygon before: [%s]" % ",".join("(%f,%f)" % (p.x(), p.y()) if p is not None else "None" for p in polygon)) while cur != start: if polygon[cur] is None: cnt = 1 next = cur+1 if cur+1 < len(polygon) else 0 while True: if polygon[next] is None: cnt += 1 else: break next += 1 if next == len(polygon): next = 0 #print "%d points missing" % cnt # First, find total length of wall length = 0.0 side = sides[prev] for i in range(len(side)-1): length += dist(side[i], side[i+1]) diff = length/(cnt+1) # Distance between two points i = cur p = side[0] for j in range(cnt): l = 0.0 found = False for k in range(len(side)-1): dl = dist(side[k], side[k+1]) l += dl if l > diff*(1+1e-5): # Account for accumulation of small errors c = (i + j) % len(polygon) delta = diff-l+dl p = side[k] + (side[k+1]-side[k])*delta/dl s1 = side[:k+1] + [p] s2 = [p] + side[k+1:] sides[c-1] = s1 sides[c] = s2 side = s2 polygon[c] = p found = True break assert found, "Could not find point in polygon for position %d" % (j+i,) #p = p + diff #c = (i+j)%len(polygon) #polygon[c] = QPointF(p) cur = next else: prev = cur cur += 1 if cur >= len(polygon): cur = 0 assert None not in polygon, "Error, some dummy points were not added" else: polygon = [p for p in polygon if p is not None] center = sum(polygon, QPointF(0, 0)) / float(len(polygon)) self.center = center if len(polygon) > 2: polygon = QPolygonF(polygon+[polygon[0]]) polygon.translate(-center) polygon_shape = QPolygonF(polygon_shape + [polygon_shape[0]]) self.polygon_shape = polygon_shape polygon_shape.translate(-center) # Translate the sides too sides = [[p-center for p in s] for s in sides] self.sides = sides assert len(sides) == len(polygon)-1 elif len(polygon) == 2: polygon = QLineF(polygon[0], polygon[1]) polygon.translate(-center) else: polygon = None self.polygon = polygon self.setPos(center) params = parameters.instance self.prepareGeometryChange() size = params.cell_size scale = self.scale height = size*cos(pi/6)*scale[1] width = size*scale[0] pos_x = size*cos(pi/3)*scale[0] self.sel_rect = QRectF(-width, -height, 2*width, 2*height) if isinstance(polygon, QPolygonF): self.rect = self.polygon_shape.boundingRect() | self.sel_rect elif isinstance(polygon, QLineF): self.rect = QRectF(polygon.p1(), polygon.p2()).normalized() | self.sel_rect else: self.rect = self.sel_rect self.bounding_rect = QRectF(self.rect) if self.p1 in points and self.p2 in points: self.division_line = QLineF(points[self.p1].pos()-center, points[self.p2].pos()-center) else: self.division_line = None self.hexagon = QPolygonF([QPointF(-width, 0), QPointF(-pos_x, height), QPointF(pos_x, height), QPointF(width, 0), QPointF(pos_x, -height), QPointF(-pos_x, -height)]) self.hexagon_path = QPainterPath() self.hexagon_path.addPolygon(self.hexagon) s1 = QPainterPath() if isinstance(self.polygon, QPolygonF): s1.addPolygon(polygon_shape) elif isinstance(self.polygon, QLineF): s1.moveTo(self.polygon.p1()) s1.lineTo(self.polygon.p2()) stroke = QPainterPathStroker() sel_thick = 3*params.cell_thickness if sel_thick == 0: sel_thick = 3 stroke.setWidth(sel_thick) self.stroke = stroke.createStroke(s1)
def paintEvent(self, event): option = QStyleOption() option.initFrom(self) contents_rect = self.style().subElementRect(QStyle.SE_FrameContents, option, self) or self.contentsRect() # the SE_FrameContents rect is Null unless the stylesheet defines decorations if self.graphStyle == self.BarStyle: graph_width = self.__dict__['graph_width'] = int(ceil(float(contents_rect.width()) / self.horizontalPixelsPerUnit)) else: graph_width = self.__dict__['graph_width'] = int(ceil(float(contents_rect.width() - 1) / self.horizontalPixelsPerUnit) + 1) max_value = self.__dict__['max_value'] = max(chain([0], *(islice(reversed(graph.data), graph_width) for graph in self.graphs if graph.enabled))) if self.graphHeight == self.AutomaticHeight or self.graphHeight < 0: graph_height = self.__dict__['graph_height'] = max(self.scaler.get_height(max_value), self.minHeight) else: graph_height = self.__dict__['graph_height'] = max(self.graphHeight, self.minHeight) if self.graphStyle == self.BarStyle: height_scaling = float(contents_rect.height()) / graph_height else: height_scaling = float(contents_rect.height() - self.lineThickness) / graph_height painter = QStylePainter(self) painter.drawPrimitive(QStyle.PE_Widget, option) painter.setClipRect(contents_rect) painter.save() painter.translate(contents_rect.x() + contents_rect.width() - 1, contents_rect.y() + contents_rect.height() - 1) painter.scale(-1, -1) painter.setRenderHint(QStylePainter.Antialiasing, self.graphStyle != self.BarStyle) for graph in (graph for graph in self.graphs if graph.enabled and graph.data): if self.boundary is not None and 0 < self.boundary < graph_height: boundary_width = min(5.0/height_scaling, self.boundary-0, graph_height-self.boundary) pen_color = QLinearGradient(0, (self.boundary - boundary_width) * height_scaling, 0, (self.boundary + boundary_width) * height_scaling) pen_color.setColorAt(0, graph.color) pen_color.setColorAt(1, graph.over_boundary_color) brush_color = QLinearGradient(0, (self.boundary - boundary_width) * height_scaling, 0, (self.boundary + boundary_width) * height_scaling) brush_color.setColorAt(0, self.color_with_alpha(graph.color, self.fillTransparency)) brush_color.setColorAt(1, self.color_with_alpha(graph.over_boundary_color, self.fillTransparency)) else: pen_color = graph.color brush_color = self.color_with_alpha(graph.color, self.fillTransparency) dataset = islice(reversed(graph.data), graph_width) if self.graphStyle == self.BarStyle: lines = [QLine(x*self.horizontalPixelsPerUnit, 0, x*self.horizontalPixelsPerUnit, y*height_scaling) for x, y in enumerate(dataset)] painter.setPen(QPen(pen_color, self.lineThickness)) painter.drawLines(lines) else: painter.translate(0, +self.lineThickness/2 - 1) if self.smoothEnvelope and self.smoothFactor > 0: min_value = 0 max_value = graph_height * height_scaling cx_offset = self.horizontalPixelsPerUnit / 3.0 smoothness = self.smoothFactor last_values = deque(3*[dataset.next() * height_scaling], maxlen=3) # last 3 values: 0 last, 1 previous, 2 previous previous envelope = QPainterPath() envelope.moveTo(0, last_values[0]) for x, y in enumerate(dataset, 1): x = x * self.horizontalPixelsPerUnit y = y * height_scaling * (1 - smoothness) + last_values[0] * smoothness last_values.appendleft(y) c1x = x - cx_offset * 2 c2x = x - cx_offset c1y = limit((1 + smoothness) * last_values[1] - smoothness * last_values[2], min_value, max_value) # same gradient as previous previous value to previous value c2y = limit((1 - smoothness) * last_values[0] + smoothness * last_values[1], min_value, max_value) # same gradient as previous value to last value envelope.cubicTo(c1x, c1y, c2x, c2y, x, y) else: envelope = QPainterPath() envelope.addPolygon(QPolygonF([QPointF(x*self.horizontalPixelsPerUnit, y*height_scaling) for x, y in enumerate(dataset)])) if self.fillEnvelope or graph.fill_envelope: first_element = envelope.elementAt(0) last_element = envelope.elementAt(envelope.elementCount() - 1) fill_path = QPainterPath() fill_path.moveTo(last_element.x, last_element.y) fill_path.lineTo(last_element.x + 1, last_element.y) fill_path.lineTo(last_element.x + 1, -self.lineThickness) fill_path.lineTo(-self.lineThickness, -self.lineThickness) fill_path.lineTo(-self.lineThickness, first_element.y) fill_path.connectPath(envelope) painter.fillPath(fill_path, brush_color) painter.strokePath(envelope, QPen(pen_color, self.lineThickness, join=Qt.RoundJoin)) painter.translate(0, -self.lineThickness/2 + 1) if self.boundary is not None and self.boundaryColor: painter.setRenderHint(QStylePainter.Antialiasing, False) painter.setPen(QPen(self.boundaryColor, 1.0)) painter.drawLine(0, self.boundary*height_scaling, contents_rect.width(), self.boundary*height_scaling) painter.restore() # queue the 'updated' signal to be emited after returning to the main loop QMetaObject.invokeMethod(self, 'updated', Qt.QueuedConnection)
def shape(self): path = QPainterPath() path.addPolygon( EDraw.DefaultPolygon( self.boundingRect().width(), self.boundingRect().height() ) ) return path
class CellItem(QGraphicsItem): """ Class representing a cell on the screen. :IVariables: cell_id : int Identifier of the cell hover : bool True if the cell is being hovered over hover_contour : bool True if the contour of the cell is being hovered over hover_side : int|None Number of the side being hovered over sides : list of `QPainterPath` shape of each side polygon_id : list of int list of point ids for the polygon walls : `TimedWallShapes` Ref to the structure defining the shape of the walls at the current time points : dict of int * QPointF Ref to the structure holding the position of all the points at the current time current : bool True if the cell is the currently selected one drag_line : bool True if one of the side is being dragged p1 : int Start of a division line p2 : int End of a division line """ def __init__(self, scale, glob_scale, cell_id, polygon, points, walls, parent=None): QGraphicsItem.__init__(self, parent) self.cell_id = cell_id self.setZValue(2.5) self.setAcceptsHoverEvents(True) self.setSelectable(False) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) self.hover = False self.hover_contour = False self.hover_side = None self.scale = scale self.glob_scale = glob_scale self.setToolTip(unicode(cell_id)) self.polygon_id = polygon self.walls = walls self.sides = [] self.points = points self.current = False self.drag_line = False self.dragging_line = False self.editable = False self.p1 = None self.p2 = None self.setGeometry() def __del__(self): cleanQObject(self) def setCurrent(self, value=True): """ Set this cell as the current one (i.e. on top and different color) """ if value: self.setZValue(2.8) else: self.setZValue(2.5) self.current = value self.update() def setDivisionLine(self, p1, p2): """ Set the line from p1 to p2 to be a division line (i.e. different representation) """ self.p1 = p1 self.p2 = p2 self.setGeometry() def setGeometry(self): """ Read the parameters that define the geometry of the point """ params = parameters.instance self.setEditable(params.is_cell_editable) points = self.points polygon_id = self.polygon_id polygon = [points[pt].pos() if pt in points else None for pt in polygon_id] non_none_cnt = len([p for p in polygon if p is not None]) if non_none_cnt == 0: self.rect = QRectF() self.bounding_rect = QRectF() self.setVisible(False) return self.setVisible(True) # First, check if this is a "simple" cell walls = self.walls real_polygon = [pid for pid in polygon_id if pid in points] #sides = [ walls[real_polygon[i], real_polygon[(i+1)%len(real_polygon)]] for i in range(len(real_polygon)) ] sides = [None] * len(polygon) self.sides = sides real_scale_x = self.scale[0]/self.glob_scale real_scale_y = self.scale[1]/self.glob_scale for i in range(len(polygon)): if polygon[i] is not None: # Find the next j = (i+1) % len(polygon) while polygon[j] is None: j = (j+1) % len(polygon) w = [QPointF(p.x()*real_scale_x, p.y()*real_scale_y) for p in walls[polygon_id[i], polygon_id[j]]] sides[i] = [polygon[i]] + w + [polygon[j]] prev = real_polygon[-1] polygon_shape = [] for i in range(len(polygon)): if polygon[i]: polygon_shape.append(polygon[i]) polygon_shape.extend(sides[i]) # Now add the dummy points .. starts at the first non-None point if non_none_cnt > 2: start = None for i in range(len(polygon)): if polygon[i] is not None: start = i break prev = start cur = start+1 if start+1 < len(polygon) else 0 log_debug("Polygon before: [%s]" % ",".join("(%f,%f)" % (p.x(), p.y()) if p is not None else "None" for p in polygon)) while cur != start: if polygon[cur] is None: cnt = 1 next = cur+1 if cur+1 < len(polygon) else 0 while True: if polygon[next] is None: cnt += 1 else: break next += 1 if next == len(polygon): next = 0 #print "%d points missing" % cnt # First, find total length of wall length = 0.0 side = sides[prev] for i in range(len(side)-1): length += dist(side[i], side[i+1]) diff = length/(cnt+1) # Distance between two points i = cur p = side[0] for j in range(cnt): l = 0.0 found = False for k in range(len(side)-1): dl = dist(side[k], side[k+1]) l += dl if l > diff*(1+1e-5): # Account for accumulation of small errors c = (i + j) % len(polygon) delta = diff-l+dl p = side[k] + (side[k+1]-side[k])*delta/dl s1 = side[:k+1] + [p] s2 = [p] + side[k+1:] sides[c-1] = s1 sides[c] = s2 side = s2 polygon[c] = p found = True break assert found, "Could not find point in polygon for position %d" % (j+i,) #p = p + diff #c = (i+j)%len(polygon) #polygon[c] = QPointF(p) cur = next else: prev = cur cur += 1 if cur >= len(polygon): cur = 0 assert None not in polygon, "Error, some dummy points were not added" else: polygon = [p for p in polygon if p is not None] center = sum(polygon, QPointF(0, 0)) / float(len(polygon)) self.center = center if len(polygon) > 2: polygon = QPolygonF(polygon+[polygon[0]]) polygon.translate(-center) polygon_shape = QPolygonF(polygon_shape + [polygon_shape[0]]) self.polygon_shape = polygon_shape polygon_shape.translate(-center) # Translate the sides too sides = [[p-center for p in s] for s in sides] self.sides = sides assert len(sides) == len(polygon)-1 elif len(polygon) == 2: polygon = QLineF(polygon[0], polygon[1]) polygon.translate(-center) else: polygon = None self.polygon = polygon self.setPos(center) params = parameters.instance self.prepareGeometryChange() size = params.cell_size scale = self.scale height = size*cos(pi/6)*scale[1] width = size*scale[0] pos_x = size*cos(pi/3)*scale[0] self.sel_rect = QRectF(-width, -height, 2*width, 2*height) if isinstance(polygon, QPolygonF): self.rect = self.polygon_shape.boundingRect() | self.sel_rect elif isinstance(polygon, QLineF): self.rect = QRectF(polygon.p1(), polygon.p2()).normalized() | self.sel_rect else: self.rect = self.sel_rect self.bounding_rect = QRectF(self.rect) if self.p1 in points and self.p2 in points: self.division_line = QLineF(points[self.p1].pos()-center, points[self.p2].pos()-center) else: self.division_line = None self.hexagon = QPolygonF([QPointF(-width, 0), QPointF(-pos_x, height), QPointF(pos_x, height), QPointF(width, 0), QPointF(pos_x, -height), QPointF(-pos_x, -height)]) self.hexagon_path = QPainterPath() self.hexagon_path.addPolygon(self.hexagon) s1 = QPainterPath() if isinstance(self.polygon, QPolygonF): s1.addPolygon(polygon_shape) elif isinstance(self.polygon, QLineF): s1.moveTo(self.polygon.p1()) s1.lineTo(self.polygon.p2()) stroke = QPainterPathStroker() sel_thick = 3*params.cell_thickness if sel_thick == 0: sel_thick = 3 stroke.setWidth(sel_thick) self.stroke = stroke.createStroke(s1) def changePoints(self, polygon): self.polygon_id = polygon self.setGeometry() def paint(self, painter, option, widget): params = parameters.instance pen = QPen(QColor(Qt.black)) scale = self.scale ms = min(scale) pen.setWidth(params.cell_thickness) sel_thick = 2*params.cell_thickness if sel_thick == 0: sel_thick = 2*ms col = None if self.current: col = QColor(params.selected_cell_color) elif self.hover: col = QColor(params.cell_hover_color) else: col = QColor(params.cell_color) painter.setBrush(col) if self.hover_contour: pen1 = QPen(QColor(Qt.white)) pen1.setWidth(sel_thick) painter.setPen(pen1) else: painter.setPen(pen) if isinstance(self.polygon, QPolygonF): painter.drawPolygon(self.polygon_shape) elif isinstance(self.polygon, QLineF): painter.drawLine(self.polygon) pen.setWidth(0) painter.setPen(pen) painter.drawPolygon(self.hexagon) if self.hover_side is not None: pen = QPen(QColor(Qt.red)) pen.setWidth(sel_thick) side = self.sides[self.hover_side] painter.setPen(pen) pp = QPainterPath() pp.moveTo(side[0]) for p in side[1:]: pp.lineTo(p) painter.drawPath(pp) elif self.division_line is not None: pen = QPen(params.division_wall_color) pen.setWidth(sel_thick) painter.setPen(pen) painter.drawLine(self.division_line) elif self.dragging_line: pen = QPen(QColor(Qt.red)) pen.setWidth(sel_thick) painter.setPen(pen) polygon = self.polygon dg = self.drag_side p1 = polygon[dg] p2 = polygon[dg+1] painter.drawLine(p1, self.moving_point) painter.drawLine(self.moving_point, p2) def setEditable(self, value): self.editable = value def boundingRect(self): return self.bounding_rect def shape(self): """ Returns the shape used for selection """ s = QPainterPath() s.addPath(self.hexagon_path) if self.editable: s.addPath(self.stroke) return s def setSelectable(self, value=True): self.setFlag(QGraphicsItem.ItemIsSelectable, value) self.setFlag(QGraphicsItem.ItemIsMovable, True) def findClosestSide(self, pos): """ Find the side of the polygon closest to pos """ polygon = self.polygon min_dist = inf min_side = None if isinstance(polygon, QLineF): min_side = 0 min_dist = 0 else: #pos = pos + self.center sides = self.sides assert len(sides) == len(polygon)-1 for i in range(len(polygon)-1): side = sides[i] d = distToPolyLine(pos, side) if d < min_dist: min_dist = d min_side = i #print "Closest side found = %d, with distance = %f"%(min_side, min_dist) return min_side, min_dist def findHover(self, pos): """ Find which side is being hovered over """ if self.editable: if self.drag_line: self.hover = False self.hover_side = None self.hover_contour = False elif self.hexagon_path.contains(pos): self.hover = True self.hover_side = None self.hover_contour = False else: params = parameters.instance sel_thick = 3*params.cell_thickness if sel_thick == 0: sel_thick = 3 self.hover_contour = True self.hover = False polygon = self.polygon if isinstance(polygon, QPolygonF): min_side, min_dist = self.findClosestSide(pos) #print "Closest side = %d"%min_side if min_dist < sel_thick and min_side is not None: self.hover_side = min_side else: self.hover_side = None self.hover_contour = False self.hover = True else: self.hover = True self.hover_side = None self.hover_contour = False def hoverEnterEvent(self, event): self.findHover(event.pos()) self.update() def hoverMoveEvent(self, event): self.findHover(event.pos()) self.update() def hoverLeaveEvent(self, event): self.hover = False self.hover_contour = False self.hover_side = None self.update() def mousePressEvent(self, event): if self.hover_side is not None: moving_point = QPointF(event.pos()) self.drag_side = self.hover_side #self.polygon.insert(self.hover_side+1, moving_point) #self.sides[self.hover_side] = [self.polygon[self.hover_side], self.polygon[self.hover_side+1]] #self.sides.insert(self.hover_side+1, [self.polygon[self.hover_side+1], self.polygon[self.hover_side+2]]) self.moving_point = moving_point self.start_drag = event.screenPos() self.hover_contour = False self.hover_side = None self.drag_line = True self.dragging_line = False self.update() event.accept() else: event.ignore() QGraphicsItem.mousePressEvent(self, event) def mouseMoveEvent(self, event): if not self.dragging_line and self.drag_line and (self.start_drag - event.screenPos()).manhattanLength() > 5: self.dragging_line = True if self.dragging_line: params = parameters.instance ms = min(self.scale) sel_thick = 2*params.cell_thickness if sel_thick == 0: sel_thick = 2*ms self.prepareGeometryChange() mp = self.moving_point mp.setX(event.pos().x()) mp.setY(event.pos().y()) self.bounding_rect = self.rect | QRectF(mp.x()-sel_thick, mp.y()-sel_thick, 2*sel_thick, 2*sel_thick) self.update() def mouseReleaseEvent(self, event): if self.dragging_line: log_debug("Adding point to cell") self.setGeometry() drag_side = (self.drag_side+1) % (len(self.polygon)-1) self.scene().addPointToCell(self.cell_id, drag_side, self.mapToScene(event.pos())) else: event.ignore() QGraphicsItem.mouseReleaseEvent(self, event) self.drag_line = False self.dragging_line = False
ppL5 = QPainterPath() # Left 5' PainterPath ppR5 = QPainterPath() # Right 5' PainterPath ppL3 = QPainterPath() # Left 3' PainterPath ppR3 = QPainterPath() # Right 3' PainterPath # set up ppL5 (left 5' blue square) ppL5.addRect(0.25 * baseWidth, 0.125 * baseWidth,\ 0.75 * baseWidth, 0.75 * baseWidth) # set up ppR5 (right 5' blue square) ppR5.addRect(0, 0.125 * baseWidth,\ 0.75 * baseWidth, 0.75 * baseWidth) # set up ppL3 (left 3' blue triangle) l3poly = QPolygonF() l3poly.append(QPointF(baseWidth, 0)) l3poly.append(QPointF(0.25 * baseWidth, 0.5 * baseWidth)) l3poly.append(QPointF(baseWidth, baseWidth)) ppL3.addPolygon(l3poly) # set up ppR3 (right 3' blue triangle) r3poly = QPolygonF() r3poly.append(QPointF(0, 0)) r3poly.append(QPointF(0.75 * baseWidth, 0.5 * baseWidth)) r3poly.append(QPointF(0, baseWidth)) ppR3.addPolygon(r3poly) class PathHelix(QGraphicsItem): """ PathHelix is the primary "view" of the VirtualHelix data. It manages the ui interactions from the user, such as dragging breakpoints or crossovers addition/removal, and updates the data model accordingly.
class Arrow(QGraphicsObject, QGraphicsItem): def __init__(self, x_offset, y_offset, parent=None): super(Arrow, self).__init__(parent=parent) self._parent = parent self.color = BLACK self.path = QPainterPath() self.setAcceptDrops(True) self.setAcceptHoverEvents(True) self.x_offset = x_offset self.y_offset = y_offset # self.rect = QRectF(x_offset + ARROW_BASE_X, # y_offset - ARROW_HEIGHT, # ARROW_WIDTH, ARROW_HEIGHT) # self.path.addRect(self.rect) self.setup_display() def setup_display(self): polygon = QPolygonF( [QPointF(self.x_offset + ARROW_BASE_X, COMMIT_HEIGHT + ARROW_HEIGHT + self.y_offset), QPointF(self.x_offset + ARROW_BASE_X, COMMIT_HEIGHT + ARROW_HEIGHT + self.y_offset - 20), QPointF(self.x_offset + ARROW_BASE_X - ARROW_TIP_WIDTH / 2, COMMIT_HEIGHT + ARROW_HEIGHT + self.y_offset - 19), QPointF(self.x_offset + ARROW_BASE_X + ARROW_BASE_WIDTH / 2, COMMIT_HEIGHT + ARROW_HEIGHT + self.y_offset - ARROW_HEIGHT), QPointF(self.x_offset + ARROW_BASE_X + ARROW_BASE_WIDTH + ARROW_TIP_WIDTH / 2, COMMIT_HEIGHT + ARROW_HEIGHT + self.y_offset - 19), QPointF(self.x_offset + ARROW_BASE_X + ARROW_BASE_WIDTH, COMMIT_HEIGHT + ARROW_HEIGHT + self.y_offset - 20), QPointF(self.x_offset + ARROW_BASE_X + ARROW_BASE_WIDTH, COMMIT_HEIGHT + ARROW_HEIGHT + self.y_offset), ] ) self.path.addPolygon(polygon) # def hoverMoveEvent(self, event): # print "hover arrow move" # # def hoverEnterEvent(self, event): # print "hover arrow enter" # # def hoverLeaveEvent(self, event): # print "hover arrow leave" def dragEnterEvent(self, event): print "Enter" def dragLeaveEvent(self, event): print "Leave" def dragMoveEvent(self, event): print "Move" def dropEvent(self, event): # print dir(event.source()) # print dir(event) # print dir(event.mimeData()) # First string is commit hash, second is the branch self._parent.emit(SIGNAL("commitItemInserted(QString*, QString*)"), QString(event.mimeData().text()), self._parent.branch) print "Drop" def paint(self, painter, option, widget=None): painter.setPen(Qt.NoPen) painter.setBrush(QBrush(self.color)) painter.drawPath(self.path) def boundingRect(self): return self.path.boundingRect() def shape(self): return self.path
class CellItem(QGraphicsItem): """ Class representing a cell on the screen. :IVariables: cell_id : int Identifier of the cell hover : bool True if the cell is being hovered over hover_contour : bool True if the contour of the cell is being hovered over hover_side : int|None Number of the side being hovered over sides : list of `QPainterPath` shape of each side polygon_id : list of int list of point ids for the polygon walls : `TimedWallShapes` Ref to the structure defining the shape of the walls at the current time points : dict of int * QPointF Ref to the structure holding the position of all the points at the current time current : bool True if the cell is the currently selected one drag_line : bool True if one of the side is being dragged p1 : int Start of a division line p2 : int End of a division line """ def __init__(self, scale, glob_scale, cell_id, polygon, points, walls, parent=None): QGraphicsItem.__init__(self, parent) self.cell_id = cell_id self.setZValue(2.5) self.setAcceptsHoverEvents(True) self.setSelectable(False) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) self.hover = False self.hover_contour = False self.hover_side = None self.scale = scale self.glob_scale = glob_scale self.setToolTip(unicode(cell_id)) self.polygon_id = polygon self.walls = walls self.sides = [] self.points = points self.current = False self.drag_line = False self.dragging_line = False self.editable = False self.p1 = None self.p2 = None self.setGeometry() def __del__(self): cleanQObject(self) def setCurrent(self, value=True): """ Set this cell as the current one (i.e. on top and different color) """ if value: self.setZValue(2.8) else: self.setZValue(2.5) self.current = value self.update() def setDivisionLine(self, p1, p2): """ Set the line from p1 to p2 to be a division line (i.e. different representation) """ self.p1 = p1 self.p2 = p2 self.setGeometry() def setGeometry(self): """ Read the parameters that define the geometry of the point """ params = parameters.instance self.setEditable(params.is_cell_editable) points = self.points polygon_id = self.polygon_id polygon = [ points[pt].pos() if pt in points else None for pt in polygon_id ] non_none_cnt = len([p for p in polygon if p is not None]) if non_none_cnt == 0: self.rect = QRectF() self.bounding_rect = QRectF() self.setVisible(False) return self.setVisible(True) # First, check if this is a "simple" cell walls = self.walls real_polygon = [pid for pid in polygon_id if pid in points] #sides = [ walls[real_polygon[i], real_polygon[(i+1)%len(real_polygon)]] for i in range(len(real_polygon)) ] sides = [None] * len(polygon) self.sides = sides real_scale_x = self.scale[0] / self.glob_scale real_scale_y = self.scale[1] / self.glob_scale for i in range(len(polygon)): if polygon[i] is not None: # Find the next j = (i + 1) % len(polygon) while polygon[j] is None: j = (j + 1) % len(polygon) w = [ QPointF(p.x() * real_scale_x, p.y() * real_scale_y) for p in walls[polygon_id[i], polygon_id[j]] ] sides[i] = [polygon[i]] + w + [polygon[j]] prev = real_polygon[-1] polygon_shape = [] for i in range(len(polygon)): if polygon[i]: polygon_shape.append(polygon[i]) polygon_shape.extend(sides[i]) # Now add the dummy points .. starts at the first non-None point if non_none_cnt > 2: start = None for i in range(len(polygon)): if polygon[i] is not None: start = i break prev = start cur = start + 1 if start + 1 < len(polygon) else 0 log_debug("Polygon before: [%s]" % ",".join("(%f,%f)" % (p.x(), p.y()) if p is not None else "None" for p in polygon)) while cur != start: if polygon[cur] is None: cnt = 1 next = cur + 1 if cur + 1 < len(polygon) else 0 while True: if polygon[next] is None: cnt += 1 else: break next += 1 if next == len(polygon): next = 0 #print "%d points missing" % cnt # First, find total length of wall length = 0.0 side = sides[prev] for i in range(len(side) - 1): length += dist(side[i], side[i + 1]) diff = length / (cnt + 1) # Distance between two points i = cur p = side[0] for j in range(cnt): l = 0.0 found = False for k in range(len(side) - 1): dl = dist(side[k], side[k + 1]) l += dl if l > diff * ( 1 + 1e-5 ): # Account for accumulation of small errors c = (i + j) % len(polygon) delta = diff - l + dl p = side[k] + (side[k + 1] - side[k]) * delta / dl s1 = side[:k + 1] + [p] s2 = [p] + side[k + 1:] sides[c - 1] = s1 sides[c] = s2 side = s2 polygon[c] = p found = True break assert found, "Could not find point in polygon for position %d" % ( j + i, ) #p = p + diff #c = (i+j)%len(polygon) #polygon[c] = QPointF(p) cur = next else: prev = cur cur += 1 if cur >= len(polygon): cur = 0 assert None not in polygon, "Error, some dummy points were not added" else: polygon = [p for p in polygon if p is not None] center = sum(polygon, QPointF(0, 0)) / float(len(polygon)) self.center = center if len(polygon) > 2: polygon = QPolygonF(polygon + [polygon[0]]) polygon.translate(-center) polygon_shape = QPolygonF(polygon_shape + [polygon_shape[0]]) self.polygon_shape = polygon_shape polygon_shape.translate(-center) # Translate the sides too sides = [[p - center for p in s] for s in sides] self.sides = sides assert len(sides) == len(polygon) - 1 elif len(polygon) == 2: polygon = QLineF(polygon[0], polygon[1]) polygon.translate(-center) else: polygon = None self.polygon = polygon self.setPos(center) params = parameters.instance self.prepareGeometryChange() size = params.cell_size scale = self.scale height = size * cos(pi / 6) * scale[1] width = size * scale[0] pos_x = size * cos(pi / 3) * scale[0] self.sel_rect = QRectF(-width, -height, 2 * width, 2 * height) if isinstance(polygon, QPolygonF): self.rect = self.polygon_shape.boundingRect() | self.sel_rect elif isinstance(polygon, QLineF): self.rect = QRectF(polygon.p1(), polygon.p2()).normalized() | self.sel_rect else: self.rect = self.sel_rect self.bounding_rect = QRectF(self.rect) if self.p1 in points and self.p2 in points: self.division_line = QLineF(points[self.p1].pos() - center, points[self.p2].pos() - center) else: self.division_line = None self.hexagon = QPolygonF([ QPointF(-width, 0), QPointF(-pos_x, height), QPointF(pos_x, height), QPointF(width, 0), QPointF(pos_x, -height), QPointF(-pos_x, -height) ]) self.hexagon_path = QPainterPath() self.hexagon_path.addPolygon(self.hexagon) s1 = QPainterPath() if isinstance(self.polygon, QPolygonF): s1.addPolygon(polygon_shape) elif isinstance(self.polygon, QLineF): s1.moveTo(self.polygon.p1()) s1.lineTo(self.polygon.p2()) stroke = QPainterPathStroker() sel_thick = 3 * params.cell_thickness if sel_thick == 0: sel_thick = 3 stroke.setWidth(sel_thick) self.stroke = stroke.createStroke(s1) def changePoints(self, polygon): self.polygon_id = polygon self.setGeometry() def paint(self, painter, option, widget): params = parameters.instance pen = QPen(QColor(Qt.black)) scale = self.scale ms = min(scale) pen.setWidth(params.cell_thickness) sel_thick = 2 * params.cell_thickness if sel_thick == 0: sel_thick = 2 * ms col = None if self.current: col = QColor(params.selected_cell_color) elif self.hover: col = QColor(params.cell_hover_color) else: col = QColor(params.cell_color) painter.setBrush(col) if self.hover_contour: pen1 = QPen(QColor(Qt.white)) pen1.setWidth(sel_thick) painter.setPen(pen1) else: painter.setPen(pen) if isinstance(self.polygon, QPolygonF): painter.drawPolygon(self.polygon_shape) elif isinstance(self.polygon, QLineF): painter.drawLine(self.polygon) pen.setWidth(0) painter.setPen(pen) painter.drawPolygon(self.hexagon) if self.hover_side is not None: pen = QPen(QColor(Qt.red)) pen.setWidth(sel_thick) side = self.sides[self.hover_side] painter.setPen(pen) pp = QPainterPath() pp.moveTo(side[0]) for p in side[1:]: pp.lineTo(p) painter.drawPath(pp) elif self.division_line is not None: pen = QPen(params.division_wall_color) pen.setWidth(sel_thick) painter.setPen(pen) painter.drawLine(self.division_line) elif self.dragging_line: pen = QPen(QColor(Qt.red)) pen.setWidth(sel_thick) painter.setPen(pen) polygon = self.polygon dg = self.drag_side p1 = polygon[dg] p2 = polygon[dg + 1] painter.drawLine(p1, self.moving_point) painter.drawLine(self.moving_point, p2) def setEditable(self, value): self.editable = value def boundingRect(self): return self.bounding_rect def shape(self): """ Returns the shape used for selection """ s = QPainterPath() s.addPath(self.hexagon_path) if self.editable: s.addPath(self.stroke) return s def setSelectable(self, value=True): self.setFlag(QGraphicsItem.ItemIsSelectable, value) self.setFlag(QGraphicsItem.ItemIsMovable, True) def findClosestSide(self, pos): """ Find the side of the polygon closest to pos """ polygon = self.polygon min_dist = inf min_side = None if isinstance(polygon, QLineF): min_side = 0 min_dist = 0 else: #pos = pos + self.center sides = self.sides assert len(sides) == len(polygon) - 1 for i in range(len(polygon) - 1): side = sides[i] d = distToPolyLine(pos, side) if d < min_dist: min_dist = d min_side = i #print "Closest side found = %d, with distance = %f"%(min_side, min_dist) return min_side, min_dist def findHover(self, pos): """ Find which side is being hovered over """ if self.editable: if self.drag_line: self.hover = False self.hover_side = None self.hover_contour = False elif self.hexagon_path.contains(pos): self.hover = True self.hover_side = None self.hover_contour = False else: params = parameters.instance sel_thick = 3 * params.cell_thickness if sel_thick == 0: sel_thick = 3 self.hover_contour = True self.hover = False polygon = self.polygon if isinstance(polygon, QPolygonF): min_side, min_dist = self.findClosestSide(pos) #print "Closest side = %d"%min_side if min_dist < sel_thick and min_side is not None: self.hover_side = min_side else: self.hover_side = None self.hover_contour = False self.hover = True else: self.hover = True self.hover_side = None self.hover_contour = False def hoverEnterEvent(self, event): self.findHover(event.pos()) self.update() def hoverMoveEvent(self, event): self.findHover(event.pos()) self.update() def hoverLeaveEvent(self, event): self.hover = False self.hover_contour = False self.hover_side = None self.update() def mousePressEvent(self, event): if self.hover_side is not None: moving_point = QPointF(event.pos()) self.drag_side = self.hover_side #self.polygon.insert(self.hover_side+1, moving_point) #self.sides[self.hover_side] = [self.polygon[self.hover_side], self.polygon[self.hover_side+1]] #self.sides.insert(self.hover_side+1, [self.polygon[self.hover_side+1], self.polygon[self.hover_side+2]]) self.moving_point = moving_point self.start_drag = event.screenPos() self.hover_contour = False self.hover_side = None self.drag_line = True self.dragging_line = False self.update() event.accept() else: event.ignore() QGraphicsItem.mousePressEvent(self, event) def mouseMoveEvent(self, event): if not self.dragging_line and self.drag_line and ( self.start_drag - event.screenPos()).manhattanLength() > 5: self.dragging_line = True if self.dragging_line: params = parameters.instance ms = min(self.scale) sel_thick = 2 * params.cell_thickness if sel_thick == 0: sel_thick = 2 * ms self.prepareGeometryChange() mp = self.moving_point mp.setX(event.pos().x()) mp.setY(event.pos().y()) self.bounding_rect = self.rect | QRectF( mp.x() - sel_thick, mp.y() - sel_thick, 2 * sel_thick, 2 * sel_thick) self.update() def mouseReleaseEvent(self, event): if self.dragging_line: log_debug("Adding point to cell") self.setGeometry() drag_side = (self.drag_side + 1) % (len(self.polygon) - 1) self.scene().addPointToCell(self.cell_id, drag_side, self.mapToScene(event.pos())) else: event.ignore() QGraphicsItem.mouseReleaseEvent(self, event) self.drag_line = False self.dragging_line = False
def setGeometry(self): """ Read the parameters that define the geometry of the point """ params = parameters.instance self.setEditable(params.is_cell_editable) points = self.points polygon_id = self.polygon_id polygon = [ points[pt].pos() if pt in points else None for pt in polygon_id ] non_none_cnt = len([p for p in polygon if p is not None]) if non_none_cnt == 0: self.rect = QRectF() self.bounding_rect = QRectF() self.setVisible(False) return self.setVisible(True) # First, check if this is a "simple" cell walls = self.walls real_polygon = [pid for pid in polygon_id if pid in points] #sides = [ walls[real_polygon[i], real_polygon[(i+1)%len(real_polygon)]] for i in range(len(real_polygon)) ] sides = [None] * len(polygon) self.sides = sides real_scale_x = self.scale[0] / self.glob_scale real_scale_y = self.scale[1] / self.glob_scale for i in range(len(polygon)): if polygon[i] is not None: # Find the next j = (i + 1) % len(polygon) while polygon[j] is None: j = (j + 1) % len(polygon) w = [ QPointF(p.x() * real_scale_x, p.y() * real_scale_y) for p in walls[polygon_id[i], polygon_id[j]] ] sides[i] = [polygon[i]] + w + [polygon[j]] prev = real_polygon[-1] polygon_shape = [] for i in range(len(polygon)): if polygon[i]: polygon_shape.append(polygon[i]) polygon_shape.extend(sides[i]) # Now add the dummy points .. starts at the first non-None point if non_none_cnt > 2: start = None for i in range(len(polygon)): if polygon[i] is not None: start = i break prev = start cur = start + 1 if start + 1 < len(polygon) else 0 log_debug("Polygon before: [%s]" % ",".join("(%f,%f)" % (p.x(), p.y()) if p is not None else "None" for p in polygon)) while cur != start: if polygon[cur] is None: cnt = 1 next = cur + 1 if cur + 1 < len(polygon) else 0 while True: if polygon[next] is None: cnt += 1 else: break next += 1 if next == len(polygon): next = 0 #print "%d points missing" % cnt # First, find total length of wall length = 0.0 side = sides[prev] for i in range(len(side) - 1): length += dist(side[i], side[i + 1]) diff = length / (cnt + 1) # Distance between two points i = cur p = side[0] for j in range(cnt): l = 0.0 found = False for k in range(len(side) - 1): dl = dist(side[k], side[k + 1]) l += dl if l > diff * ( 1 + 1e-5 ): # Account for accumulation of small errors c = (i + j) % len(polygon) delta = diff - l + dl p = side[k] + (side[k + 1] - side[k]) * delta / dl s1 = side[:k + 1] + [p] s2 = [p] + side[k + 1:] sides[c - 1] = s1 sides[c] = s2 side = s2 polygon[c] = p found = True break assert found, "Could not find point in polygon for position %d" % ( j + i, ) #p = p + diff #c = (i+j)%len(polygon) #polygon[c] = QPointF(p) cur = next else: prev = cur cur += 1 if cur >= len(polygon): cur = 0 assert None not in polygon, "Error, some dummy points were not added" else: polygon = [p for p in polygon if p is not None] center = sum(polygon, QPointF(0, 0)) / float(len(polygon)) self.center = center if len(polygon) > 2: polygon = QPolygonF(polygon + [polygon[0]]) polygon.translate(-center) polygon_shape = QPolygonF(polygon_shape + [polygon_shape[0]]) self.polygon_shape = polygon_shape polygon_shape.translate(-center) # Translate the sides too sides = [[p - center for p in s] for s in sides] self.sides = sides assert len(sides) == len(polygon) - 1 elif len(polygon) == 2: polygon = QLineF(polygon[0], polygon[1]) polygon.translate(-center) else: polygon = None self.polygon = polygon self.setPos(center) params = parameters.instance self.prepareGeometryChange() size = params.cell_size scale = self.scale height = size * cos(pi / 6) * scale[1] width = size * scale[0] pos_x = size * cos(pi / 3) * scale[0] self.sel_rect = QRectF(-width, -height, 2 * width, 2 * height) if isinstance(polygon, QPolygonF): self.rect = self.polygon_shape.boundingRect() | self.sel_rect elif isinstance(polygon, QLineF): self.rect = QRectF(polygon.p1(), polygon.p2()).normalized() | self.sel_rect else: self.rect = self.sel_rect self.bounding_rect = QRectF(self.rect) if self.p1 in points and self.p2 in points: self.division_line = QLineF(points[self.p1].pos() - center, points[self.p2].pos() - center) else: self.division_line = None self.hexagon = QPolygonF([ QPointF(-width, 0), QPointF(-pos_x, height), QPointF(pos_x, height), QPointF(width, 0), QPointF(pos_x, -height), QPointF(-pos_x, -height) ]) self.hexagon_path = QPainterPath() self.hexagon_path.addPolygon(self.hexagon) s1 = QPainterPath() if isinstance(self.polygon, QPolygonF): s1.addPolygon(polygon_shape) elif isinstance(self.polygon, QLineF): s1.moveTo(self.polygon.p1()) s1.lineTo(self.polygon.p2()) stroke = QPainterPathStroker() sel_thick = 3 * params.cell_thickness if sel_thick == 0: sel_thick = 3 stroke.setWidth(sel_thick) self.stroke = stroke.createStroke(s1)