def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget): if self.being_dragged(): # p = QtGui.QPen(ctrl.cm.ui_tr()) # p.setWidthF(0.5) # QPainter.setPen(p) pos = self.pos() sp, end_point = self.get_label_line_positions() ex, ey = utils.to_tuple(self.mapFromScene(pos)) epx, epy = utils.to_tuple(self.mapFromScene(end_point)) sx, sy = utils.to_tuple(self.mapFromScene(sp)) # for mx, my in self.magnet_positions(): # QPainter.drawLine(sx, sy, ex + mx, ey + my) mx, my = self.find_closest_magnet(pos, sp) p = QtGui.QPen(ctrl.cm.ui_tr()) p.setWidthF(0.5) QPainter.setPen(p) QPainter.drawLine(sx, sy, ex + mx, ey + my) QPainter.drawLine(sx, sy, epx, epy) p = QtGui.QPen(ctrl.cm.ui_tr()) p.setWidthF(2.0) QPainter.setPen(p) QPainter.drawEllipse(self.mapFromScene(end_point), 4, 4) if self.selected: p = QtGui.QPen(ctrl.cm.ui_tr()) p.setWidthF(0.5) QPainter.setPen(p) QPainter.drawRect(self.boundingRect()) QtWidgets.QGraphicsTextItem.paint(self, QPainter, QStyleOptionGraphicsItem, QWidget)
def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget): if self.being_dragged(): # p = QtGui.QPen(ctrl.cm.ui_tr()) # p.setWidthF(0.5) # QPainter.setPen(p) pos = self.pos() sp, end_point = self.get_label_line_positions() ex, ey = utils.to_tuple(self.mapFromScene(pos)) epx, epy = utils.to_tuple(self.mapFromScene(end_point)) sx, sy = utils.to_tuple(self.mapFromScene(sp)) # for mx, my in self.magnet_positions(): # QPainter.drawLine(sx, sy, ex + mx, ey + my) mx, my = self.find_closest_magnet(pos, sp) p = QtGui.QPen(ctrl.cm.ui_tr()) p.setWidthF(0.5) QPainter.setPen(p) QPainter.drawLine(sx, sy, ex + mx, ey + my) QPainter.drawLine(sx, sy, epx, epy) p = QtGui.QPen(ctrl.cm.ui_tr()) p.setWidthF(2.0) QPainter.setPen(p) QPainter.drawEllipse(self.mapFromScene(end_point), 4, 4) if self.selected: p = QtGui.QPen(ctrl.cm.ui_tr()) p.setWidthF(0.5) QPainter.setPen(p) QPainter.drawRect(self.boundingRect()) self.setDefaultTextColor(self.parentItem().color) QtWidgets.QGraphicsTextItem.paint(self, QPainter, QStyleOptionGraphicsItem, QWidget)
def move_selection(self, direction, add_to_selection=False): """ Move selection to best candidate :param direction: """ def edge_of_set(my_selectables): if direction == 'left': sortable = [(po[0], po[1], it) for it, po in my_selectables] x, y, item = min(sortable) elif direction == 'right': sortable = [(po[0], po[1], it) for it, po in my_selectables] x, y, item = max(sortable) elif direction == 'up': sortable = [(po[1], po[0], it) for it, po in my_selectables] y, x, item = min(sortable) elif direction == 'down': sortable = [(po[1], po[0], it) for it, po in my_selectables] y, x, item = max(sortable) else: raise KeyError return item # debugging plotter # for item, pos in selectables: # x,y = pos # el = QtGui.QGraphicsEllipseItem(x-2, y-2, 4, 4) # el.setBrush(colors.drawing) # self.addItem(el) # # ############### Absolute left/right/up/down ############################### # if nothing is selected, select the edgemost item from given direction if not ctrl.selected: selectables = [(item, to_tuple(item.sceneBoundingRect().center())) for item in self.items() if hasattr(item, 'select') and item.is_visible()] best = edge_of_set(selectables) ctrl.select(best) return best # ################ Relative left/right/up/down ############################# else: if len(ctrl.selected) == 1: current = ctrl.get_single_selected() else: # when there are many selected items, extend it to given direction, from the # edgemost item in that direction. # this behavior may give odd results, but there may be no intuitive ways how such # a blob of selections should behave. selectables = [(item, to_tuple(item.sceneBoundingRect().center())) for item in ctrl.selected if item.is_visible()] current = edge_of_set(selectables) best = None if isinstance(current, Node): best = self.next_selectable_from_node(current, direction) elif isinstance(current, Edge): best = self.next_selectable_from_edge(current, direction) if best: if add_to_selection: ctrl.add_to_selection(best) else: ctrl.select(best) return best
def update_end_points(self, end_point=None): """ :param end_point: End point can be given or it can be calculated. """ e = self.host shape_name = ctrl.settings.get_edge_setting('shape_name', edge=e) self._fill_path = e.is_filled() sx, sy = to_tuple(e.get_point_at(0.5)) self.start_point = sx, sy if end_point: self.end_point = end_point else: d = e.get_angle_at(0.5) d += 90 # 75 angle = math.radians(-d) dx = math.cos(angle) dy = math.sin(angle) l = 12 x = sx + dx * l y = sy + dy * l self.end_point = x, y self.setPos(self.end_point[0], self.end_point[1]) rel_sp = sub_xy(self.start_point, self.end_point) adjust = [] self._path = SHAPE_PRESETS[shape_name].path(rel_sp, (0, 0), alignment=g.RIGHT, curve_adjustment=adjust)[0]
def mouseReleaseEvent(self, event): """ Either we are finishing dragging or clicking the node. If clicking a node with editable label, the click has to be replayed to Label (QGraphicsTextItem) when it has toggled the edit mode on, to let its inaccessible method for positioning cursor on click position to do its work. :param event: :return: """ replay_click = False shift = event.modifiers() == QtCore.Qt.ShiftModifier if ctrl.pressed is self: ctrl.release(self) if ctrl.dragged_set: x, y = to_tuple(event.scenePos()) self.drop_to(int(x), int(y), recipient=ctrl.drag_hovering_on, shift_down=shift) ctrl.graph_scene.kill_dragging() ctrl.ui.update_selections() # drag operation may have changed visible affordances else: # This is a regular click on 'pressed' object self.select(adding=shift, select_area=False) if self.label_object.is_quick_editing(): replay_click = True self.update() Movable.mouseReleaseEvent(self, event) if replay_click and False: ctrl.graph_view.replay_mouse_press() self.label_object.mouseReleaseEvent(event) ctrl.release(self)
def drag(self, event): self._dragging = True ep = to_tuple(event.scenePos()) self.end_point = ep self.start_point = ep self.setPos(ep[0], ep[1]) self._path = None
def mousePressEvent(self, event): """ :param event: """ lp = event.localPos() x, y = to_tuple(lp) f_x, f_y, f_w, f_h = self._flag_area if f_x <= x <= f_x + f_w and f_y <= y <= f_y + f_h: self._pressed = FLAG return found = -1 for i, rect in enumerate(self.clickable_areas): if rect.contains(x, y): found = i if self.outer.all_colors[i] == self.outer.selected_role: break if found >= 0: self._pressed = self.outer.all_colors[found] if self.outer.selected_role != self._pressed: if self._pressed in self.outer.editable_colors: self.outer.set_color_role(self._pressed, update_selector=True) else: if self._pressed.startswith('content'): self.outer.set_color_role('content1', update_selector=True) elif self._pressed.startswith('background'): self.outer.set_color_role('background1', update_selector=True) self._pressed = None # prevent dragging weirdness
def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget): if self.being_dragged(): sp, end_point = self.get_label_line_positions() ex, ey = utils.to_tuple(self.mapFromScene(end_point)) sx, sy = utils.to_tuple(self.mapFromScene(sp)) p = QtGui.QPen(ctrl.cm.ui_tr()) p.setWidthF(0.5) QPainter.setPen(p) QPainter.drawLine(sx, sy, ex, ey) p = QtGui.QPen(ctrl.cm.ui_tr()) p.setWidthF(2.0) QPainter.setPen(p) QPainter.drawEllipse(self.mapFromScene(end_point), 4, 4) if self.selected: p = QtGui.QPen(ctrl.cm.ui_tr()) p.setWidthF(0.5) QPainter.setPen(p) QPainter.drawRect(self.boundingRect()) QtWidgets.QGraphicsTextItem.paint(self, QPainter, QStyleOptionGraphicsItem, QWidget)
def mouseMoveEvent(self, e): # mouseMoveEvents only happen between mousePressEvents and mouseReleaseEvents scene_pos_pf = e.scenePos() if ctrl.dragged_focus is self: self.drag(e) ctrl.graph_scene.dragging_over(scene_pos_pf) elif (e.buttonDownScenePos(QtCore.Qt.LeftButton) - scene_pos_pf).manhattanLength() > 6: scene_pos = to_tuple(scene_pos_pf) self.start_dragging(scene_pos) self.drag(e) ctrl.graph_scene.dragging_over(scene_pos_pf) Movable.mouseMoveEvent(self, e)
def mouseReleaseEvent(self, event): if ctrl.pressed is self: ctrl.release(self) if self.being_dragged: x, y = to_tuple(event.scenePos()) self.drop_to(x, y, recipient=ctrl.drag_hovering_on) self.being_dragged = False ctrl.graph_scene.kill_dragging() ctrl.ui.update_selections() # drag operation may have changed visible affordances ctrl.main.action_finished() # @UndefinedVariable return None # this mouseRelease is now consumed super().mouseReleaseEvent(event)
def mouseReleaseEvent(self, event): if ctrl.pressed is self: ctrl.release(self) if self.being_dragged: x, y = to_tuple(event.scenePos()) self.drop_to(x, y, recipient=ctrl.drag_hovering_on) self.being_dragged = False ctrl.graph_scene.kill_dragging() ctrl.ui.update_selections( ) # drag operation may have changed visible affordances ctrl.main.action_finished() # @UndefinedVariable return None # this mouseRelease is now consumed super().mouseReleaseEvent(event)
def mouseMoveEvent(self, event): """ :param event: :return: """ def get_value_from_flag_position(y): dv = (self._lum_box_y + self._lum_box_height - y) / self._lum_box_height if dv < 0: dv = 0 if dv > 1: dv = 1 return dv def get_color_from_position(x, y): dx = self._origin_x - x dy = self._origin_y - y hyp = math.sqrt(dx * dx + dy * dy) if self._radius == 0: s = 1.0 else: s = hyp / self._radius if s > 1: s = 1.0 h = (math.atan2(dy, dx) + math.pi) / (math.pi * 2) return h, s h, s, v = self.outer.selected_hsv if self._pressed == FLAG: x, y = to_tuple(event.localPos()) v = get_value_from_flag_position(y) self.outer.send_color(h, s, v) elif self._pressed: x, y = to_tuple(event.localPos()) h, s = get_color_from_position(x, y) self.outer.send_color(h, s, v) QtWidgets.QWidget.mouseMoveEvent(self, event)
def paint(self, painter, option, widget=None): """ :param painter: :param option: :param widget: :return: """ c = self.contextual_color() sx, sy = self.start_point ex, ey = self.end_point if self.path.use_simple_path: p = QtGui.QPen() p.setColor(c) painter.setPen(p) painter.drawPath(self.path.true_path) else: dpath = self.path.draw_path if self.has_outline(): thickness = self.settings.get_shape('thickness') p = QtGui.QPen() p.setColor(c) p.setCapStyle(QtCore.Qt.RoundCap) p.setWidthF(thickness) painter.setPen(p) painter.drawPath(dpath) if self.is_filled(): painter.fillPath(dpath, c) if self.path.arrowhead_start_path: painter.fillPath(self.path.arrowhead_start_path, c) if self.path.arrowhead_end_path: painter.fillPath(self.path.arrowhead_end_path, c) if self.selected and not ctrl.multiple_selection(): p = QtGui.QPen(ctrl.cm.ui_tr()) self.path.draw_control_point_hints(painter, p, self.curve_adjustment) if self.crossed_out_flag: cx, cy = to_tuple(self._true_path.pointAtPercent(0.5)) p = QtGui.QPen(ctrl.cm.ui()) p.setWidthF(1.0) painter.setPen(p) painter.drawLine(QtCore.QLineF(cx - 20, cy - 10, cx + 20, cy + 10)) painter.drawLine(QtCore.QLineF(cx - 20, cy + 10, cx + 20, cy - 10))
def drag(self, event): """ Drag also elements that are counted to be involved: features, children etc. Drag is called to only one principal drag host element. 'dragged_to' is called for each element. :param event: """ crossed_out_flag = event.modifiers() == QtCore.Qt.ShiftModifier for edge in self.edges_up: edge.crossed_out_flag = crossed_out_flag scene_pos = to_tuple(event.scenePos()) nx, ny = scene_pos # Call dragged_to -method for all nodes that are dragged with the drag focus # Their positions are relative to this focus, compute how much. for node in ctrl.dragged_set: d = node.drag_data dx, dy = d.distance_from_pointer node.dragged_to((int(nx + dx), int(ny + dy))) ctrl.ui.show_drag_adjustment() for group in ctrl.dragged_groups: group.update_shape()
def update_end_points(self, end_point=None): """ :param end_point: End point can be given or it can be calculated. """ e = self.host sx, sy = to_tuple(e.path.get_point_at(0.4)) self.start_point = sx, sy if end_point: self.end_point = end_point else: d = e.path.get_angle_at(0.4) d -= 60 # 75 angle = math.radians(-d) dx = math.cos(angle) dy = math.sin(angle) l = 12 x = sx + dx * l y = sy + dy * l self.end_point = x, y self.setPos(self.end_point[0], self.end_point[1])
def update_end_points(self, end_point=None): """ :param end_point: End point can be given or it can be calculated. """ e = self.host sx, sy = to_tuple(e.get_point_at(0.4)) self.start_point = sx, sy if end_point: self.end_point = end_point else: d = e.get_angle_at(0.4) d += 60 # 75 angle = math.radians(-d) dx = math.cos(angle) dy = math.sin(angle) l = 12 x = sx + dx * l y = sy + dy * l self.end_point = x, y self.setPos(self.end_point[0], self.end_point[1])
def _compute_adjust_from_pos(self, scene_pos): x, y = to_tuple(scene_pos) assert (self._index != -1) cx, cy = self.host.control_points[self._index] x_adjust = int(x - cx) y_adjust = int(y - cy) if self._index == 0: sx, sy = self.host.start_point else: sx, sy = self.host.end_point sx_to_cx = cx - sx sy_to_cy = cy - sy line_rad = math.atan2(sy_to_cy, sx_to_cx) line_dist = math.hypot(sx_to_cx, sy_to_cy) adj_rad = math.atan2(y_adjust, x_adjust) adj_dist = math.hypot(x_adjust, y_adjust) if line_dist != 0: relative_dist = adj_dist / line_dist else: relative_dist = adj_dist relative_rad = adj_rad - line_rad return relative_dist, relative_rad
def _compute_adjust_from_pos(self, scene_pos): x, y = to_tuple(scene_pos) assert (self._index != -1) cx, cy = self.host.path.control_points[self._index] x_adjust = int(x - cx) y_adjust = int(y - cy) if self._index == 0: sx, sy = self.host.start_point else: sx, sy = self.host.end_point sx_to_cx = cx - sx sy_to_cy = cy - sy line_rad = math.atan2(sy_to_cy, sx_to_cx) line_dist = math.hypot(sx_to_cx, sy_to_cy) adj_rad = math.atan2(y_adjust, x_adjust) adj_dist = math.hypot(x_adjust, y_adjust) if line_dist != 0: relative_dist = adj_dist / line_dist else: relative_dist = adj_dist relative_rad = adj_rad - line_rad return relative_dist, relative_rad
def draw_feature_shape(painter, rect, left, right, color): def triangle(path, x, y, mid, dx=1, dy=1): path.lineTo(x, y + mid - 4 * dy) path.lineTo(x - 4 * dx, y + mid) path.lineTo(x, y + mid + 4 * dy) path.lineTo(x, y + mid + mid * dy) def square(path, x, y, mid, dx=1, dy=1): path.lineTo(x, y + mid - 4 * dy) path.lineTo(x - 4 * dx, y + mid - 4 * dy) path.lineTo(x - 4 * dx, y + mid + 4 * dy) path.lineTo(x, y + mid + 4 * dy) path.lineTo(x, y + mid + mid * dy) def roundish(path, x, y, mid, dx=1, dy=1): path.lineTo(x, y + mid - 2 * dy) path.cubicTo(x - 3 * dx, y + mid - 2 * dy, x - 3 * dx, y + mid - 6 * dy, x - 6 * dx, y + mid) path.cubicTo(x - 3 * dx, y + mid + 6 * dy, x - 3 * dx, y + mid + 2 * dy, x, y + mid + 2 * dy) path.lineTo(x, y + mid + mid * dy) old_pen = painter.pen() painter.setPen(QtCore.Qt.NoPen) if left or right: # square, triangular or round knob base_shape = rect.adjusted(4, 0, -4, 0) if not right: base_shape.adjust(0, 0, -4, 0) path = QtGui.QPainterPath(base_shape.topLeft()) path.lineTo(base_shape.topRight()) mid = base_shape.height() / 2 x, y = to_tuple(base_shape.topRight()) if right == 2: # triangle plug triangle(path, x, y, mid, -1, 1) elif right == -2: # triangle hole triangle(path, x, y, mid, 1, 1) elif right == 3: # square plug square(path, x, y, mid, -1, 1) elif right == -3: # square hole square(path, x, y, mid, 1, 1) elif right == 4: # roundish plug roundish(path, x, y, mid, -1, 1) elif right == -4: # roundish hole roundish(path, x, y, mid, 1, 1) else: path.quadTo(x + 8, y + mid, x, y + mid + mid) path.lineTo(base_shape.bottomLeft()) x, y = to_tuple(base_shape.topLeft()) if left == 2: # triangle plug triangle(path, x, y, mid, 1, -1) elif left == -2: # triangle hole triangle(path, x, y, mid, -1, -1) elif left == 3: # square plug square(path, x, y, mid, 1, -1) elif left == -3: # square hole square(path, x, y, mid, -1, -1) elif left == 4: # roundish plug roundish(path, x, y, mid, 1, -1) elif left == -4: # roundish hole roundish(path, x, y, mid, -1, -1) else: path.quadTo(x - 8, y + mid, x, y) painter.fillPath(path, color) painter.drawPath(path) else: # solid rect painter.drawRect(rect) painter.setPen(old_pen)
def drag(self, event): self._dragging = True self.update_end_points(end_point=to_tuple(event.scenePos()))
def _simplify(data): """ Goes through common iterable datatypes and if common Qt types are found, replaces them with basic python tuples. If object is one of Kataja's own data classes, then save its uid """ if isinstance(data, (int, float, str)): return data elif isinstance(data, ITextNode): r = data.as_latex() if r: return 'INode', r else: return '' elif isinstance(data, dict): result = {} for k, value in data.items(): value = _simplify(value) result[k] = value return result elif isinstance(data, list): result = [] for o in data: result.append(_simplify(o)) return result elif isinstance(data, tuple): result = [] for o in data: result.append(_simplify(o)) result = tuple(result) return result elif isinstance(data, set): result = set() for o in data: result.add(_simplify(o)) return result elif isinstance(data, types.FunctionType): # if functions are stored in the dict, there should be some # original # version of the same dict, where these # are in their original form. raise SaveError('trying to save a function at object ', self) elif data is None: return data elif isinstance(data, QPointF): return 'QPointF', to_tuple(QPointF) elif isinstance(data, QPoint): return 'QPoint', to_tuple(QPoint) elif isinstance(data, QtGui.QColor): return 'QColor', data.red(), data.green(), data.blue(), \ data.alpha() elif isinstance(data, QtGui.QPen): pass elif isinstance(data, QtCore.QRectF): return 'QRectF', data.x(), data.y(), data.width(), data.height( ) elif isinstance(data, QtCore.QRect): return 'QRect', data.x(), data.y(), data.width(), data.height() elif isinstance(data, QtGui.QFont): raise SaveError("We shouldn't save QFonts!: ", data) elif hasattr(data, 'uid'): k = getattr(data, 'uid') if k not in saved_objs and k not in open_refs: # print('in %s adding open reference %s' % ( # self.uid, k)) open_refs[k] = data return '|'.join(('*r*', str(k))) else: raise SaveError("simplifying unknown data type:", data, type(data))
def _compute_adjust(self): x, y = to_tuple(self.pos()) assert (self._index != -1) p = self.host.control_points[self._index] return int(x - p[0]), int(y - p[1])
def _compute_adjust(self): x, y = to_tuple(self.pos()) assert (self._index != -1) p = self.host.path.control_points[self._index] return int(x - p[0]), int(y - p[1])
def _simplify(data): """ Goes through common iterable datatypes and if common Qt types are found, replaces them with basic python tuples. If object is one of Kataja's own data classes, then save its uid """ if isinstance(data, (int, float, str)): return data elif isinstance(data, ITextNode): r = data.as_latex() if r: return 'INode', r else: return '' elif isinstance(data, dict): result = {} for k, value in data.items(): value = _simplify(value) result[k] = value return result elif isinstance(data, list): result = [] for o in data: result.append(_simplify(o)) return result elif isinstance(data, tuple): result = [] for o in data: result.append(_simplify(o)) result = tuple(result) return result elif isinstance(data, set): #log.warn(f'attempting to simplify a set -- sets are not compatible with JSON: {data}') if ALLOW_SETS: result = set() for o in data: result.add(_simplify(o)) else: result = [] for o in data: result.append(_simplify(o)) return result elif isinstance(data, types.FunctionType): # if functions are stored in the dict, there should be some # original # version of the same dict, where these # are in their original form. raise SaveError('trying to save a function at object ', self) elif data is None: return data elif isinstance(data, QPointF): return 'QPointF', to_tuple(QPointF) elif isinstance(data, QPoint): return 'QPoint', to_tuple(QPoint) elif isinstance(data, QtGui.QColor): return 'QColor', data.red(), data.green(), data.blue(), data.alpha() elif isinstance(data, QtGui.QPen): pass elif isinstance(data, QtCore.QRectF): return 'QRectF', data.x(), data.y(), data.width(), data.height() elif isinstance(data, QtCore.QRect): return 'QRect', data.x(), data.y(), data.width(), data.height() elif isinstance(data, QtGui.QFont): raise SaveError("We shouldn't save QFonts!: ", data) elif hasattr(data, 'uid'): k = getattr(data, 'uid') if k not in saved_objs and k not in open_refs: # print('in %s adding open reference %s' % ( # self.uid, k)) open_refs[k] = data return '|'.join(('*r*', str(k))) else: raise SaveError("simplifying unknown data type:", data, type(data))