class ColorGraphicsScene(QGraphicsScene): """ The widget that is responsible for displaying the color gradient to the user """ def __init__(self, parent=None): super(ColorGraphicsScene, self).__init__(parent) # setup default attrs self.CROSSHAIR_RADIUS = 10 self._rgba_crosshair_pos = QPoint(0, 0) self._linear_crosshair_pos = QPoint(0, 0) self._linear_crosshair_size = 15 # create scene self.setSceneRect(0, 0, 500, 500) self.gradient_type = attrs.RGBA self.drawRGBACrosshair() self.drawLinearCrosshair() self._drawRGBAForegroundItem() """ DRAW GRADIENTS """ def drawGradient(self): """ Draws the gradient that will be displayed to the user """ _gradient = draw.drawColorTypeGradient(self.gradient_type, self.width(), self.height()) direction = self.linearCrosshairDirection() self.rgba_foreground.hide() # show gradient for RGBA gradient if self.gradient_type == attrs.RGBA: """ for some reason the darker it gets the harder of a time the picker has and the steps become larger and larger =/ """ # get value main_widget = getWidgetAncestor(self.views()[0], ColorGradientDelegate) value = main_widget.color().valueF() self.rgba_foreground.updateSize(QRectF(0, 0, self.width(), self.height())) self.rgba_foreground.updateGradient(value, self.width(), self.height()) self.rgba_foreground.show() # update linear gradient else: if direction == Qt.Horizontal: _gradient.setFinalStop(QPoint(self.width(), 0)) elif direction == Qt.Vertical: # TODO Move this to draw utils (gradient inversion) _gradient.setStart(QPoint(0, self.height())) _gradient.setFinalStop(QPoint(0, 0)) self.setBackgroundBrush(QBrush(_gradient)) """ PROPERTIES """ @property def gradient_type(self): return self._gradient_type @gradient_type.setter def gradient_type(self, gradient_type): self._gradient_type = gradient_type """ CROSSHAIR""" def drawLinearCrosshair(self): """ Creates the initial linear crosshair. Note that this is hard coded to setup to be drawn horizontally. You can update the direction with setLinearCrosshairDirection(Qt.Direction) on the ColorInputWidget. """ self.linear_crosshair_item = ColorPickerItem1D() self.linear_crosshair_item.setWidth(self.width()) self.linear_crosshair_item.setHeight(self.height()) self.linear_crosshair_item.setDirection(Qt.Horizontal) # add group item self.addItem(self.linear_crosshair_item) # hide by default self.linear_crosshair_item.hide() def setLinearCrosshairDirection(self, direction): """ Sets the direction of travel of the linear crosshair. This will also update the display of the crosshair Args: direction (Qt.Vertical | Qt. Horizontal): what direction this gradient will be displayed as. """ # set direction self._linear_crosshair_direction = direction self.linear_crosshair_item.setDirection(direction) def linearCrosshairDirection(self): return self._linear_crosshair_direction """ RGBA """ def drawRGBACrosshair(self): """ rgba_topline_item rgba_botline_item rgba_leftline_item rgba_rightline_item: rgba_crosshair_item_circle: center portion of crosshair """ # vertical line self.rgba_topline_item = DualColoredLineSegment() self.rgba_topline_item.setLine(0, 0, 0, self.height()) self.rgba_botline_item = DualColoredLineSegment() self.rgba_botline_item.setLine(0, 0, 0, self.height()) # horizontal line self.rgba_leftline_item = DualColoredLineSegment() self.rgba_leftline_item.setLine(0, 0, self.width(), 0) self.rgba_rightline_item = DualColoredLineSegment() self.rgba_rightline_item.setLine(0, 0, self.width(), 0) # crosshair circle self.rgba_crosshair_item_circle = QGraphicsEllipseItem() self.rgba_crosshair_item_circle.setRect( -(self.CROSSHAIR_RADIUS * 0.5), -(self.CROSSHAIR_RADIUS * 0.5), self.CROSSHAIR_RADIUS, self.CROSSHAIR_RADIUS ) # add items self.rgba_crosshair_item = QGraphicsItemGroup() self.rgba_crosshair_item.addToGroup(self.rgba_crosshair_item_circle) self.rgba_crosshair_item.addToGroup(self.rgba_topline_item) self.rgba_crosshair_item.addToGroup(self.rgba_botline_item) self.rgba_crosshair_item.addToGroup(self.rgba_leftline_item) self.rgba_crosshair_item.addToGroup(self.rgba_rightline_item) self.addItem(self.rgba_crosshair_item) def getRGBACrosshairPos(self): return self._rgba_crosshair_pos def setRGBACrosshairPos(self, pos): """ Places the crosshair at a specific location in the widget. This is generally used when updating color values, and passing them back to the color widget. Args: pos (QPoint): in LOCAL space """ crosshair_radius = self.CROSSHAIR_RADIUS * 0.5 xpos = pos.x() ypos = pos.y() # update items positions self.rgba_crosshair_item_circle.setPos(pos) self.rgba_topline_item.setLine(xpos, 0, xpos, ypos - crosshair_radius) self.rgba_botline_item.setLine(xpos, ypos + crosshair_radius, xpos, self.height()) self.rgba_leftline_item.setLine(0, ypos, xpos - crosshair_radius, ypos) self.rgba_rightline_item.setLine(xpos + crosshair_radius, ypos, self.width(), ypos) # set attr self._rgba_crosshair_pos = pos def updateRGBACrosshair(self): main_widget = getWidgetAncestor(self, ColorGradientDelegate) xpos = (main_widget.color().hueF() * self.width()) ypos = math.fabs((main_widget.color().saturationF() * self.height()) - self.height()) pos = QPoint(xpos, ypos) self.setRGBACrosshairPos(pos) def _drawRGBAForegroundItem(self): """ Creates the RGBA Foreground item. This item is just one big rectangle that spans the entire scene with a 1D gradient in it to compensate for the Saturation portion of the RGBA gradient. """ self.rgba_foreground = RGBAForegroundGradient(self.width(), self.height()) self.addItem(self.rgba_foreground) self.rgba_foreground.setZValue(-10)
class DataNode: def __init__(self, data, radius=15): self.data = data # Add circular node self.node = QGraphicsEllipseItem(0, 0, 1, 1) # Set radius self.radius = radius # Add text label self.label = QGraphicsTextItem(data.label) font = self.label.font() font.setPointSize(10) self.label.setFont(font) # Add line between label and node self.line1 = QGraphicsLineItem(0, 0, 1, 1) self.line2 = QGraphicsLineItem(0, 0, 1, 1) self.node.setZValue(20) self.label.setZValue(10) self.line1.setZValue(10) self.line2.setZValue(10) self.line1.setPen(get_pen('0.5')) self.line2.setPen(get_pen('0.5')) self.color = '0.8' @property def radius(self): return self._radius @radius.setter def radius(self, value): self._radius = value self.node.setRect(-value, -value, 2 * value, 2 * value) def contains(self, point): # Check label if self.label.contains(self.label.mapFromScene(point)): return True # Check node if self.node.contains(self.node.mapFromScene(point)): return True return False def update(self): self.node.update() def add_to_scene(self, scene): scene.addItem(self.node) scene.addItem(self.label) scene.addItem(self.line1) scene.addItem(self.line2) def remove_from_scene(self, scene): scene.removeItem(self.node) scene.removeItem(self.label) scene.removeItem(self.line1) scene.removeItem(self.line2) @property def node_position(self): pos = self.node.pos() return pos.x(), pos.y() @node_position.setter def node_position(self, value): self.node.setPos(value[0], value[1]) self.update_lines() @property def label_position(self): pos = self.label.pos() return pos.x(), pos.y() @label_position.setter def label_position(self, value): self.label.setPos(value[0], value[1]) self.update_lines() def update_lines(self): x0, y0 = self.label_position x2, y2 = self.node_position x1 = 0.5 * (x0 + x2) y1 = y0 self.line1.setLine(x0, y0, x1, y1) self.line2.setLine(x1, y1, x2, y2) @property def color(self): return qt_to_mpl_color(self.node.brush().color()) @color.setter def color(self, value): self.node.setBrush(mpl_to_qt_color(value))
class DataNode: def __init__(self, data, radius=15): self.data = data # Add circular node self.node = QGraphicsEllipseItem(0, 0, 1, 1) # Set radius self.radius = radius # Add text label self.label = QGraphicsTextItem(data.label) font = self.label.font() font.setPointSize(10) self.label.setFont(font) # Add line between label and node self.line1 = QGraphicsLineItem(0, 0, 1, 1) self.line2 = QGraphicsLineItem(0, 0, 1, 1) self.node.setZValue(20) self.label.setZValue(10) self.line1.setZValue(10) self.line2.setZValue(10) self.line1.setPen(get_pen('0.5')) self.line2.setPen(get_pen('0.5')) self.color = '0.8' @property def radius(self): return self._radius @radius.setter def radius(self, value): self._radius = value self.node.setRect(-value, -value, 2 * value, 2 * value) def contains(self, point): # Check label if self.label.contains(self.label.mapFromScene(point)): return True # Check node if self.node.contains(self.node.mapFromScene(point)): return True return False def update(self): self.node.update() def add_to_scene(self, scene): scene.addItem(self.node) scene.addItem(self.label) scene.addItem(self.line1) scene.addItem(self.line2) def remove_from_scene(self, scene): scene.removeItem(self.node) scene.removeItem(self.label) scene.removeItem(self.line1) scene.removeItem(self.line2) @property def node_position(self): pos = self.node.pos() return pos.x(), pos.y() @node_position.setter def node_position(self, value): self.node.setPos(value[0], value[1]) self.update_lines() @property def label_position(self): pos = self.label.pos() return pos.x(), pos.y() @label_position.setter def label_position(self, value): self.label.setPos(value[0], value[1]) self.update_lines() def update_lines(self): x0, y0 = self.label_position x2, y2 = self.node_position x1 = 0.5 * (x0 + x2) y1 = y0 self.line1.setLine(x0, y0, x1, y1) self.line2.setLine(x1, y1, x2, y2) @property def color(self): return qt_to_mpl_color(self.node.brush().color()) @color.setter def color(self, value): self.node.setBrush(mpl_to_qt_color(value))