def _draw_edges(self, painter, topleft_point, bottomright_point): for edge in self._edges: edge_coords = edge.coordinates color = QColor(0x70, 0x70, 0x70) pen = QPen(color) pen.setWidth(1.5) painter.setPen(pen) for from_, to_ in zip(edge_coords, edge_coords[1:]): start_point = QPointF(*from_) end_point = QPointF(*to_) # optimization: don't draw edges that are outside of the current scope if (start_point.x() > bottomright_point.x() or start_point.y() > bottomright_point.y()) and \ (end_point.x() > bottomright_point.x() or end_point.y() > bottomright_point.y()): continue elif (start_point.x() < topleft_point.x() or start_point.y() < topleft_point.y()) and \ (end_point.x() < topleft_point.x() or end_point.y() < topleft_point.y()): continue painter.drawPolyline((start_point, end_point)) # arrow # end_point = self.mapToScene(*edges[-1]) end_point = (edge_coords[-1][0], edge_coords[-1][1]) arrow = [ QPointF(end_point[0] - 3, end_point[1]), QPointF(end_point[0] + 3, end_point[1]), QPointF(end_point[0], end_point[1] + 6) ] brush = QBrush(color) painter.setBrush(brush) painter.drawPolygon(arrow)
def mouseMoveEvent(self, event): """ Mouse move event handler @param event: @type event: """ modifiers = QApplication.keyboardModifiers() # mouse coordinates, relative to parent widget pos = self.mapToParent(event.pos()) img = self.tool.layer.parentImage r = self.tool.resizingCoeff self.tool.targetQuad_old = self.tool.getTargetQuad( ) # TODO added 15/05/18 validate self.posRelImg = (pos - QPoint(img.xOffset, img.yOffset)) / r if modifiers == Qt.ControlModifier | Qt.AltModifier: if self.tool.isModified(): dlgWarn("A transformation is in progress", "Reset first") return # update the new starting position self.posRelImg_ori = self.posRelImg # (pos - QPoint(img.xOffset, img.yOffset)) / r self.posRelImg_frozen = self.posRelImg self.tool.moveRotatingTool() self.tool.parent().repaint() return curimg = self.tool.layer.getCurrentImage() w, h = curimg.width(), curimg.height() s = w / self.tool.img.width() form = self.tool.getForm() if form.options['Free']: pass elif form.options['Rotation']: center = self.tool.getTargetQuad().boundingRect().center() v = QPointF(self.posRelImg.x() - center.x(), self.posRelImg.y() - center.y()) v0 = QPointF(self.posRelImg_frozen.x() - center.x(), self.posRelImg_frozen.y() - center.y()) theta = (np.arctan2(v.y(), v.x()) - np.arctan2(v0.y(), v0.x())) * 180.0 / np.pi T = QTransform() # self.tool.geoTrans_ori) T.translate(center.x(), center.y()).rotate(theta).translate( -center.x(), -center.y()) q = T.map(self.tool.getFrozenQuad()) for i, role in enumerate( ['topLeft', 'topRight', 'bottomRight', 'bottomLeft']): self.tool.btnDict[role].posRelImg = q.at(i) elif form.options['Translation']: # translation vector (coordinates are relative to the full size image) p = QPointF(self.posRelImg) - QPointF(self.posRelImg_frozen) T = QTransform() T.translate(p.x(), p.y()) q = T.map(self.tool.getFrozenQuad()) for i, role in enumerate( ['topLeft', 'topRight', 'bottomRight', 'bottomLeft']): self.tool.btnDict[role].posRelImg = q.at(i) self.tool.moveRotatingTool() self.tool.modified = True self.tool.layer.applyToStack() self.parent().repaint()
def _draw_edges(self, painter, topleft_point, bottomright_point): # draw edges if self._edges: for edge in self._edges: edge_coords = edge.coordinates if edge.sort == EdgeSort.BACK_EDGE: # it's a back edge # Honey color = QColor(0xf9, 0xd5, 0x77) elif edge.sort == EdgeSort.TRUE_BRANCH: # True branch # Aqar color = QColor(0x79, 0xcc, 0xcd) elif edge.sort == EdgeSort.FALSE_BRANCH: # False branch # Tomato color = QColor(0xf1, 0x66, 0x64) else: # Dark Gray color = QColor(0x56, 0x5a, 0x5c) pen = QPen(color) pen.setWidth(2) painter.setPen(pen) for from_, to_ in zip(edge_coords, edge_coords[1:]): start_point = QPointF(*from_) end_point = QPointF(*to_) # optimization: don't draw edges that are outside of the current scope if (start_point.x() > bottomright_point.x() or start_point.y() > bottomright_point.y()) and \ (end_point.x() > bottomright_point.x() or end_point.y() > bottomright_point.y()): continue elif (start_point.x() < topleft_point.x() or start_point.y() < topleft_point.y()) and \ (end_point.x() < topleft_point.x() or end_point.y() < topleft_point.y()): continue painter.drawPolyline((start_point, end_point)) # arrow # end_point = self.mapToScene(*edges[-1]) end_point = (edge_coords[-1][0], edge_coords[-1][1]) arrow = [ QPointF(end_point[0] - 3, end_point[1]), QPointF(end_point[0] + 3, end_point[1]), QPointF(end_point[0], end_point[1] + 6) ] brush = QBrush(color) painter.setBrush(brush) painter.drawPolygon(arrow)
def on_mouse_move_event_from_view(self, point: QPointF): """ Highlight memory region under cursor. """ self.setToolTip('') mr = self._get_region_from_point(point) if mr is None: self._remove_hover_region() return try: addr = self._get_addr_from_pos(point.x()) item = self.workspace.instance.cfb.floor_item(addr) if item is not None: _, item = item self.setToolTip(f'{str(item)} in {str(mr)}') except KeyError: pass if mr is self._map_hover_region: return self._remove_hover_region() self._map_hover_region = mr self._generate_hover_region()
def __init__(self, center: QPointF): super(BezierGraphicsItem, self).__init__() self.setCursor(Qt.ArrowCursor) self.setZValue(-1) self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) self._m_radius = float(0) self._m_centerPos = center self._m_textPos = QPointF(center.x() - 7, center.y() - 30) self._m_pointList = BezierPointItemList() self._m_pen_isSelected = QPen() self._m_pen_noSelected = QPen() self._m_noSelectedThickness = 2 self._m_isSelectedThickness = 2 self._m_pen_noSelectedColor = QColor(Qt.gray) self._m_pen_isSelectedColor = QColor(Qt.blue) self._m_pen_isSelected.setColor(self._m_pen_isSelectedColor) self._m_pen_noSelected.setColor(self._m_pen_noSelectedColor) self._m_pen_isSelected.setWidth(self._m_isSelectedThickness) self._m_pen_noSelected.setWidth(self._m_noSelectedThickness) self._m_graphicsType = GraphicsType.NoneType self.setPen(self._m_pen_noSelected)
def move_selected_copmonents__cmd(self, x, y): new_rel_pos = QPointF(x, y) # if one node item would leave the scene (f.ex. pos.x < 0), stop left = False for i in self.scene().selectedItems(): new_pos = i.pos() + new_rel_pos if new_pos.x() - i.width / 2 < 0 or \ new_pos.x() + i.width / 2 > self.scene().width() or \ new_pos.y() - i.height / 2 < 0 or \ new_pos.y() + i.height / 2 > self.scene().height(): left = True break if not left: # moving the items items_group = self.scene().createItemGroup( self.scene().selectedItems()) items_group.moveBy(new_rel_pos.x(), new_rel_pos.y()) self.scene().destroyItemGroup(items_group) # saving the command self.undo_stack.push( MoveComponents_Command(self, self.scene().selectedItems(), p_from=-new_rel_pos, p_to=QPointF(0, 0))) self.viewport().repaint()
def on_mouse_move_event_from_view(self, point: QPointF): """ Add hover items. """ self._remove_hover_indicators() self._hover_addr = self._get_addr_from_pos(point.x()) self._gen_hover_indicator()
def edgeAt(self, pos: QtCore.QPointF) -> Optional[str]: view = next(iter(self.scene().views())) dist = view.mapToScene(QtCore.QRect(0, 0, self.cursor_dist, 1)).boundingRect().width() edge = "" if abs(self.rect().top() - pos.y()) < dist: edge += "top" elif abs(self.rect().bottom() - pos.y()) < dist: edge += "bottom" if abs(self.rect().left() - pos.x()) < dist: edge += "left" elif abs(self.rect().right() - pos.x()) < dist: edge += "right" if edge == "": return None return edge
def edgeAt(self, pos: QtCore.QPointF) -> Optional[str]: view = next(iter(self.scene().views()), None) if view is None: return None dist = (view.mapToScene(QtCore.QRect(0, 0, 10, 1)).boundingRect().width()) if abs(self.rect().left() - pos.x()) < dist: return "left" elif abs(self.rect().right() - pos.x()) < dist: return "right" elif abs(pos.y() - self.top) < dist: return "top" if self.trim_enabled else None elif abs(pos.y() - self.bottom) < dist: return "bottom" if self.trim_enabled else None else: return None
def draw_text(painter: QPainter, x: float, y: float, flags, text: str): # Originally from # https://stackoverflow.com/questions/24831484/how-to-align-qpainter-drawtext-around-a-point-not-a-rectangle size = 32767.0 corner = QPointF(x, y - size) if flags & Qt.AlignHCenter: corner.setX(corner.x() - size / 2.0) elif flags & Qt.AlignRight: corner.setX(corner.x() - size) if flags & Qt.AlignVCenter: corner.setY(corner.y() + size / 2.0) elif flags & Qt.AlignTop: corner.setY(corner.y() + size) else: flags |= Qt.AlignBottom rect = QRectF(corner.x(), corner.y(), size, size) painter.drawText(rect, int(flags), text)
def mousePressEvent(self, e): self.clicked = True xt, yt = e.pos().x(), e.pos().y() p = QPointF(xt, yt) self.moving = None for p in [self.A, self.B, self.C]: if abs(xt - p.x()) + abs(yt - p.y()) < 15: self.moving = p break
def drawText(self, painter, flags, text, boundingRect=0): size = 32767.0 x, y = self.boundingRect().center() corner = QPointF(x, y - size) if flags & Qt.AlignHCenter: corner.setX(corner.x() - size / 2.0) elif flags & Qt.AlignRight: corner.setX(corner.x() - size) if flags & Qt.AlignVCenter: corner.setY(corner.y() + size / 2.0) elif flags & Qt.AlignTop: corner.setY(corner.y() + size) else: flags |= Qt.AlignBottom rect = QRectF(corner.x(), corner.y(), size, size) pen = painter.setPen(painter.brush().color().inverse()) painter.save() painter.drawText(rect, flags, text, boundingRect) painter.restore()
def readFromStream(self, inStream): size = inStream.readInt32() count = inStream.readInt32() for point in self.childItems(): self.scene().removeItem(point) self.fixedPoints = [] for i in range(count): point = QPointF() inStream >> point self.fixedPoints.append( activeSplinePoint(point.x(), point.y(), parentItem=self)) self.updatePath() self.updateLUTXY() return self
def tooltip(self, point: QPointF, state: bool): if not self._calloutEnabled: return if not self._tooltip: self._tooltip = Callout(self.chart()) if state: self._tooltip.setText('X: {} \nY: {} '.format( computeAxisValue(self.chart().axisX(), point.x()), computeAxisValue(self.chart().axisY(), point.y()))) self._tooltip.setAnchor(point) self._tooltip.setZValue(11) self._tooltip.updateGeometry() self._tooltip.show() else: self._tooltip.hide()
def infoDone(): try: token = self.info.text().split(' ') if self.colorInfoFormat == 0: # RGB r, g, b = [int(x) for x in token if x.isdigit()] pt = QPointF(*swatchImg.GetPoint(*rgb2hsB(r, g, b)[:2])) + offset elif self.colorInfoFormat == 1: # CMYK c, m, y, k = [int(x) for x in token if x.isdigit()] pt = QPointF(*swatchImg.GetPoint(*rgb2hsB(*cmyk2rgb(c, m, y, k))[:2])) + offset elif self.colorInfoFormat == 2: # HSV h, s, _ = [int(x) for x in token if x.isdigit()] if not 0 <= s <= 100: raise ValueError pt = QPointF(*swatchImg.GetPoint(h, s / 100.0)) + offset else: raise ValueError self.workingMarker.setPos(pt.x(), pt.y()) except ValueError: dlgWarn("Invalid color")
def moveTo_point(self, point: QPointF) -> None: """ 移动到指定坐标位置条目 """ count: int = len(self.__listItem) for i in range(count): # 如果不是最后一个,则辨别指定项 if i != (count - 1): # 辨别方法,如果不在两项之间,则不是指定项 if self.__horizontal: if not ( (point.x() >= self.__listItem[i][1].topLeft().x()) and (point.x() < self.__listItem[i + 1][1].topLeft().x())): continue else: if not ( (point.y() >= self.__listItem[i][1].topLeft().y()) and (point.y() < self.__listItem[i + 1][1].topLeft().y())): continue self.__currentIndex = i self.__currentItem = self.__listItem[i][0] self.__targetRect = self.__listItem[i][1] if self.__horizontal: self.__targetLen = self.__targetRect.topLeft().x() self.__barLen = self.__barRect.topLeft().x() else: self.__targetLen = self.__targetRect.topLeft().y() self.__barLen = self.__barRect.topLeft().y() self.__isForward = (self.__targetLen > self.__barLen) distance: int = int(abs(self.__targetLen - self.__barLen)) # 重新获取每次移动的步长 self.__step = self.__initStep(int(distance)) self.__timer.start() self.currentItemChanged.emit(self.__currentIndex, self.__currentItem) break
def paint(self, painter, option, widget): pen = QPen(Qt.black, 1) if self._hover: pen.setColor(Qt.blue) if self.isSelected(): pen.setColor(Qt.red) pen.setWidth(2) painter.setPen(pen) painter.drawEllipse(self.boundingRect()) if self.state_type == StateType.FINAL: painter.drawEllipse(self.boundingRect().adjusted(7, 7, -7, -7)) elif self.state_type == StateType.INITIAL: center = QPointF(self.boundingRect().center()) center.setX(center.x() - (self.boundingRect().height() * 0.3)) up = QPointF(self.boundingRect().topLeft()) up.setY(up.y() + (self.boundingRect().height() * 0.3)) down = QPointF(self.boundingRect().bottomLeft()) down.setY(down.y() - (self.boundingRect().height() * 0.3)) triangle = [center, up, down] painter.drawPolygon(triangle) painter.drawText(self.boundingRect(), Qt.AlignCenter, str(self.state_number))
def test_drop_project_item_on_design_view(self): mime_data = QMimeData() item_type = next(iter(self.toolbox.item_factories)) mime_data.setText(f"{item_type},spec") gv = self.toolbox.ui.graphicsView scene_pos = QPointF(44, 20) pos = gv.mapFromScene(scene_pos) event = QDropEvent(pos, Qt.CopyAction, mime_data, Qt.NoButton, Qt.NoModifier) with mock.patch('PySide2.QtWidgets.QGraphicsSceneDragDropEvent.source' ) as mock_drop_event_source, mock.patch.object( self.toolbox, "project"), mock.patch.object( self.toolbox, "show_add_project_item_form" ) as mock_show_add_project_item_form: mock_drop_event_source.return_value = ProjectItemDragMixin() gv.dropEvent(event) mock_show_add_project_item_form.assert_called_once() mock_show_add_project_item_form.assert_called_with(item_type, scene_pos.x(), scene_pos.y(), spec="spec") item_shadow = gv.scene().item_shadow self.assertTrue(item_shadow.isVisible()) self.assertEqual(item_shadow.pos(), scene_pos)
class mixerForm(baseGraphicsForm): @classmethod def getNewWindow(cls, targetImage=None, axeSize=500, layer=None, parent=None): wdgt = mixerForm(axeSize=axeSize, layer=layer, parent=parent) wdgt.setWindowTitle(layer.name) return wdgt def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None): super().__init__(parent=parent, targetImage=targetImage, layer=layer) self.setMinimumSize(axeSize, axeSize + 80) # color wheel size self.cwSize = axeSize * 0.95 self.setAttribute(Qt.WA_DeleteOnClose) # options optionList = ['Monochrome'] listWidget1 = optionsWidget(options=optionList, exclusive=False, changed=self.dataChanged) listWidget1.setMinimumWidth(listWidget1.sizeHintForColumn(0) + 5) listWidget1.setMinimumHeight(listWidget1.sizeHintForRow(0) * len(optionList) + 5) self.options = listWidget1.options # barycentric coordinate basis : the 3 base points form an equilateral triangle h = self.cwSize - 50 s = h * 2 / np.sqrt(3) self.R, self.G, self.B = QPointF(10, h + 20), QPointF(10 + s, h + 20), QPointF(10 + s / 2, 20) # Conversion matrix from cartesian coordinates (x, y, 1) to barycentric coordinates (alpha, beta, gamma) self.M = np.array([[self.R.x(), self.G.x(), self.B.x()], [self.R.y(), self.G.y(), self.B.y()], [1, 1, 1 ]]) self.invM = np.linalg.inv(self.M) self.setBackgroundImage() # active points self.rPoint = activeMixerPoint(self.R.x(), self.R.y(), color=Qt.red, fillColor=Qt.white) self.rPoint.source = self.R self.gPoint = activeMixerPoint(self.G.x(), self.G.y(), color=Qt.green, fillColor=Qt.white) self.gPoint.source = self.G self.bPoint = activeMixerPoint(self.B.x(), self.B.y(), color=Qt.blue, fillColor=Qt.white) self.bPoint.source = self.B graphicsScene = self.scene() for point in [self.rPoint, self.gPoint, self.bPoint]: graphicsScene.addItem(point) gl = QGridLayout() gl.addWidget(listWidget1, 0, 0, 2, 2) self.addCommandLayout(gl) self.setDefaults() self.setWhatsThis( """<b>Channel Mixer</b><br> To <b>mix the R, G, B channels</b>, drag the 3 control points inside the triangle.<br> The triangle vertices and the control points correspond to channels. The closer a control point is to a vertex, the greater the corresponding channel contribution. """ ) # end of setWhatsThis def updateLayer(self): baryCoordR = self.invM @ [self.rPoint.x(), self.rPoint.y(), 1] baryCoordG = self.invM @ [self.gPoint.x(), self.gPoint.y(), 1] baryCoordB = self.invM @ [self.bPoint.x(), self.bPoint.y(), 1] self.mixerMatrix = np.vstack((baryCoordR, baryCoordG, baryCoordB)) self.layer.applyToStack() self.layer.parentImage.onImageChanged() def setDefaults(self): try: self.dataChanged.disconnect() except RuntimeError: pass self.mixerMatrix = np.identity(3, dtype=np.float) self.dataChanged.connect(self.updateLayer) def setBackgroundImage(self): img = QImage(QSize(256, 256), QImage.Format_ARGB32) img.fill(QColor(100, 100, 100)) a = np.arange(256) buf = np.meshgrid(a, a) buf1 = QImageBuffer(img)[:, :, :3][:, :, ::-1] buf1[:, :, 0], buf1[:, :, 1] = buf buf1[:, :, 2] = 1 buf2 = np.tensordot(buf1, self.invM, axes=(-1, -1)) * 255 np.clip(buf2, 0, 255, out=buf2) buf1[...] = buf2 img = img.scaled(self.cwSize, self.cwSize) qp = QPainter(img) # draw edges qp.drawLine(self.R, self.G) qp.drawLine(self.G, self.B) qp.drawLine(self.B, self.R) # draw center b = (self.B + self.R + self.G) / 3.0 qp.drawLine(b-QPointF(10, 0), b + QPointF(10, 0)) qp.drawLine(b - QPointF(0, 10), b + QPointF(0, 10)) qp.end() self.scene().addItem(QGraphicsPixmapItem(QPixmap.fromImage(img)))
def translate(self, *args): offset = QPointF(*args) for item in self: item.moveBy(offset.x(), offset.y())
def _get_distance(pt0: QPointF, pt1: QPointF) -> float: d = (pt1.x() - pt0.x())**2 + (pt1.y() - pt0.y())**2 return math.sqrt(d)
def connection_path(p1: QPointF, p2: QPointF): """Returns the nice looking QPainterPath of a connection for two given points.""" path = QPainterPath() path.moveTo(p1) distance_x = abs(p1.x()) - abs(p2.x()) distance_y = abs(p1.y()) - abs(p2.y()) if ((p1.x() < p2.x() - 30) or math.sqrt((distance_x**2) + (distance_y**2)) < 100) and (p1.x() < p2.x()): path.cubicTo(p1.x() + ((p2.x() - p1.x()) / 2), p1.y(), p1.x() + ((p2.x() - p1.x()) / 2), p2.y(), p2.x(), p2.y()) elif p2.x() < p1.x() - 100 and abs(distance_x) / 2 > abs(distance_y): path.cubicTo(p1.x() + 100 + (p1.x() - p2.x()) / 10, p1.y(), p1.x() + 100 + (p1.x() - p2.x()) / 10, p1.y() - (distance_y / 2), p1.x() - (distance_x / 2), p1.y() - (distance_y / 2)) path.cubicTo(p2.x() - 100 - (p1.x() - p2.x()) / 10, p2.y() + (distance_y / 2), p2.x() - 100 - (p1.x() - p2.x()) / 10, p2.y(), p2.x(), p2.y()) else: path.cubicTo(p1.x() + 100 + (p1.x() - p2.x()) / 3, p1.y(), p2.x() - 100 - (p1.x() - p2.x()) / 3, p2.y(), p2.x(), p2.y()) return path
class wheel(QWidget): currentColorChanged = Signal(QColor) def __init__(self, parent=None): super(wheel, self).__init__(parent) self.setFixedSize(256, 256) # start, end angles for value arc self.s_ang, self.e_ang = 135, 225 # offset angle and direction for color wheel self.o_ang, self.rot_d = 45, -1 # 1 for clock-wise, -1 for widdershins # other initializations self.pos = QPointF(-100, -100) self.vIdCen = QPointF(-100, -100) self.vIdAng = radians(self.s_ang) self.chPt = self.pos self.hue = self.sat = self.value = 255 self.setup() self.pos = self.cWhBox.center() self._namedColorList = [] self._namedColorPts = [] self._showNames = False self.setMouseTracking(True) self.installEventFilter(self) self._startedTimer = False ## def timerSpinner(self): ## "won't this be fun" ## self.o_ang -= 1; self.o_ang %= 360 ## stable = False ## ## colWhl = QConicalGradient(self.cen, self.o_ang) ## whl_cols = [Qt.red, Qt.magenta, ## Qt.blue, Qt.cyan, Qt.green, ## Qt.yellow, Qt.red] ## for i, c in enumerate(whl_cols[::self.rot_d]): ## colWhl.setColorAt(i / 6.0, c) ## ## if stable: # crosshairs stay on color ## t = radians(self.hue + self.o_ang * -self.rot_d) * -self.rot_d ## r = self.sat / 255.0 * self.cW_rad ## x, y = r * cos(t) + self.cen.x(), r * -sin(t) + self.cen.y() ## self.chPt = QPointF(x, y) ## else: # crosshairs stay on point ## t = atan2(self.cen.y() - self.pos.y(), self.pos.x() - self.cen.x()) ## h = (int(degrees(t)) - self.o_ang) * -self.rot_d ## self.hue = (h if h > 0 else h + 360) % 360 ## col = QColor(); col.setHsv(self.hue, self.sat, self.value) ## self.currentColorChanged.emit(col) ## ## self.cWhlBrush1 = QBrush(colWhl) ## self.update() def resizeEvent(self, event): self.setup() # re-construct the sizes self.setNamedColors(self._namedColorList) def getColor(self): col = QColor() col.setHsv(self.hue, self.sat, self.value) return col def setNamedColors(self, colorList): "sets list [(name, #html)] of named colors" self._namedColorList = colorList lst = [] r2 = (self.vAoBox.width() + self.vAiBox.width()) / 4.0 for i in self._namedColorList: h, s, v, a = QColor(i[1]).getHsv() t = radians(h + self.o_ang * -self.rot_d) * -self.rot_d r = s / 255.0 * self.cW_rad x, y = r * cos(t) + self.cen.x(), r * -sin(t) + self.cen.y() lst.append(QPointF(x, y)) #t2 = ((v / 255.0) * self.ang_w + radians(self.e_ang) + 2 * pi) % (2 * pi) #x, y = r2 * cos(t2) + self.cen.x(), r2 * -sin(t2) + self.cen.y() #lst.append(QPointF(x, y)) self._namedColorPts = lst def showNamedColors(self, flag=False): "show/hide location of named colors on color wheel" self._showNames = flag self.update() def setColor(self, color): # saturation -> radius h, s, v, a = color.getHsv() # hue -> angle self.hue, self.sat, self.value = h, s, v # value -> side bar thingy t = radians(h + self.o_ang * -self.rot_d) * -self.rot_d r = s / 255.0 * self.cW_rad x, y = r * cos(t) + self.cen.x(), r * -sin(t) + self.cen.y() self.chPt = QPointF(x, y) # hue, saturation self.vIdAng = t2 = (v / 255.0) * self.ang_w + radians(self.e_ang) self.vIdAng = t2 = t2 if t2 > 0 else t2 + 2 * pi r2 = self.vAoBox.width() / 2.0 x, y = r2 * cos(t2) + self.cen.x(), r2 * -sin(t2) + self.cen.y() self.vIdCen, self.vIdAng = QPointF(x, y), t2 # value self.vIdBox.moveCenter(self.vIdCen) self.update() def eventFilter(self, source, event): if (event.type() == QEvent.MouseButtonPress or (event.type() == QEvent.MouseMove and event.buttons() == Qt.LeftButton)): self.pos = pos = event.pos() t = atan2(self.cen.y() - pos.y(), pos.x() - self.cen.x()) if self.colWhlPath.contains(pos): # in the color wheel self.chPt = pos #if not self._startedTimer: # self.timer = QTimer() # self.timer.timeout.connect(self.timerSpinner) # self.timer.start(30.303) # self._startedTimer = True # hue -> mouse angle (same as t here) h = (int(degrees(t)) - self.o_ang) * -self.rot_d self.hue = (h if h > 0 else h + 360) % 360 # saturation -> mouse radius (clipped to wheel radius) m_rad = sqrt((self.pos.x() - self.cen.x())**2 + (self.pos.y() - self.cen.y())**2) self.sat = int(255 * min(m_rad / self.cW_rad, 1)) if self.vInArcPath.contains(pos): # in the value selection arc self.vIdAng = t if t > 0 else t + 2 * pi r2 = self.vAoBox.width() / 2.0 x, y = r2 * cos(t) + self.cen.x(), r2 * -sin(t) + self.cen.y() self.vIdCen = QPointF(x, y) self.vIdBox.moveCenter(self.vIdCen) self.value = int(255 * (t - radians(self.e_ang)) / self.ang_w) % 256 self.update() col = QColor() col.setHsv(self.hue, self.sat, self.value) self.currentColorChanged.emit(col) return QWidget.eventFilter(self, source, event) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(painter.Antialiasing) #painter.setBrush(QBrush(Qt.black, Qt.NoBrush)) #painter.drawRect(self.winBox) # border # value selector indicator painter.setBrush(QBrush(Qt.black, Qt.SolidPattern)) painter.drawPie(self.vIdBox, 16 * (degrees(self.vIdAng) - 22.5), 720) # value selector arc painter.setClipPath(self.vArcPath) painter.setPen(Qt.NoPen) arc = QConicalGradient(self.cen, self.e_ang) color = QColor() color.setHsv(self.hue, self.sat, 255) arc.setColorAt(1 - (self.e_ang - self.s_ang) / 360.0, color) arc.setColorAt(1, Qt.black) arc.setColorAt(0, Qt.black) painter.setBrush(arc) painter.drawPath(self.vArcPath) painter.setClipPath(self.vArcPath, Qt.NoClip) # color wheel painter.setPen(Qt.NoPen) painter.setBrush(self.cWhlBrush1) painter.drawEllipse(self.cWhBox) painter.setBrush(self.cWhlBrush2) painter.drawEllipse(self.cWhBox) # crosshairs painter.setClipPath(self.colWhlPath) painter.setBrush(QBrush(Qt.black, Qt.SolidPattern)) chVert = QRectF(0, 0, 2, 20) chHort = QRectF(0, 0, 20, 2) chVert.moveCenter(self.chPt) chHort.moveCenter(self.chPt) painter.drawRect(chVert) painter.drawRect(chHort) # named color locations if self._showNames: painter.setClipPath(self.vArcPath, Qt.NoClip) painter.setPen(Qt.SolidLine) try: painter.drawPoints(*self._namedColorPts) # PyQt except: painter.drawPoints(self._namedColorPts) # PySide def setup(self): "sets bounds on value arc and color wheel" # bounding boxes self.winBox = QRectF(self.rect()) self.vIoBox = QRectF() # value indicator arc outer self.vIdBox = QRectF() # value indicator box self.vAoBox = QRectF() # value arc outer self.vAiBox = QRectF() # value arc inner self.cWhBox = QRectF() # color wheel self.vIdBox.setSize(QSizeF(15, 15)) self.vIoBox.setSize(self.winBox.size()) self.vAoBox.setSize(self.winBox.size() - self.vIdBox.size() / 2.0) self.vAiBox.setSize(self.vAoBox.size() - QSizeF(20, 20)) self.cWhBox.setSize(self.vAiBox.size() - QSizeF(20, 20)) # center - shifted to the right slightly x = self.winBox.width() - (self.vIdBox.width() + self.vAiBox.width()) / 2.0 self.cen = QPointF(x, self.winBox.height() / 2.0) # positions and initial settings self.vAoBox.moveCenter(self.cen) self.vAiBox.moveCenter(self.cen) self.cWhBox.moveCenter(self.cen) self.vIdBox.moveCenter(self.vIdCen) self.cW_rad = self.cWhBox.width() / 2.0 self.ang_w = radians(self.s_ang) - radians(self.e_ang) # gradients colWhl = QConicalGradient(self.cen, self.o_ang) whl_cols = [ Qt.red, Qt.magenta, Qt.blue, Qt.cyan, Qt.green, Qt.yellow, Qt.red ] for i, c in enumerate(whl_cols[::self.rot_d]): colWhl.setColorAt(i / 6.0, c) rad = min(self.cWhBox.width() / 2.0, self.cWhBox.height() / 2.0) cWhlFade = QRadialGradient(self.cen, rad, self.cen) cWhlFade.setColorAt(0, Qt.white) cWhlFade.setColorAt(1, QColor(255, 255, 255, 0)) self.cWhlBrush1 = QBrush(colWhl) self.cWhlBrush2 = QBrush(cWhlFade) # painter paths (arcs, wheel) rad = self.vAoBox.width() / 2.0 x, y = rad * cos(radians(self.s_ang)), -rad * sin(radians(self.s_ang)) x += self.cen.x() y += self.cen.y() self.vArcPath = QPainterPath(QPointF(x, y)) # value arc (for color) self.vArcPath.arcTo(self.vAoBox, self.s_ang, self.e_ang - self.s_ang) self.vArcPath.arcTo(self.vAiBox, self.e_ang, self.s_ang - self.e_ang) self.vArcPath.closeSubpath() self.vInArcPath = QPainterPath(QPointF(x, y)) # value arc (for mouse) self.vInArcPath.arcTo(self.vIoBox, self.s_ang, self.e_ang - self.s_ang) self.vInArcPath.arcTo(self.vAiBox, self.e_ang, self.s_ang - self.e_ang) self.vInArcPath.closeSubpath() self.colWhlPath = QPainterPath() self.colWhlPath.addEllipse(self.cWhBox)
class activeTriangle(QGraphicsPathItem): """ interactive bump triangle """ def __init__(self, x, y, bump, persistent=False, rect=None, parentItem=None): super().__init__(parent=parentItem) self.setAcceptHoverEvents(True) self.persistent = persistent self.rect = rect self.setPos(QPointF(x, y)) self.clicked = False # coordinates are relative to activeTriangle self.B, self.C, self.A = QPointF(0, 0), QPointF(50, 0), QPointF(25, -bump) self.setPen(QPen(QColor(255, 255, 255), 2)) self.update() def update(self): qpp = QPainterPath() trans = QPointF(-4, -4) # coordinates are relative to activeTriangle for p in [self.A, self.B, self.C]: qpp.addEllipse(p + trans, 4, 4) self.setPath(qpp) super().update() def mousePressEvent(self, e): self.clicked = True xt, yt = e.pos().x(), e.pos().y() p = QPointF(xt, yt) self.moving = None for p in [self.A, self.B, self.C]: if abs(xt - p.x()) + abs(yt - p.y()) < 15: self.moving = p break def mouseMoveEvent(self, e): if self.moving is None: return self.clicked = False xt, yt = e.pos().x(), e.pos().y() if self.moving is self.A: self.A.setY(yt) else: if self.moving is self.B: x1, x2 = xt, self.C.x() elif self.moving is self.C: x1, x2 = xt, self.B.x() self.A.setX((self.B.x() + self.C.x()) / 2) self.B.setX(min(x1, x2)) self.C.setX(max(x1, x2)) self.update() self.parentItem().updatePath() def mouseReleaseEvent(self, e): # get scene current spline sc = self.scene() # get parent spline activeSpline = self.parentItem() # sc.cubicItem # click event : remove point if self.clicked: if self.persistent: return activeSpline.fixedPoints.remove(self) sc.removeItem(self) return activeSpline.updateLUTXY() activeSpline.curveChanged.sig.emit() def hoverEnterEvent(self, *args, **kwargs): self.setPen(QPen(QColor(0, 255, 0), 2)) self.update() def hoverLeaveEvent(self, *args, **kwargs): self.setPen(QPen(QColor(255, 255, 255), 2)) self.update()
class mixerForm(baseGraphicsForm): @classmethod def getNewWindow(cls, targetImage=None, axeSize=500, layer=None, parent=None): wdgt = mixerForm(axeSize=axeSize, layer=layer, parent=parent) wdgt.setWindowTitle(layer.name) return wdgt def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None): super().__init__(parent=parent, targetImage=targetImage, layer=layer) self.setMinimumSize(axeSize, axeSize) self.setAttribute(Qt.WA_DeleteOnClose) self.options = None # barycentric coordinate basis : the 3 base points form an equilateral triangle self.R, self.G, self.B = QPointF(20, 235), QPointF(235, 235), QPointF(128, 13) # Conversion matrix from cartesian coordinates (x, y, 1) to barycentric coordinates (alpha, beta, gamma) self.M = np.array([[self.R.x(), self.G.x(), self.B.x()], [self.R.y(), self.G.y(), self.B.y()], [1, 1, 1]]) self.invM = np.linalg.inv(self.M) self.setBackgroundImage() # active points self.rPoint = activeMixerPoint(20, 235) self.rPoint.source = self.R self.gPoint = activeMixerPoint(235, 235) self.gPoint.source = self.G self.bPoint = activeMixerPoint(128, 13) self.bPoint.source = self.B graphicsScene = self.scene() for point in [self.rPoint, self.gPoint, self.bPoint]: graphicsScene.addItem(point) self.setDefaults() self.setWhatsThis(""" <b>Channel Mixer</b><br> To <b>mix the R, G, B channels</b>, drag the 3 control points inside the triangle.<br> """) # end of setWhatsThis def updateLayer(self): baryCoordR = self.invM @ [self.rPoint.x(), self.rPoint.y(), 1] baryCoordG = self.invM @ [self.gPoint.x(), self.gPoint.y(), 1] baryCoordB = self.invM @ [self.bPoint.x(), self.bPoint.y(), 1] self.mixerMatrix = np.vstack((baryCoordR, baryCoordG, baryCoordB)) self.layer.applyToStack() self.layer.parentImage.onImageChanged() def setDefaults(self): try: self.dataChanged.disconnect() except RuntimeError: pass self.mixerMatrix = np.identity(3, dtype=np.float) self.dataChanged.connect(self.updateLayer) def setBackgroundImage(self): img = QImage(QSize(256, 256), QImage.Format_ARGB32) img.fill(QColor(100, 100, 100)) a = np.arange(256) buf = np.meshgrid(a, a) buf1 = QImageBuffer(img)[:, :, :3][:, :, ::-1] buf1[:, :, 0], buf1[:, :, 1] = buf buf1[:, :, 2] = 1 buf2 = np.tensordot(buf1, self.invM, axes=(-1, -1)) * 255 np.clip(buf2, 0, 255, out=buf2) buf1[...] = buf2 qp = QPainter(img) qp.drawLine(self.R, self.G) qp.drawLine(self.G, self.B) qp.drawLine(self.B, self.R) b = (self.B + self.R + self.G) / 3.0 qp.drawLine(b - QPointF(10, 0), b + QPointF(10, 0)) qp.drawLine(b - QPointF(0, 10), b + QPointF(0, 10)) qp.end() self.scene().addItem(QGraphicsPixmapItem(QPixmap.fromImage(img)))
class FDAI(QWidget): def __init__(self, parent, *args, **kwargs): super().__init__(parent, *args, **kwargs) self.size = 800 self.setMinimumSize(self.size, self.size) self.center = QPointF(self.size / 2, self.size / 2) self.orientation = quaternion.from_rotation_vector( 0 * normalize(np.array([0, 1, 1]))) self.data = FDAIData() def setOrientation(self, x, y, z, angle): """Should take IMU X, Y and Z angles""" axis = normalize(np.array([x, y, z])) angle = math.radians(angle) q = quaternion.from_rotation_vector(angle * axis) self.orientation = q self.update() def setRates(self, roll, pitch, yaw): """Takes omegas for roll, pitch and yaw""" pass def setErrorNeedles(self, error_x, error_y, error_z): pass def sizeHint(self): return QSize(self.size + 1, self.size + 1) def drawScale(self, painter): painter.setBrush(Qt.black) painter.setPen(Qt.black) r = int(round(self.size / 2)) painter.drawEllipse(QPointF(r, r), r, r) painter.setBrush(Qt.white) painter.drawEllipse(QPointF(r, r), r - 26, r - 26) painter.setPen(Qt.white) pen = painter.pen() pen.setCapStyle(Qt.FlatCap) for i in range(72): angle = math.radians(i * 5.0) c = math.cos(angle) s = math.sin(angle) r1 = r - 25 if i % 18 == 0: pen.setWidth(8) painter.setPen(pen) r2 = r - 8 elif i % 6 == 0: pen.setWidth(4) painter.setPen(pen) r2 = r - 12 elif i % 2 == 0: pen.setWidth(2) painter.setPen(pen) r2 = r - 16 else: pen.setWidth(2) painter.setPen(pen) r2 = r - 20 x1 = r + c * r1 y1 = r + s * r1 x2 = r + c * r2 y2 = r + s * r2 painter.drawLine(QPointF(x1, y1), QPointF(x2, y2)) def drawBall(self, painter): painter.setPen(Qt.black) painter.drawEllipse(self.center, BALL_SCALE * R, BALL_SCALE * R) painter.setBrush(Qt.black) painter.setPen(Qt.NoPen) for p in self.data.get_visible_polygons(self.orientation): painter.setBrush(p.color) painter.drawPath(self.build_path(p.points, BALL_SCALE)) def build_path(self, points, scale=1): path = QPainterPath() if len(points) > 0: p0 = self.projectPointToCanvas(points[0], scale) path.moveTo(p0) for p in points[1:]: cp = self.projectPointToCanvas(p, scale) path.lineTo(cp) path.lineTo(p0) return path def projectPointToCanvas(self, p, scale=1): return self.pointToCanvas(self.projectPoint(p), scale) def projectPoint(self, p): """ World X is out of screen World Y is left to right World Z is bottom to top """ return p[1:] def pointToCanvas(self, p, scale=1): """ Input: projected world coordinates (2D) Input origin is in center of canvas Input first coordinate is left to right Input second coordinate is bottom to top Screen X is left to right Screen Y is top to bottom """ return QPointF(self.center.x() + scale * p[0], self.center.y() - scale * p[1]) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, on=True) self.drawScale(painter) self.drawBall(painter) painter.end()
def _handle_click(self, point: QPointF) -> None: self.selected_solar_mult.emit(point.x())
def _handle_hover(self, point: QPointF, moving_in: bool) -> None: if moving_in: self.selected_solar_mult.emit(point.x())
def sceneToGrid(pos: QPointF) -> QPoint: """Map a scene position to grid coordinates.""" return QPoint(pos.x() // Plan.cellSize, pos.y() // Plan.cellSize)
def _autoPosition(self, pos: QtCore.QPointF): """ Automatically positions the widget. Sets position just outside of the fullOpacityMargin so the widget initializes as semi-transparent. Tries to place widget to the lower right of the position. """ # Get sizes parentSize: QtCore.QSizeF = self.parent().size() size: QtCore.QSizeF = self.size() # Margin around rect. Increase the fullOpacityMargin so the widget # starts out semi-transparent. margin = self._fullOpacityMargin + self._extraAutoMargin # Update opacity to reflect new cursor distance from the widget self._updateDistanceOpacity((margin**2 + margin**2)**(0.5)) # Initialize fitting checks rightFit = False leftFit = False topFit = False bottomFit = False # Check if the widget could fit to the right of the position if pos.x() + margin + size.width() < parentSize.width(): rightFit = True else: rightFit = False # Check if the widget could fit to the left of the position if pos.x() - margin - size.width() > 0: leftFit = True else: leftFit = False # Check if the widget could fit to the bottom of the position if pos.y() + margin + size.height() < parentSize.height(): bottomFit = True else: bottomFit = False # Check if the widget could fit to the top of the position if pos.y() - margin - size.height() > 0: topFit = True else: topFit = False # Retreive geometry geo = self.geometry() # Favored position is below and to the right if (rightFit and bottomFit or rightFit and not topFit or not leftFit and not topFit): geo.moveTopLeft(QtCore.QPoint(pos.x() + margin, pos.y() + margin)) # Above to the right elif rightFit and topFit or not leftFit and topFit: geo.moveBottomLeft( QtCore.QPoint(pos.x() + margin, pos.y() - margin)) # Below to the left elif leftFit and bottomFit or leftFit and not topFit: geo.moveTopRight(QtCore.QPoint(pos.x() - margin, pos.y() + margin)) # Above to the left else: geo.moveBottomRight( QtCore.QPoint(pos.x() - margin, pos.y() - margin)) # Assign newly positioned geometry self.setGeometry(geo)