class CurveNodeItem(QGraphicsItem, object): curveUpdated = Signal() WIDTH = 10 def __init__(self, rect=None, parent=None): self.signals = CurveNodeItemSignals() gradient = QRadialGradient( self.WIDTH * 0.75, self.WIDTH * 0.75, self.WIDTH * 0.75, self.WIDTH * 0.75, self.WIDTH * 0.75) gradient.setColorAt(0, self.theme().accent_color_6 if self.theme() else QColor.fromRgbF(1, 0.5, 0.01, 1)) gradient.setColorAt(1, self.theme().accent_color_8 if self.theme() else QColor.fromRgbF(1, 0.6, 0.06, 1)) self._brush = QBrush(gradient) self._brush.setStyle(Qt.RadialGradientPattern) self._pen = QPen() self._pen.setStyle(Qt.SolidLine) self._pen.setWidth(2) self._pen.setColor(self.theme().accent_color if self.theme() else QColor(104, 104, 104, 255)) self._selected_pen = QPen() self._selected_pen.setStyle(Qt.SolidLine) self._selected_pen.setWidth(3) self._selected_pen.setColor(self.theme().accent_color_4 if self.theme() else QColor(67, 255, 163, 255)) super(CurveNodeItem, self).__init__(parent) self._lock_x_pos = False self._snap = False self._current_pos = None self._new_pos = None self._line = None self._is_point1 = False self.set_rect(rect if rect else QRect(0, 0, 10, 10)) self.setFlags( QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemSendsScenePositionChanges) @property def snap(self): return self._snap @snap.setter def snap(self, flag): self._snap = bool(flag) @property def lock_x_pos(self): return self._lock_x_pos @lock_x_pos.setter def lock_x_pos(self, value): self._lock_x_pos = value def boundingRect(self): return QRectF(0, 0, 20, 20) def paint(self, painter, option, widget): painter.setBrush(self._brush) painter.setPen(self._selected_pen if self.isSelected() else self._pen) painter.drawEllipse(self._rect) def mousePressEvent(self, event): self._current_pos = self.pos() super(CurveNodeItem, self).mousePressEvent(event) def mouseMoveEvent(self, event): super(CurveNodeItem, self).mouseMoveEvent(event) curve_offset = -self.WIDTH * 0.5 scale_x = min(max(event.scenePos().x(), curve_offset), self.scene().base_size + curve_offset) scale_y = min(max(event.scenePos().y(), curve_offset), self.scene().base_size + curve_offset) if self._lock_x_pos: scale_x = self._lock_x_pos if self._snap is not False: scale_x = round((float(scale_x) / self._snap)) * self._snap scale_y = round((float(scale_y) / self._snap)) * self._snap self._new_pos = QPointF(scale_x, scale_y) self.setPos(self._new_pos) self.scene().update_curve() self.signals.curveUpdated.emit() def mouseReleaseEvent(self, event): super(CurveNodeItem, self).mouseReleaseEvent(event) if not self._new_pos: return self.scene().undo_stack.push(CurveNodeMoveUndoCommand(self.scene(), self, self._current_pos, self._new_pos)) self._new_pos = None def itemChange(self, change, value): if change == self.ItemPositionChange and self.scene(): new_pos = value self._move_line_to_center(new_pos) return super(CurveNodeItem, self).itemChange(change, value) def set_rect(self, rect): self._rect = rect self.update() def add_line(self, line, is_point1): self._line = line self._is_point1 = is_point1 self._move_line_to_center(self.pos()) def _move_line_to_center(self, new_pos): if not self._line: return x_offset = self._rect.x() - self._rect.width() / 2 y_offset = self._rect.y() - self._rect.height() / 2 new_center_pos = QPointF(new_pos.x() - x_offset, new_pos.y() - y_offset) p1 = new_center_pos if self._is_point1 else self._line.line().p1() p2 = self._line.line().p2() if self._is_point1 else new_center_pos self._line.setLine(QLineF(p1, p2))
class GridScene(BaseScene, object): """ Scene with grid background drawing support """ def __init__(self, grid_main_spacing=8, grid_secondary_spacing=8, grid_main_pen_color=GridColors.BaseColor, grid_secondary_pen_color=GridColors.DarkerColor, grid_main_width=1, grid_secondary_width=1, grid_main_style=Qt.SolidLine, grid_secondary_style=Qt.SolidLine, draw_main_grid=True, draw_secondary_grid=True, fit_grid=False, fit_grid_main_divisions=10, fit_grid_secondary_divisions=4, parent=None): super(GridScene, self).__init__(parent=parent) self._grid_main_pen = QPen() self._grid_secondary_pen = QPen() self._grid_main_spacing = grid_main_spacing self._grid_secondary_spacing = grid_secondary_spacing self._grid_main_pen.setColor(grid_main_pen_color) self._grid_secondary_pen.setColor(grid_secondary_pen_color) self._grid_main_pen.setWidth(grid_main_width) self._grid_secondary_pen.setWidth(grid_secondary_width) self._grid_main_style = grid_main_style self._grid_secondary_style = grid_secondary_style self._draw_main_grid = draw_main_grid self._draw_secondary_grid = draw_secondary_grid self._fit_grid = fit_grid self._fit_grid_main_divisions = fit_grid_main_divisions self._fit_grid_secondary_divisions = fit_grid_secondary_divisions self._grid_main_pen.setStyle(self._grid_main_style) self._grid_secondary_pen.setStyle(self._grid_secondary_style) self._fit_grid_draw = False @decorators.accepts(int) def set_grid_main_spacing(self, value): """ Set the size of the main grid (define the gap between grid lines) """ self._grid_main_spacing = value @decorators.accepts(int) def set_grid_secondary_spacing(self, value): """ Set the size of the secondary grid """ self._grid_secondary_spacing = value @decorators.returns(int) def get_grid_main_spacing(self): """ Returns the size of the main grid :return: int, size of the main grid """ return self._grid_main_spacing @decorators.returns(int) def get_grid_secondary_spacing(self): """ Returns the size of the secondary grid :return: int, size of the secondary grid """ return self._grid_secondary_spacing @decorators.accepts(QColor) def set_main_pen_color(self, value): self._grid_main_pen.setColor(value) @decorators.returns(QColor) def get_main_pen_color(self): return self._grid_main_pen.color() @decorators.accepts(QColor) def set_secondary_pen_color(self, value): self._grid_secondary_pen.setColor(value) @decorators.returns(QColor) def get_secondary_pen_color(self): return self._grid_secondary_pen.color() @decorators.accepts(float) def set_grid_width(self, value): self._grid_main_pen.setWidth(value) self._grid_secondary_pen.setWidth(value) @decorators.returns(float) def get_grid_width(self): return self._grid_main_pen.width() @decorators.returns(bool) def get_draw_main_grid(self): return self._draw_main_grid @decorators.accepts(bool) def set_draw_main_grid(self, draw): self._draw_main_grid = draw @decorators.returns(bool) def get_draw_secondary_grid(self): return self._draw_secondary_grid @decorators.accepts(bool) def set_draw_secondary_grid(self, draw): self._draw_secondary_grid = draw @decorators.returns(bool) def get_fit_grid(self): return self._fit_grid @decorators.accepts(bool) def set_fit_grid(self, fit): self._fit_grid = fit grid_main_spacing = property(get_grid_main_spacing, set_grid_main_spacing) grid_secondary_spacing = property(get_grid_secondary_spacing, set_grid_secondary_spacing) grid_main_pen_color = property(get_main_pen_color, set_main_pen_color) grid_secondary_pen_color = property(get_secondary_pen_color, set_secondary_pen_color) grid_width = property(get_grid_width, set_grid_width) draw_main_grid = property(get_draw_main_grid, set_draw_main_grid) draw_secondary_grid = property(get_draw_secondary_grid, set_draw_secondary_grid) fit_grid = property(get_fit_grid, set_fit_grid) # region Override Functions def drawBackground(self, painter, rect): """ Draw grid background for the graph scene """ scene_rect = self.sceneRect() gradient = QColor(65, 65, 65) painter.fillRect(rect.intersected(scene_rect), QBrush(gradient)) painter.setPen(QPen()) painter.drawRect(scene_rect) if len(self.views()) <= 0: LOGGER.error('Scene has not view associated to it!') return if not self.views()[0]: LOGGER.error('View {0} is not valid!'.format(self.views()[0])) return if hasattr(self.views()[0], 'is_grid_visible'): if self.views()[0].is_grid_visible: return if self._fit_grid: if not self._fit_grid_draw: if self._draw_main_grid: pos = float(self.width() / self._fit_grid_main_divisions) for div in range(self._fit_grid_main_divisions): if div == 0: continue line = QGraphicsLineItem(0, pos * div, self.width(), pos * div) line.setZValue(-1) line.setPen(self._grid_main_pen) self.addItem(line) line = QGraphicsLineItem(pos * div, 0, pos * div, self.width()) line.setZValue(-1) line.setPen(self._grid_main_pen) self.addItem(line) rect = QGraphicsRectItem(self.sceneRect()) rect.setZValue(-1) self.addItem(rect) if self._draw_secondary_grid: pos = float(self.width() / self._fit_grid_secondary_divisions) for div in range(self._fit_grid_secondary_divisions): if div == 0: continue line = QGraphicsLineItem(0, pos * div, self.width(), pos * div) line.setZValue(-1) line.setPen(self._grid_secondary_pen) self.addItem(line) line = QGraphicsLineItem(pos * div, 0, pos * div, self.width()) line.setZValue(-1) line.setPen(self._grid_secondary_pen) self.addItem(line) rect = QGraphicsRectItem(self.sceneRect()) rect.setZValue(-1) self.addItem(rect) self._fit_grid_draw = True else: if self._draw_main_grid: left = int(self.sceneRect().left()) - (int(self.sceneRect().left()) % self._grid_main_spacing) top = int(self.sceneRect().top()) - (int(self.sceneRect().top()) % self._grid_main_spacing) painter.setPen(self._grid_main_pen) # draw grid vertical lines for x in range(left, int(self.sceneRect().right()), self.grid_main_spacing): painter.drawLine(x, self.sceneRect().top(), x, self.sceneRect().bottom()) # draw grid horizontal lines for y in range(top, int(self.sceneRect().bottom()), self.grid_main_spacing): painter.drawLine(self.sceneRect().left(), y, self.sceneRect().right(), y) if self._draw_secondary_grid: left = int(self.sceneRect().left()) - (int(self.sceneRect().left()) % self._grid_secondary_spacing) top = int(self.sceneRect().top()) - (int(self.sceneRect().top()) % self._grid_secondary_spacing) painter.setPen(self._grid_secondary_pen) # draw grid vertical lines for x in range(left, int(self.sceneRect().right()), self.grid_secondary_spacing * 10): painter.drawLine(x, self.sceneRect().top(), x, self.sceneRect().bottom()) # draw grid horizontal lines for y in range(top, int(self.sceneRect().bottom()), self.grid_secondary_spacing * 10): painter.drawLine(self.sceneRect().left(), y, self.sceneRect().right(), y)