def adjust_regular_roi_bounds(rect: QRectF, point: Anchor, mouse: QPointF) -> QRectF: """ Get the new bounding rectangle of a "regular" ROI (rectangle or ellipse) after dragging of anchor. @param rect: original bounding rectangle. @param point: anchor that has been dragged. @param mouse: point that mouse has been dragged to. @return: new bounding rectangle. """ if point.position == AnchorPosition.LEFT: rect.setLeft(rect.left() + mouse.x()) elif point.position == AnchorPosition.RIGHT: rect.setRight(rect.right() + mouse.x()) elif point.position == AnchorPosition.TOP: rect.setTop(rect.top() + mouse.y()) elif point.position == AnchorPosition.TOP_LEFT: rect.setLeft(rect.left() + mouse.x()) rect.setTop(rect.top() + mouse.y()) elif point.position == AnchorPosition.TOP_RIGHT: rect.setRight(rect.right() + mouse.x()) rect.setTop(rect.top() + mouse.y()) elif point.position == AnchorPosition.BOTTOM: rect.setBottom(rect.bottom() + mouse.y()) elif point.position == AnchorPosition.BOTTOM_LEFT: rect.setLeft(rect.left() + mouse.x()) rect.setBottom(rect.bottom() + mouse.y()) elif point.position == AnchorPosition.BOTTOM_RIGHT: rect.setRight(rect.right() + mouse.x()) rect.setBottom(rect.bottom() + mouse.y()) return rect.normalized( ) # normalised() causes width/height to go to zero when dragging to negative
def drawBackground(self, painter: QPainter, rect: QRectF): rect = self.sceneRect() super().drawBackground(painter, rect) # 绘制纯色背景 # 绘制网格 xLines_thin = [] yLines_thin = [] xLines_blod = [] yLines_blod = [] for i in range(0, self.rectSize, self.gridSize): if (i % 100) != 0: xLines_thin.append( QLineF(rect.left(), rect.top() + i, rect.right(), rect.top() + i)) yLines_thin.append( QLineF(rect.left() + i, rect.top(), rect.left() + i, rect.bottom())) else: xLines_blod.append( QLineF(rect.left(), rect.top() + i, rect.right(), rect.top() + i)) yLines_blod.append( QLineF(rect.left() + i, rect.top(), rect.left() + i, rect.bottom())) painter.setPen(QPen(self.grid_line_color, self.tinny_grid_line_width)) painter.drawLines(xLines_thin) painter.drawLines(yLines_thin) painter.setPen(QPen(self.grid_line_color, self.bold_grid_line_width)) painter.drawLines(xLines_blod) painter.drawLines(yLines_blod)
def adjust_regular_roi_anchors(bounds: QRectF, anchors: list): """ Adjust the anchors of a "regular" ROI (rectangle or ellipse) @param bounds: bounding rectangle of ROI @param anchors: list of anchors """ for point in anchors: off = point.boundingRect().width() / 2 if point.position == AnchorPosition.LEFT: point.setPos(bounds.left() - off, bounds.top() - off + bounds.height() / 2) elif point.position == AnchorPosition.RIGHT: point.setPos(bounds.right() - off, bounds.top() - off + bounds.height() / 2) elif point.position == AnchorPosition.TOP: point.setPos(bounds.left() - off + bounds.width() / 2, bounds.top() - off) elif point.position == AnchorPosition.TOP_LEFT: point.setPos(bounds.left() - off, bounds.top() - off) elif point.position == AnchorPosition.TOP_RIGHT: point.setPos(bounds.right() - off, bounds.top() - off) elif point.position == AnchorPosition.BOTTOM: point.setPos(bounds.left() - off + bounds.width() / 2, bounds.bottom() - off) elif point.position == AnchorPosition.BOTTOM_LEFT: point.setPos(bounds.left() - off, bounds.bottom() - off) elif point.position == AnchorPosition.BOTTOM_RIGHT: point.setPos(bounds.right() - off, bounds.bottom() - off)
def draw_background(self, painter: QtGui.QPainter, rect: QtCore.QRectF) -> None: if not self._show_grid: return painter.setPen( QtGui.QPen( QtGui.QColor(0, 0, 0) ) ) lines = [] for x in list(self.stacker_map.row_termination_points())[:-1]: if rect.left() <= x <= rect.right(): lines.append( QLineF( x, rect.top(), x, rect.bottom(), ) ) for y in list(self.stacker_map.column_termination_points())[:-1]: if rect.top() <= y <= rect.bottom(): lines.append( QLineF( rect.left(), y, rect.right(), y, ) ) painter.drawLines(lines)
def drawBackground(self, painter: QPainter, rect: QRectF): # freqs = np.fft.fftfreq(len(w), 1 / self.sample_rate) if self.draw_grid and len(self.frequencies) > 0: painter.setPen(QPen(painter.pen().color(), 0)) parent_width = self.parent().width() if hasattr( self.parent(), "width") else 750 view_rect = self.parent().view_rect() if hasattr( self.parent(), "view_rect") else rect font_width = self.font_metrics.width( Formatter.big_value_with_suffix(self.center_freq) + " ") x_grid_size = int(view_rect.width() / parent_width * font_width) # x_grid_size = int(0.1 * view_rect.width()) if 0.1 * view_rect.width() > 1 else 1 y_grid_size = 1 x_mid = np.where(self.frequencies == 0)[0] x_mid = int(x_mid[0]) if len(x_mid) > 0 else 0 left = int(rect.left()) - (int(rect.left()) % x_grid_size) left = left if left > 0 else 0 top = rect.top() - (rect.top() % y_grid_size) bottom = rect.bottom() - (rect.bottom() % y_grid_size) right_border = int( rect.right()) if rect.right() < len(self.frequencies) else len( self.frequencies) x_range = list(range(x_mid, left, -x_grid_size)) + list( range(x_mid, right_border, x_grid_size)) lines = [QLineF(x, rect.top(), x, bottom) for x in x_range] \ + [QLineF(rect.left(), y, rect.right(), y) for y in np.arange(top, bottom, y_grid_size)] painter.drawLines(lines) scale_x, scale_y = util.calc_x_y_scale(rect, self.parent()) painter.scale(scale_x, scale_y) counter = -1 # Counter for Label for every second line for x in x_range: freq = self.frequencies[x] counter += 1 if freq != 0 and (counter % 2 != 0): # Label for every second line continue if freq != 0: prefix = "+" if freq > 0 else "" value = prefix + Formatter.big_value_with_suffix(freq, 2) else: counter = 0 value = Formatter.big_value_with_suffix(6800e6 - self.center_freq + self.status_k) font_width = self.font_metrics.width(value) painter.drawText(x / scale_x - font_width / 2, bottom / scale_y, value)
def drawPopupPointer(self, p): r = QRectF(self.rect()) if self.pointerPos == self.LeftSide: PPIX_X = self.LR_MARGIN PPIX_Y = PPIX_X * 2.0 points = [ QPointF(r.x() + PPIX_X, r.height() / 2.0 - PPIX_Y / 2.0), QPointF(r.x() + PPIX_X, r.height() / 2.0 + PPIX_Y / 2.0), QPointF(r.x(), r.height() / 2.0), ] p.drawPolygon(*points) if self.pointerPos == self.RightSide: PPIX_X = self.LR_MARGIN PPIX_Y = PPIX_X * 2.0 points = [ QPointF(r.right() - PPIX_X, r.height() / 2.0 - PPIX_Y / 2.0), QPointF(r.right() - PPIX_X, r.height() / 2.0 + PPIX_Y / 2.0), QPointF(r.right(), r.height() / 2.0), ] p.drawPolygon(*points) if self.pointerPos == self.TopSide: PPIX_Y = self.TB_MARGIN PPIX_X = PPIX_Y * 2.0 points = [ QPointF(r.x() + r.width() / 2.0 - PPIX_X / 2.0, r.top() + PPIX_Y), QPointF(r.x() + r.width() / 2.0 + PPIX_X / 2.0, r.top() + PPIX_Y), QPointF(r.x() + r.width() / 2.0, r.top()), ] p.drawPolygon(*points) if self.pointerPos == self.BottomSide: PPIX_Y = self.TB_MARGIN PPIX_X = PPIX_Y * 2.0 points = [ QPointF(r.x() + r.width() / 2.0 - PPIX_X / 2.0, r.bottom() - PPIX_Y), QPointF(r.x() + r.width() / 2.0 + PPIX_X / 2.0, r.bottom() - PPIX_Y), QPointF(r.x() + r.width() / 2.0, r.bottom()), ] p.drawPolygon(*points)
def drawBackground(self, painter: QPainter, rect: QRectF): # freqs = np.fft.fftfreq(len(w), 1 / self.sample_rate) if self.draw_grid and len(self.frequencies) > 0: painter.setPen(QPen(painter.pen().color(), Qt.FlatCap)) parent_width = self.parent().width() if hasattr(self.parent(), "width") else 750 view_rect = self.parent().view_rect() if hasattr(self.parent(), "view_rect") else rect font_width = self.font_metrics.width(Formatter.big_value_with_suffix(self.center_freq) + " ") x_grid_size = int(view_rect.width() / parent_width * font_width) # x_grid_size = int(0.1 * view_rect.width()) if 0.1 * view_rect.width() > 1 else 1 y_grid_size = view_rect.height() / parent_width * font_width x_mid = np.where(self.frequencies == 0)[0] x_mid = int(x_mid[0]) if len(x_mid) > 0 else 0 left = int(rect.left()) - (int(rect.left()) % x_grid_size) left = left if left > 0 else 0 top = rect.top() - (rect.top() % y_grid_size) bottom = rect.bottom() - (rect.bottom() % y_grid_size) right_border = int(rect.right()) if rect.right() < len(self.frequencies) else len(self.frequencies) x_range = list(range(x_mid, left, -x_grid_size)) + list(range(x_mid, right_border, x_grid_size)) lines = [QLineF(x, rect.top(), x, bottom) for x in x_range] \ + [QLineF(rect.left(), y, rect.right(), y) for y in np.arange(top, bottom, y_grid_size)] painter.drawLines(lines) scale_x = view_rect.width() / parent_width scale_y = view_rect.height() / parent_width painter.scale(scale_x, scale_y) font_height = self.font_metrics.height() counter = -1 # Counter for Label for every second line for x in x_range: freq = self.frequencies[x] counter += 1 if freq != 0 and (counter % 2 != 0): # Label for every second line continue if freq != 0: prefix = "+" if freq > 0 else "" value = prefix+Formatter.big_value_with_suffix(freq, 2) else: counter = 0 value = Formatter.big_value_with_suffix(self.center_freq) font_width = self.font_metrics.width(value) painter.drawText(x / scale_x - font_width / 2, bottom / scale_y + font_height, value)
def __iniGraphicsSystem(self): ##初始化 graphics View系统 rect = QRectF(-200, -100, 400, 200) self.scene = QGraphicsScene(rect) #scene逻辑坐标系定义 self.view.setScene(self.scene) ## 画一个矩形框,大小等于scene item = QGraphicsRectItem(rect) #矩形框正好等于scene的大小 item.setFlag(QGraphicsItem.ItemIsSelectable) #可选, item.setFlag(QGraphicsItem.ItemIsFocusable) #可以有焦点 pen = QPen() pen.setWidth(2) item.setPen(pen) self.scene.addItem(item) ##一个位于scene中心的椭圆,测试局部坐标 #矩形框内创建椭圆,绘图项的局部坐标,左上角(-100,-50),宽200,高100 item2 = QGraphicsEllipseItem(-100, -50, 200, 100) item2.setPos(0, 0) item2.setBrush(QBrush(Qt.blue)) item2.setFlag(QGraphicsItem.ItemIsSelectable) #可选, item2.setFlag(QGraphicsItem.ItemIsFocusable) #可以有焦点 item2.setFlag(QGraphicsItem.ItemIsMovable) #可移动 self.scene.addItem(item2) ##一个圆,中心位于scene的边缘 item3 = QGraphicsEllipseItem(-50, -50, 100, 100) #矩形框内创建椭圆,绘图项的局部坐标 item3.setPos(rect.right(), rect.bottom()) item3.setBrush(QBrush(Qt.red)) item3.setFlag(QGraphicsItem.ItemIsSelectable) #可选, item3.setFlag(QGraphicsItem.ItemIsFocusable) #可以有焦点 item3.setFlag(QGraphicsItem.ItemIsMovable) #可移动 self.scene.addItem(item3) self.scene.clearSelection()
def _hitTest(self, rc: QRectF, mousePos: QPointF) -> Hit: maxdist = 4 if not rc.adjusted(-maxdist, -maxdist, maxdist, maxdist).contains(mousePos): return Hit.NoHit def dist(p1, p2): return (p1 - p2).manhattanLength() if dist(rc.topLeft(), mousePos) < maxdist: return Hit.TopLeft elif dist(rc.topRight(), mousePos) < maxdist: return Hit.TopRight elif dist(rc.bottomRight(), mousePos) < maxdist: return Hit.BottomRight elif dist(rc.bottomLeft(), mousePos) < maxdist: return Hit.BottomLeft elif abs(rc.left() - mousePos.x()) < maxdist: return Hit.Left elif abs(rc.right() - mousePos.x()) < maxdist: return Hit.Right elif abs(rc.top() - mousePos.y()) < maxdist: return Hit.Top elif abs(rc.bottom() - mousePos.y()) < maxdist: return Hit.Bottom elif rc.contains(mousePos): return Hit.Center else: return Hit.NoHit
def _GridLines(self, painter: QtGui.QPainter, rect: QtCore.QRectF): # * grid bounds left = int(math.floor(rect.left())) right = int(math.ceil(rect.right())) top = int(math.floor(rect.top())) bottom = int(math.ceil(rect.bottom())) fLeft = left - (left % self._gridSize) fTop = top - (top % self._gridSize) # * compute lines majorLine = [ QtCore.QLine(x, top, x, bottom) for x in range(fLeft, right, self._gridSize * self._lineSpacing) ] for y in range(fTop, bottom, self._gridSize * self._lineSpacing): majorLine.append(QtCore.QLine(left, y, right, y)) minorLines = [ QtCore.QLine(x, top, x, bottom) for x in range(fLeft, right, self._gridSize) ] for y in range(fTop, bottom, self._gridSize): minorLines.append(QtCore.QLine(left, y, right, y)) # * draw lines painter.setPen(self._penMajorLine) for line in majorLine: painter.drawLine(line) painter.setPen(self._penMinorLine) for line in minorLines: painter.drawLine(line)
def drawBackground(self, painter: QtGui.QPainter, rect: QtCore.QRectF): grid_size = 25 left = int(rect.left()) - (int(rect.left()) % grid_size) top = int(rect.top()) - (int(rect.top()) % grid_size) lines = [] for x in range(left, int(rect.right()), grid_size): lines.append(QLineF(x, rect.top(), x, rect.bottom())) for y in range(top, int(rect.bottom()), grid_size): lines.append(QLineF(rect.left(), y, rect.right(), y)) # print(len(lines)) painter.drawLines(lines)
def drawBackground(self, painter: QPainter, rect: QRectF): # freqs = np.fft.fftfreq(len(w), 1 / self.sample_rate) if self.draw_grid and len(self.frequencies) > 0: painter.setPen(QPen(painter.pen().color(), Qt.FlatCap)) parent_width = self.parent().width() if hasattr( self.parent(), "width") else 750 view_rect = self.parent().view_rect() if hasattr( self.parent(), "view_rect") else rect font_width = self.font_metrics.width( self.value_with_suffix(self.center_freq) + " ") x_grid_size = int(view_rect.width() / parent_width * font_width) # x_grid_size = int(0.1 * view_rect.width()) if 0.1 * view_rect.width() > 1 else 1 y_grid_size = view_rect.height() / parent_width * font_width x_mid = np.where(self.frequencies == 0)[0] x_mid = int(x_mid[0]) if len(x_mid) > 0 else 0 left = int(rect.left()) - (int(rect.left()) % x_grid_size) left = left if left > 0 else 0 top = rect.top() - (rect.top() % y_grid_size) bottom = rect.bottom() - (rect.bottom() % y_grid_size) right_border = int( rect.right()) if rect.right() < len(self.frequencies) else len( self.frequencies) x_range = list(range(x_mid, left, -x_grid_size)) + list( range(x_mid, right_border, x_grid_size)) lines = [QLineF(x, rect.top(), x, bottom) for x in x_range] \ + [QLineF(rect.left(), y, rect.right(), y) for y in np.arange(top, bottom, y_grid_size)] painter.drawLines(lines) scale_x = view_rect.width() / parent_width scale_y = view_rect.height() / parent_width painter.scale(scale_x, scale_y) font_height = self.font_metrics.height() for x in x_range: value = self.value_with_suffix(self.center_freq + self.frequencies[x]) font_width = self.font_metrics.width(value) painter.drawText(x / scale_x - font_width / 2, bottom / scale_y + font_height, value)
def qrectf_to_inscribed_rect(rect: QRectF) -> QRect: """ Return the largest integer QRect such that it is completely contained in `rect`. """ xmin = int(math.ceil(rect.x())) xmax = int(math.floor(rect.right())) ymin = int(math.ceil(rect.top())) ymax = int(math.floor(rect.bottom())) return QRect(xmin, ymin, max(xmax - xmin, 0), max(ymax - ymin, 0))
def itemChange(self, change, value): """ Check, whether the item is out of the scenes bounds """ if change == QGraphicsItem.ItemPositionChange: # get the new position newPos = value oldRect = self.sceneBoundingRect() newRect = QRectF(newPos.x(), newPos.y(), oldRect.width(), oldRect.height()) sceneRect = QRectF(self.scene().sceneRect()) # check if the left or right side of the new rect is outside the scenes bounds -> move it back inside if newRect.left() < sceneRect.left(): newPos.setX(sceneRect.left()) elif newRect.left() + newRect.width() > sceneRect.right(): newPos.setX(sceneRect.right() - newRect.width()) # check if the top or bottom side of the new rect is outside the scenes bounds -> move it back inside if newRect.bottom() > sceneRect.bottom(): newPos.setY(sceneRect.bottom() - newRect.height()) elif newRect.top() < sceneRect.top(): newPos.setY(sceneRect.top()) return newPos return super(NodeItem, self).itemChange(change, value)
def _from_local_coordinates(self, image_index: QModelIndex, rectangle: QRectF) -> Selection: """ Scales a floating point rectangle from local coordinates to an integer based rectangle in the source image coordinates. """ image = image_index.sibling(image_index.row(), 0).data(Qt.UserRole) image_width: int = image.width scaling_factor = image_width / self.scene().width() return Selection( Point(round(rectangle.left() * scaling_factor), round(rectangle.top() * scaling_factor)), Point(round(rectangle.right() * scaling_factor), round(rectangle.bottom() * scaling_factor)), image)
def on_rect_selected(self, rect): if not self.spectrogram: return scene_rect = QRectF( self.mapToScene(rect.topLeft()), self.mapToScene(rect.bottomRight()) ) self.scene.set_selection(scene_rect) fragment = self.spectrogram.get_sound_fragment( (scene_rect.left(), scene_rect.right()), (scene_rect.bottom(), scene_rect.top()), ) self.fragment_selected.emit(fragment)
def paintEvent(self, event): unused(event) compassAIIntrusion = 0 compassHalfSpan = 180 painter = QPainter() painter.begin(self) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.HighQualityAntialiasing, True) tapeGaugeWidth = self.tapesGaugeWidthFor(self.width(), self.width()) aiheight = self.height() aiwidth = self.width() - tapeGaugeWidth * 2 if (aiheight > aiwidth): aiheight = aiwidth AIMainArea = QRectF(tapeGaugeWidth, 0, aiwidth, aiheight) AIPaintArea = QRectF(0, 0, self.width(), self.height()) velocityMeterArea = QRectF(0, 0, tapeGaugeWidth, aiheight) altimeterArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, aiheight) # calc starts compassRelativeWidth = 0.75 compassBottomMargin = 0.78 compassSize = compassRelativeWidth * AIMainArea.width( ) # Diameter is this times the width. compassCenterY = AIMainArea.bottom() + compassSize / 4 if self.height( ) - compassCenterY > AIMainArea.width() / 2 * compassBottomMargin: compassCenterY = self.height( ) - AIMainArea.width() / 2 * compassBottomMargin compassCenterY = (compassCenterY * 2 + AIMainArea.bottom() + compassSize / 4) / 3 compassArea = QRectF( AIMainArea.x() + (1 - compassRelativeWidth) / 2 * AIMainArea.width(), compassCenterY - compassSize / 2, compassSize, compassSize) if self.height() - compassCenterY < compassSize / 2: compassHalfSpan = math.acos( (compassCenterY - self.height()) * 2 / compassSize) * 180 / math.pi + self.COMPASS_DISK_RESOLUTION if compassHalfSpan > 180: compassHalfSpan = 180 compassAIIntrusion = compassSize / 2 + AIMainArea.bottom( ) - compassCenterY if compassAIIntrusion < 0: compassAIIntrusion = 0 #calc ends hadClip = painter.hasClipping() painter.setClipping(True) painter.setClipRect(AIPaintArea) self.drawAIGlobalFeatures(painter, AIMainArea, AIPaintArea) self.drawAIAttitudeScales(painter, AIMainArea, compassAIIntrusion) self.drawAIAirframeFixedFeatures(painter, AIMainArea) self.drawAICompassDisk(painter, compassArea, compassHalfSpan) painter.setClipping(hadClip) if self.isGPSAltitudePrimary: self.drawAltimeter(painter, altimeterArea, self.GPSAltitude, self.primaryAltitude, self.verticalVelocity) else: self.drawAltimeter(painter, altimeterArea, self.primaryAltitude, self.GPSAltitude, self.verticalVelocity) if self.isGPSSpeedPrimary: self.drawVelocityMeter(painter, velocityMeterArea, self.groundspeed, self.primarySpeed) else: self.drawVelocityMeter(painter, velocityMeterArea, self.primarySpeed, self.groundspeed) painter.end()
class OverlayView(QGraphicsItem): """ This is a "global" view which manages all of the line things: * edge views. * interlinks (domain-domain) * components It is actually a single graphics item drawn over the top of everything else. Therefore, it is passive and doesn't react with the user in any way. """ def __init__(self, view_model: "ModelView") -> None: super().__init__() self.setZValue(DRAWING.Z_EDGES) self.view_model = view_model self.component_views: Dict[gr.Component, ComponentView] = {} self.interlink_views: Dict[gr.Domain, InterlinkView] = {} self.edge_views: Dict[gr.Edge, EdgeView] = {} # Create the edge views for edge in view_model.model.edges: self.edge_views[edge] = EdgeView(self, edge) # Create the component views for component in view_model.model.components: self.component_views[component] = ComponentView(self, component) # Create our interlink views for gene_view in view_model.gene_views.values(): for left, right in array_helper.lagged_iterate( gene_view.domain_views.values()): self.interlink_views[left] = InterlinkView(self, left, right) # Our bounds encompass the totality of the model # - find this! self.rect = QRectF(0, 0, 0, 0) for gene_view in view_model.gene_views.values(): for domain_view in gene_view.domain_views.values(): r = domain_view.window_rect() if r.left() < self.rect.left(): self.rect.setLeft(r.left()) if r.right() > self.rect.right(): self.rect.setRight(r.right()) if r.top() < self.rect.top(): self.rect.setTop(r.top()) if r.bottom() > self.rect.bottom(): self.rect.setBottom(r.bottom()) MARGIN = 256 self.rect.setTop(self.rect.top() - MARGIN * 2) self.rect.setLeft(self.rect.left() - MARGIN * 2) self.rect.setBottom(self.rect.bottom() + MARGIN) self.rect.setRight(self.rect.right() + MARGIN) def boundingRect(self): return self.rect def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: Optional[QWidget] = None) -> None: """ Paint all edges """ # Draw all the edges for edge_view in self.edge_views.values(): edge_view.paint_edge(painter) # Draw all the components for component in self.component_views.values(): component.paint_component(painter) # Draw all the interlinks for interlink in self.interlink_views.values(): interlink.paint_interlink(painter) # Draw all the names for gene_view in self.view_model.gene_views.values(): gene_view.paint_name(painter)
class GNodeSocket(QGraphicsWidget): edge_started = pyqtSignal(GEdge) edge_released = pyqtSignal(GEdge) connection_changed = pyqtSignal(object, object) # Socket, Edge or None position_changed = pyqtSignal(QPointF) INPUT = SocketType.INPUT OUTPUT = SocketType.OUTPUT def __init__(self, parent_node: 'GShaderNode', socket: NodeSocket): super().__init__(parent=parent_node) self._socket = socket self._socket.set_container(self) self._parent_g_node = parent_node self._connected_g_edges = IndexedSet() # Define socket properties self._circle_connected_brush = QColor( 255, 130, 0, 255) if self._socket.type() == NodeSocket.INPUT else QColor( 130, 255, 0, 255) self._circle_disconnected_brush = QColor( 102, 50, 0, 255) if self._socket.type() == NodeSocket.INPUT else QColor( 50, 102, 0, 255) self._circle_hover_brush = QColor( 170, 130, 0, 255) if self._socket.type() == NodeSocket.INPUT else QColor( 130, 170, 0, 255) self._border_connected_brush = QPen(QColor(255, 255, 255, 255)) self._border_disconnected_brush = QPen(Qt.black) self._circle_brush = self._circle_disconnected_brush self._border_brush = self._border_disconnected_brush self._bbox = QRectF(0, 0, 10, 10) self._moving_edge = False self._current_edge = None self._layout = QGraphicsLinearLayout(Qt.Horizontal) self._init_socket() def _init_socket(self): self.setLayout(self._layout) self.setFlag(QGraphicsItem.ItemIsMovable, False) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) self.setAcceptHoverEvents(True) def type(self) -> SocketType: return self._socket.type() def value(self): return self._socket.value() def set_value(self, value: typing.Any): self._socket.set_value(value) def save_value(self): """Saves the value of this socket internally. This value can be reassigned by calling 'restore_value()'.""" self._socket.save_value() def restore_value(self): """Restores the value of this socket to the last saved value.""" self._socket.restore_value() def parent_node(self) -> 'GShaderNode': return self._parent_g_node def get_size(self): """Returns a tuple with the width,height of this socket.""" return self._bbox.right(), self._bbox.bottom() def get_backend_socket(self) -> NodeSocket: return self._socket def get_connected_nodes(self) -> typing.List['GShaderNode']: return [n.get_container() for n in self._socket.get_connected_nodes()] def get_connected_sockets(self) -> typing.List['GNodeSocket']: return [ s.get_container() for s in self._socket.get_connected_sockets() ] def set_index(self, index: int): self._socket.set_index(index) def get_index(self) -> int: return self._socket.get_index() def is_connected(self) -> bool: return self._socket.is_connected() def boundingRect(self): return self._bbox def paint(self, painter: QPainter, option, widget=None): painter.setPen(self._border_brush) painter.setBrush(self._circle_brush) painter.drawEllipse(self._bbox) def get_scene_pos(self) -> QPointF: """Gets the center position of this socket in scene coordinates.""" pos = self.scenePos() pos.setX(pos.x() + self._bbox.right() / 2) pos.setY(pos.y() + self._bbox.bottom() / 2) return pos def connect_to(self, socket: 'GNodeSocket') -> GEdge: """ Connects this GNodeSocket to another GNodeSocket. :param other_socket: Other GNodeSocket to connect this socket to. :return: the GEdge that was created between the sockets, or the old GEdge if there already exists a connection. """ edge = self._socket.connect_to(socket.get_backend_socket()) # Only emit change event for input sockets, as nothing really changed for the output socket (at least not for the node as a whole) if self.type() == SocketType.INPUT: self.connection_changed.emit(self, edge) elif socket.type() == SocketType.INPUT: socket.connection_changed.emit(socket, edge) return GEdge.from_edge(edge) def get_connected_edges(self) -> IndexedSet: return self._connected_g_edges def add_connecting_edge(self, edge: GEdge): self._connected_g_edges.add(edge) def remove_connected_edge(self, gedge: GEdge): assert gedge in self._connected_g_edges self._connected_g_edges.remove(gedge) def label(self) -> str: return self._socket.label() def __eq__(self, other): if isinstance(other, GNodeSocket): return self._socket.__eq__(other.get_backend_socket()) return False def __hash__(self): return self._socket.__hash__() def __str__(self): return self._socket.__str__() # -------- Event Handling --------- # ---------------------------------- def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent): # self._circle_brush = self._circle_hover_brush self._border_brush = self._border_connected_brush self.update() event.accept() def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent): # self._circle_brush = self._circle_connected_brush if self.connected else self._circle_disconnected_brush if not self.is_connected(): self._border_brush = self._border_disconnected_brush self.update() event.accept() def mousePressEvent(self, event: QGraphicsSceneMouseEvent): # Needs to be reimplemented to be able to catch mouseMoveEvent, but it does not need to do anything event.accept() def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent): if event.buttons() == Qt.LeftButton: if not self._moving_edge: if self.type() == self.INPUT: edge = GEdge(destination=self) else: edge = GEdge(source=self) self._current_edge = edge self._moving_edge = True self.edge_started.emit(edge) else: self._current_edge.set_tip_pos(event.scenePos()) event.accept() else: event.ignore() def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent): if self._current_edge: # Prevent release without dragging self.edge_released.emit(self._current_edge) self._current_edge = None self._moving_edge = False def itemChange(self, change, value): if change == self.ItemScenePositionHasChanged: for edge in self._connected_g_edges: edge.update_edge() return super().itemChange(change, value)
def drawMagnifier(self): # First, calculate the magnifier position due to the mouse position watchAreaWidth = 16 watchAreaHeight = 16 watchAreaPixmap = QPixmap() cursor_pos = self.mousePoint watchArea = QRect( QPoint(cursor_pos.x() - watchAreaWidth / 2, cursor_pos.y() - watchAreaHeight / 2), QPoint(cursor_pos.x() + watchAreaWidth / 2, cursor_pos.y() + watchAreaHeight / 2)) if watchArea.left() < 0: watchArea.moveLeft(0) watchArea.moveRight(watchAreaWidth) if self.mousePoint.x() + watchAreaWidth / 2 >= self.screenPixel.width( ): watchArea.moveRight(self.screenPixel.width() - 1) watchArea.moveLeft(watchArea.right() - watchAreaWidth) if self.mousePoint.y() - watchAreaHeight / 2 < 0: watchArea.moveTop(0) watchArea.moveBottom(watchAreaHeight) if self.mousePoint.y( ) + watchAreaHeight / 2 >= self.screenPixel.height(): watchArea.moveBottom(self.screenPixel.height() - 1) watchArea.moveTop(watchArea.bottom() - watchAreaHeight) # tricks to solve the hidpi impact on QCursor.pos() watchArea.setTopLeft( QPoint(watchArea.topLeft().x() * self.scale, watchArea.topLeft().y() * self.scale)) watchArea.setBottomRight( QPoint(watchArea.bottomRight().x() * self.scale, watchArea.bottomRight().y() * self.scale)) watchAreaPixmap = self.screenPixel.copy(watchArea) # second, calculate the magnifier area magnifierAreaWidth = watchAreaWidth * 10 magnifierAreaHeight = watchAreaHeight * 10 fontAreaHeight = 40 cursorSize = 24 magnifierArea = QRectF( QPoint(QCursor.pos().x() + cursorSize, QCursor.pos().y() + cursorSize), QPoint(QCursor.pos().x() + cursorSize + magnifierAreaWidth, QCursor.pos().y() + cursorSize + magnifierAreaHeight)) if magnifierArea.right() >= self.screenPixel.width(): magnifierArea.moveLeft(QCursor.pos().x() - magnifierAreaWidth - cursorSize / 2) if magnifierArea.bottom() + fontAreaHeight >= self.screenPixel.height( ): magnifierArea.moveTop(QCursor.pos().y() - magnifierAreaHeight - cursorSize / 2 - fontAreaHeight) # third, draw the watch area to magnifier area watchAreaScaled = watchAreaPixmap.scaled( QSize(magnifierAreaWidth * self.scale, magnifierAreaHeight * self.scale)) magnifierPixmap = self.graphicsScene.addPixmap(watchAreaScaled) magnifierPixmap.setOffset(magnifierArea.topLeft()) # then draw lines and text self.graphicsScene.addRect(QRectF(magnifierArea), QPen(QColor(255, 255, 255), 2)) self.graphicsScene.addLine( QLineF(QPointF(magnifierArea.center().x(), magnifierArea.top()), QPointF(magnifierArea.center().x(), magnifierArea.bottom())), QPen(QColor(0, 255, 255), 2)) self.graphicsScene.addLine( QLineF(QPointF(magnifierArea.left(), magnifierArea.center().y()), QPointF(magnifierArea.right(), magnifierArea.center().y())), QPen(QColor(0, 255, 255), 2)) # get the rgb of mouse point pointRgb = QColor(self.screenPixel.toImage().pixel(self.mousePoint)) # draw information self.graphicsScene.addRect( QRectF( magnifierArea.bottomLeft(), magnifierArea.bottomRight() + QPoint(0, fontAreaHeight + 30)), Qt.black, QBrush(Qt.black)) rgbInfo = self.graphicsScene.addSimpleText( ' Rgb: ({0}, {1}, {2})'.format(pointRgb.red(), pointRgb.green(), pointRgb.blue())) rgbInfo.setPos(magnifierArea.bottomLeft() + QPoint(0, 5)) rgbInfo.setPen(QPen(QColor(255, 255, 255), 2)) rect = self.selectedArea.normalized() sizeInfo = self.graphicsScene.addSimpleText(' Size: {0} x {1}'.format( rect.width() * self.scale, rect.height() * self.scale)) sizeInfo.setPos(magnifierArea.bottomLeft() + QPoint(0, 15) + QPoint(0, fontAreaHeight / 2)) sizeInfo.setPen(QPen(QColor(255, 255, 255), 2))
class NodeItemRenderer: """ Class is used to separate the drawing from the logic. This class renders a given node to a graphics scene """ __blobConnectorSizeWithPadding = Constants.nodeItemConnectorPaddding * 2 + Constants.connectorItemOuterSize def __init__(self, nodeItem): self.__nodeItem = nodeItem # create lists to store TextRects for the node item self.__headRects = list() self.__blobTopNameRects = list() self.__blobBottomNameRects = list() # create Qt rects for the head and body area self.__rectHead = QRectF(0, 0, 1, 1) self.__rectBlobArea = QRectF(0, 0, 1, 1) # create Qt rect for enclosing rects used as bounding rects self.__rectAll = QRectF(0, 0, 1, 1) self.__rectAllSelected = QRectF(0, 0, 1, 1) self.__hasCurrentPhase = True self.update() def boundingRect(self): # selected layers have a outer 'glow', so selected layers are bigger than not selected layers if self.__nodeItem.isSelected(): return self.__rectAllSelected else: return self.__rectAll def update(self): """ Recalculates positions of name, type, blobs, etc. """ # clear lists del self.__headRects[:] del self.__blobTopNameRects[:] del self.__blobBottomNameRects[:] # get size of the name nameWidth = Constants.nodeItemFontNameMetrics.width( self.__nodeItem.getName()) + Constants.nodeItemHeadPadding nameHeight = Constants.nodeItemFontTypeMetrics.height() self.__headRects.append( TextRect(self.__nodeItem.getName(), QRectF(0, 0, nameWidth, nameHeight), Constants.nodeItemFontName)) # get size of the type typeWidth = Constants.nodeItemFontTypeMetrics.width( self.__nodeItem.getTypeString()) + Constants.nodeItemHeadPadding typeHeight = Constants.nodeItemFontTypeMetrics.height() self.__headRects.append( TextRect(self.__nodeItem.getTypeString(), QRectF(0, 0, typeWidth, typeHeight), Constants.nodeItemFontType)) # get size of the phase (if the node has the paramter include.phase if len(self.__nodeItem.getPhase()) > 0: phaseString = "Phase: " + self.__nodeItem.getPhase() phaseWidth = Constants.nodeItemFontTypeMetrics.width( phaseString) + Constants.nodeItemHeadPadding phaseHeight = Constants.nodeItemFontTypeMetrics.height() self.__headRects.append( TextRect(phaseString, QRectF(0, 0, phaseWidth, phaseHeight), Constants.nodeItemFontType)) # calculate rect width and height for names of blobs topBlobsInfo = self.__buildBlobNameRectList( self.__nodeItem.getTopConnectors()) bottomBlobsInfo = self.__buildBlobNameRectList( self.__nodeItem.getBottomConnectors()) self.__blobTopNameRects = topBlobsInfo[0] self.__blobBottomNameRects = bottomBlobsInfo[0] blobAreaWidth = 2 * self.__blobConnectorSizeWithPadding + Constants.nodeItemConnectorPaddding + \ bottomBlobsInfo[1] + topBlobsInfo[1] blobAreaHeight = max(topBlobsInfo[2], bottomBlobsInfo[2], Constants.nodeItemMinBlobAreaHeight) # calculate the total width of the node rectWidth = blobAreaWidth for textRect in self.__headRects: rectWidth = max(rectWidth, textRect.rect.width()) # change the width of the head rects to be the full width of the node self.__updateHeadRects(rectWidth, self.__headRects) # calculate the total node height rectHeight = self.__rectHead.bottom() + blobAreaHeight # create Qt rects for the body and enclosing width the top left at (0, 0) self.__rectBlobArea = QRectF(0, self.__rectHead.bottom(), rectWidth, blobAreaHeight) self.__rectAll = QRectF(0, 0, rectWidth, rectHeight) # calculate a larger box to support selection (outer glow) selSize = Constants.nodeItemSelectionSize self.__rectAllSelected = QRectF(self.__rectAll.left() - selSize, self.__rectAll.top() - selSize, self.__rectAll.width() + 2 * selSize, self.__rectAll.height() + 2 * selSize) # after calculating the total width, adjust the name rects to align at the left/right self.__adjustBlobNameRectPositions(self.__blobTopNameRects, self.__rectAll.width(), self.__rectHead.bottom(), False) self.__adjustBlobNameRectPositions(self.__blobBottomNameRects, self.__rectAll.width(), self.__rectHead.bottom(), True) def __updateHeadRects(self, finalWidth, headRects): """ Resizes the size of the head text rect after the total needed width was calculated """ totalHeadHeight = 0 for x in range(0, len(headRects)): # special case for the first head text -> start at (0, 0) and has space at the top if x == 0: headRects[x].rect = QRectF( 0, 0, finalWidth, headRects[x].rect.height() + Constants.nodeItemTextMargin) # special case for the last head text -> has space at the bottom elif x == len(headRects) - 1: headRects[x].rect = QRectF( 0, headRects[x - 1].rect.bottom(), finalWidth, headRects[x].rect.height() + Constants.nodeItemTextMargin) else: headRects[x].rect = QRectF(0, headRects[x - 1].rect.bottom(), finalWidth, headRects[x].rect.height()) # add the rects height to the total head rect height totalHeadHeight += headRects[x].rect.height() # create Qt rect for the head part self.__rectHead = QRectF(0, 0, finalWidth, totalHeadHeight) def __buildBlobNameRectList(self, connectors): """ Creates rects for all top/bottom connectors (bounding rect for the text only) """ # check if the connector or the blob name text is higher blobNameHeightRect = max(Constants.nodeItemFontBlobMetrics.height(), self.__blobConnectorSizeWithPadding) blobNameRectList = list() maxWidth = 0 height = 0 # for each connector calculate the width of the name and create a Qt rect. # calculate the max width of all blob names and the total height for item in connectors: blobName = item.getBlobName() blobNameWidth = Constants.nodeItemFontBlobMetrics.width( blobName) + Constants.nodeItemTextMargin blobRect = QRectF(0, 0, blobNameWidth, blobNameHeightRect) blobNameRectList.append(TextRect(blobName, blobRect)) maxWidth = max(maxWidth, blobNameWidth) height += blobNameHeightRect return blobNameRectList, maxWidth, height def __adjustBlobNameRectPositions(self, blobRectNames, totalWidth, aboveHeight, atLeftBorder): """ Recalculates the blob name rects after the total width of the node was calculated """ i = 0 for item in blobRectNames: # position at the left side of the node x = self.__blobConnectorSizeWithPadding # or position at the right side of the node if not atLeftBorder: x = totalWidth - self.__blobConnectorSizeWithPadding - item.rect.width( ) # calculate starting position (head size + position in body) item.rect = QRectF(x, aboveHeight + i * item.rect.height(), item.rect.width(), item.rect.height()) i += 1 def updateConnectorPositions(self, connectors, atLeftBorder): """ Repositions the connector items after the new size of the node was calculated """ i = 0 for item in connectors: # position the connector item at the left border of the node,left of the blob name rect x = Constants.nodeItemConnectorPaddding + Constants.connectorItemOuterSize / 2 # or at the right border of the node, right of the blob name rect if not atLeftBorder: x = self.__rectAll.width() - Constants.nodeItemConnectorPaddding - Constants.connectorItemOuterSize + \ Constants.connectorItemOuterSize / 2 # set the y coordinate of the connector item to be at the center of the blob name rect y = self.__rectHead.bottom() + Constants.nodeItemConnectorPaddding + Constants.connectorItemOuterSize / 2 + \ i * self.__blobConnectorSizeWithPadding item.setPos(QPointF(x, y)) i += 1 def paint(self, painter): # use antialiasing for both text and box painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) # get the opacity of the node item, layers with the include.phase parameter have a different opacity opacity = Constants.itemOpacityInPhase if not self.__nodeItem.getNodeEditor().isCurrentPhase( self.__nodeItem.getPhase()): opacity = Constants.itemOpacityNotInPhase # draw a outer 'glow' around the node item if the node is selected if self.__nodeItem.isSelected(): selectedColor = Constants.selectedColor selectedColor.setAlpha(opacity) painter.setPen(QPen(selectedColor, Constants.nodeItemSelectionSize)) painter.drawRect(self.__rectAllSelected) painter.fillRect(self.__rectAllSelected, QBrush(selectedColor)) # get the type color for the header (name and type) typeColor = LayerColorDefinitions.getTypeColor( self.__nodeItem.getType()) typeColor.setRgb(typeColor.red(), typeColor.green(), typeColor.blue(), opacity) # get background color backgroundColor = Constants.itemBackgroundColorLight backgroundColor.setAlpha(opacity) # set a linear gradient for the header (type color -> background color) gradient = QLinearGradient(0, self.__rectHead.top(), 0, self.__rectHead.bottom()) gradient.setColorAt(0, typeColor) gradient.setColorAt(0.5, backgroundColor) # draw background and border for the header painter.fillRect(self.__rectHead, QBrush(gradient)) # draw background for the blob area painter.fillRect(self.__rectBlobArea, QBrush(backgroundColor)) borderColor = Constants.itemBorderColor if self.__nodeItem.getIsInPlace(): borderColor = Constants.itemInPlaceColor borderColor.setAlpha(opacity) painter.setPen(QPen(borderColor, Constants.nodeItemBorderSize)) # draw outer border around the node painter.drawRect(self.__rectAll) # draw a line to separate header and connectors borSize = Constants.nodeItemBorderSize painter.setPen(QPen(borderColor, borSize)) painter.drawLine(self.__rectHead.left() + borSize / 2, self.__rectHead.bottom() - borSize / 2, self.__rectHead.right() - borSize / 2, self.__rectHead.bottom() - borSize / 2) painter.setPen(QPen(QColor(0, 0, 0, opacity))) # draw text of header if len(self.__headRects) > 1: # align the first head text at the bottom to provide some space at the top painter.setFont(self.__headRects[0].font) painter.drawText(self.__headRects[0].rect, Qt.AlignHCenter | Qt.AlignBottom, self.__headRects[0].text) # align other head texts at the center for i in range(1, len(self.__headRects) - 1): painter.setFont(self.__headRects[i].font) painter.drawText(self.__headRects[i].rect, Qt.AlignCenter, self.__headRects[i].text) # align the last head text at the top to provide some space at the bottom painter.setFont(self.__headRects[-1].font) painter.drawText(self.__headRects[-1].rect, Qt.AlignHCenter | Qt.AlignTop, self.__headRects[-1].text) # there is only one head text, so align it at the center elif len(self.__headRects) == 1: painter.setFont(self.__headRects[0].font) painter.drawText(self.__headRects[0].rect, Qt.AlignHCenter | Qt.AlignCenter, self.__headRects[0].text) # draw blob names painter.setFont(Constants.nodeItemFontBlob) for item in self.__blobBottomNameRects: painter.drawText(item.rect, Qt.AlignVCenter | Qt.AlignLeft, item.text) for item in self.__blobTopNameRects: painter.drawText(item.rect, Qt.AlignVCenter | Qt.AlignRight, item.text)
def translateItems(self, deltaX, deltaY): # Moving should only be enabled for node items if self.scene.selectedItems() and isinstance( self.scene.selectedItems()[0], NodeItem): # Prevent multiple layers from changing relative positions when the scenes border is reached selectedItems = self.scene.selectedItems() if len(selectedItems) > 0: selectedItemsBoundingRect = selectedItems[0].sceneBoundingRect( ) for i in range(1, len(selectedItems)): selectedItemsBoundingRect = selectedItemsBoundingRect.united( selectedItems[i].sceneBoundingRect()) # Fix for wrong scene bounding rects right and bottom selectedItemsBoundingRect.setWidth( selectedItemsBoundingRect.width() + 3) selectedItemsBoundingRect.setHeight( selectedItemsBoundingRect.height() + 3) newBoundingRect = QRectF(selectedItemsBoundingRect) newBoundingRect.moveTo(selectedItemsBoundingRect.topLeft() + QPointF(deltaX, deltaY)) sceneRect = self.scene.sceneRect() moved = False if newBoundingRect.left() < sceneRect.left(): deltaX = sceneRect.left() - selectedItemsBoundingRect.left( ) moved = True elif newBoundingRect.right() > sceneRect.right(): deltaX = sceneRect.right( ) - selectedItemsBoundingRect.right() moved = True if newBoundingRect.top() < sceneRect.top(): deltaY = sceneRect.top() - selectedItemsBoundingRect.top() moved = True elif newBoundingRect.bottom() > sceneRect.bottom(): deltaY = sceneRect.bottom( ) - selectedItemsBoundingRect.bottom() moved = True for item in self.scene.selectedItems(): item.setPos(item.scenePos() + QPointF(deltaX, deltaY)) # Check if any of the objects have reached the boundary and the view should be translated if len(selectedItems) > 0: selectedItemsBoundingRect = selectedItems[0].sceneBoundingRect( ) for i in range(1, len(selectedItems)): selectedItemsBoundingRect = selectedItemsBoundingRect.united( selectedItems[i].sceneBoundingRect()) newRect = self.view.mapFromScene( selectedItemsBoundingRect).boundingRect() if newRect.left() < 0 or newRect.right() > self.view.viewport( ).width() or newRect.top() < 0 or newRect.bottom( ) > self.view.viewport().height(): self.view.translate(-deltaX, -deltaY) moved = True return moved
def paintEvent(self, _): """ 重写绘制事件,参考 qfusionstyle.cpp 中的 CE_ProgressBarContents 绘制方法 """ option = QStyleOptionProgressBar() self.initStyleOption(option) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(0.5, 0.5) vertical = option.orientation == Qt.Vertical # 是否垂直 inverted = option.invertedAppearance # 是否反转 # 是否显示动画 indeterminate = (option.minimum == option.maximum) or ( option.minimum < option.progress < option.maximum) rect = option.rect if vertical: rect = QRect(rect.left(), rect.top(), rect.height(), rect.width()) # 翻转宽度和高度 m = QTransform.fromTranslate(rect.height(), 0) m.rotate(90.0) painter.setTransform(m, True) maxWidth = rect.width() progress = max(option.progress, option.minimum) totalSteps = max(1, option.maximum - option.minimum) progressSteps = progress - option.minimum progressBarWidth = int(progressSteps * maxWidth / totalSteps) width = progressBarWidth # 已进行的进度宽度 radius = max(1, (min(width, self.width() if vertical else self.height()) // 4) if self._radius is None else self._radius) reverse = (not vertical and option.direction == Qt.RightToLeft) or vertical if inverted: reverse = not reverse # 绘制范围 path = QPainterPath() if not reverse: progressBar = QRectF(rect.left(), rect.top(), width, rect.height()) else: progressBar = QRectF(rect.right() - width, rect.top(), width, rect.height()) # 切割范围 path.addRoundedRect(progressBar, radius, radius) painter.setClipPath(path) # 绘制背景颜色 painter.setPen(Qt.NoPen) painter.setBrush(self._color) painter.drawRoundedRect(progressBar, radius, radius) if not indeterminate: if self._animation: self._animation.stop() self._animation = None else: # 叠加颜色覆盖后出现类似线条间隔的效果 color = self._color.lighter(320) color.setAlpha(80) painter.setPen(QPen(color, self._lineWidth)) if self._animation: step = int(self._animation.animationStep() % self._lineWidth) else: step = 0 self._animation = QProgressStyleAnimation(self._fps, self) self._animation.start() # 动画斜线绘制 startX = int(progressBar.left() - rect.height() - self._lineWidth) endX = int(rect.right() + self._lineWidth) if (not inverted and not vertical) or (inverted and vertical): lines = [ QLineF(x + step, progressBar.bottom(), x + rect.height() + step, progressBar.top()) for x in range(startX, endX, self._lineWidth) ] else: lines = [ QLineF(x - step, progressBar.bottom(), x + rect.height() - step, progressBar.top()) for x in range(startX, endX, self._lineWidth) ] painter.drawLines(lines)
def interactiveResize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QPointF """ scene = self.scene() snap = scene.mainwindow.snapToGrid size = scene.GridSize offset = self.handleSize + self.handleMove moved = self.label.moved R = QRectF(self.boundingRect()) D = QPointF(0, 0) minBoundW = self.minwidth + offset * 2 minBoundH = self.minheight + offset * 2 self.prepareGeometryChange() if self.mousePressHandle == self.handleTL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.selection.setLeft(R.left()) self.selection.setTop(R.top()) self.background[self.indexT] = QPointF(R.left() + R.width() / 2, R.top()) self.background[self.indexB] = QPointF( R.left() + R.width() / 2, self.background[self.indexB].y()) self.background[self.indexL] = QPointF(R.left(), R.top() + R.height() / 2) self.background[self.indexE] = QPointF(R.left(), R.top() + R.height() / 2) self.background[self.indexR] = QPointF( self.background[self.indexR].x(), R.top() + R.height() / 2) self.polygon[self.indexT] = QPointF(R.left() + R.width() / 2, R.top() + offset) self.polygon[self.indexB] = QPointF(R.left() + R.width() / 2, self.polygon[self.indexB].y()) self.polygon[self.indexL] = QPointF(R.left() + offset, R.top() + R.height() / 2) self.polygon[self.indexE] = QPointF(R.left() + offset, R.top() + R.height() / 2) self.polygon[self.indexR] = QPointF(self.polygon[self.indexR].x(), R.top() + R.height() / 2) elif self.mousePressHandle == self.handleTM: fromY = self.mousePressBound.top() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, -offset, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.selection.setTop(R.top()) self.background[self.indexT] = QPointF( self.background[self.indexT].x(), R.top()) self.background[self.indexL] = QPointF( self.background[self.indexL].x(), R.top() + R.height() / 2) self.background[self.indexE] = QPointF( self.background[self.indexE].x(), R.top() + R.height() / 2) self.background[self.indexR] = QPointF( self.background[self.indexR].x(), R.top() + R.height() / 2) self.polygon[self.indexT] = QPointF(self.polygon[self.indexT].x(), R.top() + offset) self.polygon[self.indexL] = QPointF(self.polygon[self.indexL].x(), R.top() + R.height() / 2) self.polygon[self.indexE] = QPointF(self.polygon[self.indexE].x(), R.top() + R.height() / 2) self.polygon[self.indexR] = QPointF(self.polygon[self.indexR].x(), R.top() + R.height() / 2) elif self.mousePressHandle == self.handleTR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.selection.setRight(R.right()) self.selection.setTop(R.top()) self.background[self.indexT] = QPointF(R.right() - R.width() / 2, R.top()) self.background[self.indexB] = QPointF( R.right() - R.width() / 2, self.background[self.indexB].y()) self.background[self.indexL] = QPointF( self.background[self.indexL].x(), R.top() + R.height() / 2) self.background[self.indexE] = QPointF( self.background[self.indexE].x(), R.top() + R.height() / 2) self.background[self.indexR] = QPointF(R.right(), R.top() + R.height() / 2) self.polygon[self.indexT] = QPointF(R.right() - R.width() / 2, R.top() + offset) self.polygon[self.indexB] = QPointF(R.right() - R.width() / 2, self.polygon[self.indexB].y()) self.polygon[self.indexL] = QPointF(self.polygon[self.indexL].x(), R.top() + R.height() / 2) self.polygon[self.indexE] = QPointF(self.polygon[self.indexE].x(), R.top() + R.height() / 2) self.polygon[self.indexR] = QPointF(R.right() - offset, R.top() + R.height() / 2) elif self.mousePressHandle == self.handleML: fromX = self.mousePressBound.left() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, -offset, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) self.selection.setLeft(R.left()) self.background[self.indexL] = QPointF( R.left(), self.mousePressBound.top() + self.mousePressBound.height() / 2) self.background[self.indexE] = QPointF( R.left(), self.mousePressBound.top() + self.mousePressBound.height() / 2) self.background[self.indexT] = QPointF( R.left() + R.width() / 2, self.background[self.indexT].y()) self.background[self.indexB] = QPointF( R.left() + R.width() / 2, self.background[self.indexB].y()) self.polygon[self.indexL] = QPointF( R.left() + offset, self.mousePressBound.top() + self.mousePressBound.height() / 2) self.polygon[self.indexE] = QPointF( R.left() + offset, self.mousePressBound.top() + self.mousePressBound.height() / 2) self.polygon[self.indexT] = QPointF(R.left() + R.width() / 2, self.polygon[self.indexT].y()) self.polygon[self.indexB] = QPointF(R.left() + R.width() / 2, self.polygon[self.indexB].y()) elif self.mousePressHandle == self.handleMR: fromX = self.mousePressBound.right() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, +offset, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) self.selection.setRight(R.right()) self.background[self.indexR] = QPointF( R.right(), self.mousePressBound.top() + self.mousePressBound.height() / 2) self.background[self.indexT] = QPointF( R.right() - R.width() / 2, self.background[self.indexT].y()) self.background[self.indexB] = QPointF( R.right() - R.width() / 2, self.background[self.indexB].y()) self.polygon[self.indexR] = QPointF( R.right() - offset, self.mousePressBound.top() + self.mousePressBound.height() / 2) self.polygon[self.indexT] = QPointF(R.right() - R.width() / 2, self.polygon[self.indexT].y()) self.polygon[self.indexB] = QPointF(R.right() - R.width() / 2, self.polygon[self.indexB].y()) elif self.mousePressHandle == self.handleBL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.selection.setLeft(R.left()) self.selection.setBottom(R.bottom()) self.background[self.indexT] = QPointF( R.left() + R.width() / 2, self.background[self.indexT].y()) self.background[self.indexB] = QPointF(R.left() + R.width() / 2, R.bottom()) self.background[self.indexL] = QPointF(R.left(), R.bottom() - R.height() / 2) self.background[self.indexE] = QPointF(R.left(), R.bottom() - R.height() / 2) self.background[self.indexR] = QPointF( self.background[self.indexR].x(), R.bottom() - R.height() / 2) self.polygon[self.indexT] = QPointF(R.left() + R.width() / 2, self.polygon[self.indexT].y()) self.polygon[self.indexB] = QPointF(R.left() + R.width() / 2, R.bottom() - offset) self.polygon[self.indexL] = QPointF(R.left() + offset, R.bottom() - R.height() / 2) self.polygon[self.indexE] = QPointF(R.left() + offset, R.bottom() - R.height() / 2) self.polygon[self.indexR] = QPointF(self.polygon[self.indexR].x(), R.bottom() - R.height() / 2) elif self.mousePressHandle == self.handleBM: fromY = self.mousePressBound.bottom() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, +offset, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.selection.setBottom(R.bottom()) self.background[self.indexB] = QPointF( self.background[self.indexB].x(), R.bottom()) self.background[self.indexL] = QPointF( self.background[self.indexL].x(), R.top() + R.height() / 2) self.background[self.indexE] = QPointF( self.background[self.indexE].x(), R.top() + R.height() / 2) self.background[self.indexR] = QPointF( self.background[self.indexR].x(), R.top() + R.height() / 2) self.polygon[self.indexB] = QPointF(self.polygon[self.indexB].x(), R.bottom() - offset) self.polygon[self.indexL] = QPointF(self.polygon[self.indexL].x(), R.top() + R.height() / 2) self.polygon[self.indexE] = QPointF(self.polygon[self.indexE].x(), R.top() + R.height() / 2) self.polygon[self.indexR] = QPointF(self.polygon[self.indexR].x(), R.top() + R.height() / 2) elif self.mousePressHandle == self.handleBR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.selection.setRight(R.right()) self.selection.setBottom(R.bottom()) self.background[self.indexT] = QPointF( R.right() - R.width() / 2, self.background[self.indexT].y()) self.background[self.indexB] = QPointF(R.right() - R.width() / 2, R.bottom()) self.background[self.indexL] = QPointF( self.background[self.indexL].x(), R.bottom() - R.height() / 2) self.background[self.indexE] = QPointF( self.background[self.indexE].x(), R.bottom() - R.height() / 2) self.background[self.indexR] = QPointF(R.right(), R.bottom() - R.height() / 2) self.polygon[self.indexT] = QPointF(R.right() - R.width() / 2, self.polygon[self.indexT].y()) self.polygon[self.indexB] = QPointF(R.right() - R.width() / 2, R.bottom() - offset) self.polygon[self.indexL] = QPointF(self.polygon[self.indexL].x(), R.bottom() - R.height() / 2) self.polygon[self.indexE] = QPointF(self.polygon[self.indexE].x(), R.bottom() - R.height() / 2) self.polygon[self.indexR] = QPointF(R.right() - offset, R.bottom() - R.height() / 2) self.updateHandles() self.updateTextPos(moved=moved) self.updateAnchors(self.mousePressData, D)
class TestPathFinding(TestCase): ################################################################################ def setUp(self): """ """ # --left --right # ' ' # I | II | III # ------+==========+------ --top # VIII | IX (in) | IV # ------+==========+------ --bottom # VII | VI | V self._offset = 10 self._rect = QRectF(QPointF(-20, -10), QPointF(20, 10)) self._pointI = QPointF(self._rect.left()-self._offset, self._rect.top()-self._offset) self._pointII = QPointF(self._rect.center().x(), self._rect.top()-self._offset) self._pointIII = QPointF(self._rect.right()+ self._offset, self._rect.top()- self._offset) self._pointIV = QPointF(self._rect.right()+self._offset, self._rect.center().y()) self._pointV = QPointF(self._rect.right()+self._offset, self._rect.bottom()+self._offset) self._pointVI = QPointF(self._rect.center().x(), self._rect.bottom()+self._offset) self._pointVII = QPointF(self._rect.left()-self._offset, self._rect.bottom()+self._offset) self._pointVIII = QPointF(self._rect.left()-self._offset, self._rect.center().y()) self._pointIX = self._rect.center() self._lineI_VII = QLineF(self._pointI, self._pointVII) self._lineI_V = QLineF(self._pointI, self._pointV) self._lineII = QLineF(self._pointII, QPointF(self._rect.center().x(), self._rect.top())) self._lineII_IV = QLineF(QPointF(self._rect.right()-self._offset, self._rect.top()-self._offset), QPointF(self._rect.right()+self._offset, self._rect.top()+self._offset)) ################################################################################ def testPointRectDist(self): """ """ lineI = PathFinding.pointRectDist(self._pointI, self._rect) self.assertEqual(lineI.p2(), self._rect.topLeft()) self.assertEqual(lineI.length(), pow(pow(self._offset, 2)+pow(self._offset, 2), 0.5)) lineII = PathFinding.pointRectDist(self._pointII, self._rect) self.assertEqual(lineII.p2(), QPointF(self._rect.center().x(), self._rect.top())) self.assertEqual(lineII.length(), self._offset) lineIII = PathFinding.pointRectDist(self._pointIII, self._rect) self.assertEqual(lineIII.p2(), self._rect.topRight()) self.assertEqual(lineIII.length(), pow(pow(self._offset, 2)+pow(self._offset, 2), 0.5)) lineIV = PathFinding.pointRectDist(self._pointIV, self._rect) self.assertEqual(lineIV.p2(), QPointF(self._rect.right(), self._rect.center().y())) self.assertEqual(lineIV.length(), self._offset) lineV = PathFinding.pointRectDist(self._pointV, self._rect) self.assertEqual(lineV.p2(), self._rect.bottomRight()) self.assertEqual(lineV.length(), pow(pow(self._offset, 2)+pow(self._offset, 2), 0.5)) lineVI = PathFinding.pointRectDist(self._pointVI, self._rect) self.assertEqual(lineVI.p2(), QPointF(self._rect.center().x(), self._rect.bottom())) self.assertEqual(lineVI.length(), self._offset) lineVII = PathFinding.pointRectDist(self._pointVII, self._rect) self.assertEqual(lineVII.p2(), self._rect.bottomLeft()) self.assertEqual(lineVII.length(), pow(pow(self._offset, 2)+pow(self._offset, 2), 0.5)) lineVIII = PathFinding.pointRectDist(self._pointVIII, self._rect) self.assertEqual(lineVIII.p2(), QPointF(self._rect.left(), self._rect.center().y())) self.assertEqual(lineVIII.length(), self._offset) lineIX = PathFinding.pointRectDist(self._pointIX, self._rect) self.assertEqual(lineIX.p2(), self._pointIX) self.assertEqual(lineIX.length(), 0) ################################################################################ def testIntersects(self): """ """ rect = QRectF(QPointF(-50, -10), QPointF(50, 10)) # line completely outside of the rectangle self.assertFalse(PathFinding.intersects(rect, QLineF(QPointF(-100, -50), QPointF(-100, 50)))) # the line is a top side of the rectangle self.assertFalse(PathFinding.intersects(rect, QLineF(rect.topLeft(), rect.topRight()))) # the line starts at the left corner of the rectangle and is not perpendicular to any of the rectangle sides; # the line ends outside of the rectangle, not going through it self.assertFalse(PathFinding.intersects(rect, QLineF(rect.topLeft(), QPointF(-100, -100)))) # the line starts at the left corner of the rectangle and is perpendicular to the top side of the rectangle; # the line ends outside of the rectangle, not going through it self.assertFalse(PathFinding.intersects(rect, QLineF(rect.topLeft(), QPointF(rect.left(), rect.top() - 100)))) # the line is horizontal and goes straight through the center of the rectangle self.assertTrue(PathFinding.intersects(rect, QLineF(QPointF(-100, 0), QPointF(100, 0)))) # the line is vertical and goes straight through the center of the rectangle self.assertTrue(PathFinding.intersects(rect, QLineF(QPointF(0, -100), QPointF(0, 100)))) # the line is vertical and goes up from the bottom right corner of the rectangle self.assertFalse(PathFinding.intersects(rect, QLineF(rect.bottomRight(), QPointF(rect.right(), rect.top()-100)))) # the line is diagonal of the rectangle self.assertTrue(PathFinding.intersects(rect, QLineF(rect.topLeft(), rect.bottomRight())))
class DomainView(QGraphicsItem): """ The basic and only interactive unit of the view. Paints a domain. We have an `is_selected` variable. This is independent and used in lieu of either Qt's selection or the selection on the groot form. """ def __init__(self, domain: gr.UserDomain, gene_view: "GeneView", positional_index: int, precursor: Optional["DomainView"]) -> None: """ CONSTRUCTOR :param domain: Domain to view :param gene_view: Owning view :param positional_index: Index of domain within gene :param precursor: Previous domain, or None """ assert isinstance(domain, gr.UserDomain) # # SUPER # super().__init__() self.setZValue(DRAWING.Z_GENE) # # FIELDS # self.gene_view = gene_view self.model_view = gene_view.model_view self.sibling_next: DomainView = None self.sibling_previous: DomainView = precursor self.domain: gr.UserDomain = domain self.mousedown_original_pos: QPointF = None self.mousemove_label: str = None self.mousemove_snapline: Tuple[int, int] = None self.mousedown_move_all = False self.index = positional_index self.is_selected = False self.colour = DRAWING.DEFAULT_COLOUR # # POSITION # table = gene_view.model_view.lookup_table self.rect = QRectF(0, 0, domain.length * table.letter_size, table.gene_height) self.load_state() # # PRECURSOR # if precursor: precursor.sibling_next = self # # COMPONENTS # self.components: List[ gr. Component] = self.model_view.model.components.find_components_for_minor_domain( self.domain) def get_x_for_site(self, site): offset = site - self.domain.start offset *= self.model_view.lookup_table.letter_size return self.x() + offset @property def options(self) -> groot.data.config.GlobalOptions: return groot.data.config.options() @property def model(self) -> gr.Model: return self.model_view.model def load_state(self): """ Loads the state (position and colour) of this domain view from the options. If there is no saved state, the default is applied. """ ac = (self.domain.gene.index, self.domain.start) position = self.model.lego_domain_positions.get(ac) if not isinstance(position, dict): self.reset_state() return x = position.get("x", 0) y = position.get("y", 0) c = position.get("c", DRAWING.DEFAULT_COLOUR.colour.name()) self.setPos(x, y) self.colour = ColourBlock(QColor(c)) def save_state(self): """ Saves the state (position) of this domain view to the options. """ ac = (self.domain.gene.index, self.domain.start) self.model.lego_domain_positions[ac] = { "x": self.pos().x(), "y": self.pos().y(), "c": self.colour.colour.name() } def reset_state(self): """ Resets the state (position and colour) of this domain view to the default. The reset state is automatically saved to the options. """ table = self.gene_view.model_view.lookup_table precursor = self.sibling_previous domain = self.domain if precursor: x = precursor.window_rect().right() y = precursor.window_rect().top() else: x = domain.start * table.letter_size y = domain.gene.index * (table.gene_ysep + table.gene_height) self.setPos(x, y) self.colour = DRAWING.DEFAULT_COLOUR self.save_state() @override def boundingRect(self) -> QRectF: return self.rect @override def paint(self, painter: QPainter, *args, **kwargs): """ Paint the domains """ r = self.rect painter.setBrush(self.colour.brush) painter.setPen(self.colour.pen) painter.drawRect(r) is_selected = self.is_selected # Movement is allowed if we have enabled it move_enabled = misc_helper.coalesce( self.options.lego_move_enabled, self.gene_view.model_view.user_move_enabled) # Draw the piano roll unless we're moving if self.options.lego_view_piano_roll is False or move_enabled: draw_piano_roll = False elif self.options.lego_view_piano_roll is None: draw_piano_roll = is_selected else: draw_piano_roll = not is_selected # Draw the selection bars, unless the piano roll is indicative of this already draw_sel_bars = is_selected and not draw_piano_roll # Selection bars # (A blue box inside the gene box) if draw_sel_bars: self.__paint_selection_rect(painter) # Movement bars # (The same as the selection bars but dotted in red and cyan) if move_enabled and is_selected: self.__paint_movement_rect(painter) # Piano roll # (A piano roll for genes) if draw_piano_roll: lookup_table = self.model_view.lookup_table letter_size = lookup_table.letter_size painter.setPen(Qt.NoPen) painter.setBrush( DRAWING.PIANO_ROLL_SELECTED_BACKGROUND if is_selected else DRAWING.PIANO_ROLL_UNSELECTED_BACKGROUND) OFFSET_X = letter_size rect_width = self.rect.width() rect_height = lookup_table.count * letter_size painter.drawRect(0, OFFSET_X, rect_width, rect_height) array = self.domain.site_array if not array: painter.setPen(Pens.RED) painter.drawLine(0, 0, rect_width, rect_height) painter.drawLine(0, rect_height, rect_width, 0) else: for i, c in enumerate(array): pos = lookup_table.letter_order_table.get(c) if pos is not None: painter.setPen( lookup_table.letter_colour_table.get( c, DRAWING.GENE_DEFAULT_FG)) painter.drawEllipse(i * letter_size, pos * letter_size + OFFSET_X, letter_size, letter_size) # Snap-lines, when moving if self.mousemove_snapline: x = self.mousemove_snapline[0] - self.pos().x() y = self.mousemove_snapline[1] - self.pos().y() painter.setPen(DRAWING.SNAP_LINE_2) painter.drawLine(x, self.boundingRect().height() / 2, x, y) painter.setPen(DRAWING.SNAP_LINE) painter.drawLine(x, self.boundingRect().height() / 2, x, y) if not self.mousemove_label.startswith("<"): x -= QFontMetrics(painter.font()).width(self.mousemove_label) if y < 0: y = self.rect.top() - DRAWING.TEXT_MARGIN else: y = self.rect.bottom() + DRAWING.TEXT_MARGIN + QFontMetrics( painter.font()).xHeight() painter.setPen(DRAWING.TEXT_LINE) painter.drawText(QPointF(x, y), self.mousemove_label) # Mouse snapline position elif self.mousemove_label: painter.setPen(DRAWING.TEXT_LINE) painter.drawText( QPointF(self.rect.left() + DRAWING.TEXT_MARGIN, self.rect.top() - DRAWING.TEXT_MARGIN), self.mousemove_label) # Mouse position if not move_enabled: # Positions (when not in move mode) if misc_helper.coalesce(self.options.lego_view_positions, is_selected): # Draw position if self.sibling_previous is None or self.sibling_next is None or self.sibling_previous.rect.width( ) > 32: self.__draw_position(painter) # Domains (when not in move mode) if misc_helper.coalesce(self.options.lego_view_components, is_selected): self.__draw_component_name(painter) def __draw_component_name(self, painter: QPainter): text = ", ".join(str(x) for x in self.components) x = (self.rect.left() + self.rect.right()) / 2 - QFontMetrics( painter.font()).width(text) / 2 y = self.rect.top() - DRAWING.TEXT_MARGIN painter.setPen(DRAWING.COMPONENT_PEN) painter.setBrush(0) painter.drawText(QPointF(x, y), text) def __draw_position(self, painter: QPainter): text = str(self.domain.start) lx = self.rect.left() - QFontMetrics(painter.font()).width(text) / 2 painter.setPen(DRAWING.POSITION_TEXT) painter.drawText(QPointF(lx, self.rect.top() - DRAWING.TEXT_MARGIN), text) def __paint_movement_rect(self, painter: QPainter): r = self.rect MARGIN = 4 painter.setBrush(0) painter.setPen(DRAWING.MOVE_LINE) painter.drawRect(r.left() + MARGIN, r.top() + MARGIN, r.width() - MARGIN * 2, r.height() - MARGIN * 2) painter.setPen(DRAWING.MOVE_LINE_SEL) painter.drawRect(r.left() + MARGIN, r.top() + MARGIN, r.width() - MARGIN * 2, r.height() - MARGIN * 2) # Black start/end when in movement mode if domain isn't adjacent to its siblings if self.sibling_next and self.sibling_next.window_rect().left( ) != self.window_rect().right(): MARGIN = 8 painter.setPen(DRAWING.DISJOINT_LINE) painter.drawLine(r.right(), r.top() - MARGIN, r.right(), r.bottom() + MARGIN) if self.sibling_previous and self.sibling_previous.window_rect().right( ) != self.window_rect().left(): MARGIN = 8 painter.setPen(DRAWING.DISJOINT_LINE) painter.drawLine(r.left(), r.top() - MARGIN, r.left(), r.bottom() + MARGIN) def __paint_selection_rect(self, painter: QPainter): r = self.rect MARGIN = 4 painter.setBrush(0) painter.setPen(DRAWING.SELECTION_LINE) painter.drawRect(r.left() + MARGIN, r.top() + MARGIN, r.width() - MARGIN * 2, r.height() - MARGIN * 2) def __is_draw_position(self, is_selected): return misc_helper.coalesce(self.options.lego_view_positions, is_selected) def __draw_next_sibling_position(self, is_selected): ns = self.sibling_next if ns is None: return False if not ns.__is_draw_position(is_selected): return False return ns.pos().x() == self.window_rect().right() def window_rect(self) -> QRectF: result = self.boundingRect().translated(self.scenePos()) assert result.left() == self.pos().x(), "{} {}".format( self.window_rect().left(), self.pos().x()) # todo: remove assert result.top() == self.pos().y() return result def mousePressEvent(self, m: QGraphicsSceneMouseEvent): """ OVERRIDE Mouse press on domain view i.e. Use clicks a domain """ if m.buttons() & Qt.LeftButton: # Remember the initial position items in case we drag stuff # - do this for all items because it's still possible for the selection to change post-mouse-down for item in self.gene_view.domain_views.values(): item.mousedown_original_pos = item.pos() # If ctrl or meta is down, add to the selection if (m.modifiers() & Qt.ControlModifier) or (m.modifiers() & Qt.MetaModifier): toggle = True else: toggle = False if self.is_selected: # If we are selected stop, this confuses with dragging from a design perspective return self.model_view.handle_domain_clicked(self.domain, toggle) def mouseDoubleClickEvent(self, m: QGraphicsSceneMouseEvent): """ OVERRIDE Double click Just toggles "move enabled" """ self.model_view.user_move_enabled = not self.model_view.user_move_enabled self.model_view.scene.setBackgroundBrush(QBrush(QColor(255, 255, 0))) self.model_view.scene.update() def focusInEvent(self, QFocusEvent): self.setZValue(DRAWING.Z_FOCUS) def focusOutEvent(self, QFocusEvent): self.setZValue(DRAWING.Z_GENE) def snaps(self): for gene_view in self.gene_view.model_view.gene_views.values(): for domain_view in gene_view.domain_views.values(): if domain_view is not self: left_snap = domain_view.scenePos().x() right_snap = domain_view.scenePos().x( ) + domain_view.boundingRect().width() yield left_snap, "Start of {}[{}]".format( domain_view.domain.gene, domain_view.domain.start), domain_view.scenePos().y() yield right_snap, "End of {}[{}]".format( domain_view.domain.gene, domain_view.domain.end), domain_view.scenePos().y() def mouseMoveEvent(self, m: QGraphicsSceneMouseEvent) -> None: if m.buttons() & Qt.LeftButton: if not misc_helper.coalesce( self.options.lego_move_enabled, self.model_view. user_move_enabled) or self.mousedown_original_pos is None: return new_pos: QPointF = self.mousedown_original_pos + ( m.scenePos() - m.buttonDownScenePos(Qt.LeftButton)) new_x = new_pos.x() new_y = new_pos.y() new_x2 = new_x + self.boundingRect().width() self.mousemove_label = "({0} {1})".format(new_pos.x(), new_pos.y()) self.mousemove_snapline = None x_snap_enabled = misc_helper.coalesce( self.options.lego_x_snap, not bool(m.modifiers() & Qt.ControlModifier)) y_snap_enabled = misc_helper.coalesce( self.options.lego_y_snap, not bool(m.modifiers() & Qt.AltModifier)) if x_snap_enabled: for snap_x, snap_label, snap_y in self.snaps(): if (snap_x - 8) <= new_x <= (snap_x + 8): new_x = snap_x self.mousemove_label = "<-- " + snap_label self.mousemove_snapline = snap_x, snap_y break elif (snap_x - 8) <= new_x2 <= (snap_x + 8): new_x = snap_x - self.boundingRect().width() self.mousemove_label = snap_label + " -->" self.mousemove_snapline = snap_x, snap_y break if y_snap_enabled: ysep = self.rect.height() yy = (self.rect.height() + ysep) new_y += yy / 2 new_y = new_y - new_y % yy new_pos.setX(new_x) new_pos.setY(new_y) self.setPos(new_pos) self.save_state() delta_x = new_x - self.mousedown_original_pos.x() delta_y = new_y - self.mousedown_original_pos.y() selected_items = self.model_view.get_selected_userdomain_views() for selected_item in selected_items: if selected_item is not self and selected_item.mousedown_original_pos is not None: selected_item.setPos( selected_item.mousedown_original_pos.x() + delta_x, selected_item.mousedown_original_pos.y() + delta_y) selected_item.save_state() self.model_view.overlay_view.update() def mouseReleaseEvent(self, m: QGraphicsSceneMouseEvent): self.mousemove_label = None self.mousemove_snapline = None self.update() pass # suppress default mouse handling implementation def __repr__(self): return "<<View of '{}' at ({},{})>>".format(self.domain, self.window_rect().left(), self.window_rect().top())
def coerceInside(point: QPointF, rect: QRectF): return QPointF( min(max(point.x(), rect.left()), rect.right()), min(max(point.y(), rect.top()), rect.bottom()), )
def point_is_outside(self, point: QPointF, bounds: QRectF): return point.x() < bounds.left() or point.x() > bounds.right() or point.y() < bounds.top() or point.y() > bounds.bottom()
xoffset = -h if is_fwd else h w = end_poly.boundingRect().width() yoffset = w if is_fwd else -w angle = -90 if is_fwd else 90 T = QTransform() T.translate(next_pt.x()+xoffset, next_pt.y()+yoffset) T.rotate(angle) path.addPolygon(T.map(end_poly)) return path # end def # create hash marks QPainterPaths only once LO_X = BASE_RECT.left() HI_X = BASE_RECT.right() BOT_Y = BASE_RECT.bottom()+BASE_WIDTH/5 TOP_Y = BASE_RECT.top()-BASE_WIDTH/5 WIDTH_X = BASE_WIDTH/3 HEIGHT_Y = BASE_WIDTH/3 FWD_L1 = QPointF(LO_X, BOT_Y) FWD_L2 = QPointF(LO_X+WIDTH_X, BOT_Y) FWD_L3 = QPointF(LO_X+WIDTH_X, BOT_Y-HEIGHT_Y) FWD_H1 = QPointF(HI_X, BOT_Y) FWD_H2 = QPointF(HI_X-WIDTH_X, BOT_Y) FWD_H3 = QPointF(HI_X-WIDTH_X, BOT_Y-HEIGHT_Y) REV_L1 = QPointF(LO_X, TOP_Y) REV_L2 = QPointF(LO_X+WIDTH_X, TOP_Y) REV_L3 = QPointF(LO_X+WIDTH_X, TOP_Y+HEIGHT_Y) REV_H1 = QPointF(HI_X, TOP_Y) REV_H2 = QPointF(HI_X-WIDTH_X, TOP_Y)
def interactiveResize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QPointF """ scene = self.scene() snap = scene.mainwindow.snapToGrid size = scene.GridSize offset = self.handleSize + self.handleMove moved = self.label.moved R = QRectF(self.boundingRect()) D = QPointF(0, 0) minBoundW = self.minwidth + offset * 2 minBoundH = self.minheight + offset * 2 self.prepareGeometryChange() if self.mousePressHandle == self.handleTL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.background.setLeft(R.left()) self.background.setTop(R.top()) self.selection.setLeft(R.left()) self.selection.setTop(R.top()) self.polygon.setLeft(R.left() + offset) self.polygon.setTop(R.top() + offset) elif self.mousePressHandle == self.handleTM: fromY = self.mousePressBound.top() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, -offset, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.background.setTop(R.top()) self.selection.setTop(R.top()) self.polygon.setTop(R.top() + offset) elif self.mousePressHandle == self.handleTR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.background.setRight(R.right()) self.background.setTop(R.top()) self.selection.setRight(R.right()) self.selection.setTop(R.top()) self.polygon.setRight(R.right() - offset) self.polygon.setTop(R.top() + offset) elif self.mousePressHandle == self.handleML: fromX = self.mousePressBound.left() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, -offset, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) self.background.setLeft(R.left()) self.selection.setLeft(R.left()) self.polygon.setLeft(R.left() + offset) elif self.mousePressHandle == self.handleMR: fromX = self.mousePressBound.right() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, +offset, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) self.background.setRight(R.right()) self.selection.setRight(R.right()) self.polygon.setRight(R.right() - offset) elif self.mousePressHandle == self.handleBL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.background.setLeft(R.left()) self.background.setBottom(R.bottom()) self.selection.setLeft(R.left()) self.selection.setBottom(R.bottom()) self.polygon.setLeft(R.left() + offset) self.polygon.setBottom(R.bottom() - offset) elif self.mousePressHandle == self.handleBM: fromY = self.mousePressBound.bottom() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, +offset, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.background.setBottom(R.bottom()) self.selection.setBottom(R.bottom()) self.polygon.setBottom(R.bottom() - offset) elif self.mousePressHandle == self.handleBR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.background.setRight(R.right()) self.background.setBottom(R.bottom()) self.selection.setRight(R.right()) self.selection.setBottom(R.bottom()) self.polygon.setRight(R.right() - offset) self.polygon.setBottom(R.bottom() - offset) self.updateHandles() self.updateTextPos(moved=moved) self.updateAnchors(self.mousePressData, D)
def nextTick(self): self.time += 1 if (self.time >= 180 and self.time <= 300 and self.time % 20 == 0): Enemy1() Enemy2() elif self.time == 360: Enemy3() elif (self.time >= 400 and self.time < 700 and self.time % 20 == 0): Enemy4() elif (self.time >= 800 and self.time < 1100 and self.time % 20 == 0): Enemy5() if self.time == 840: Enemy6() elif (self.time >= 1200 and self.time < 1500 and self.time % 20 == 0): Enemy4(20) elif (self.time >= 1600 and self.time < 1900 and self.time % 20 == 0): Enemy5() if self.time % 30 == 0: Enemy6() elif self.time >= 2000 and self.time < 2810 and self.time % 30 == 0: Enemy7() Enemy8() elif self.time == 2870: Enemy9() elif self.time == 3050: Enemy10(100) elif self.time == 3220: Enemy10(-100) elif self.time == 3400: Enemy10(100) Enemy10(-100) elif self.time == 3700: for i in range(10): Enemy12(i) elif self.time == 3900: for i in range(10): Enemy13(i) elif self.time == 4100: for i in range(10): Enemy12(i) elif self.time == 4300: for i in range(10): Enemy13(i) elif self.time == 4500: Enemy15() else: pass ################################################ enemyComs = Component.components.get('enemy') danComs = Component.components.get('dan') dans = None if danComs is not None: dans = [Entity.entities.get(danCom.id) for danCom in danComs] if enemyComs is None: return removes = [] for enemyCom in enemyComs: enemy = enemyCom.enemy position = enemy.positionCom size = enemy.sizeCom if self.isOut(position, size): removes.append(enemy) continue # 检测Dan碰撞 if dans is None: continue for dan in dans: danSize = dan.sizeCom # 左上角坐标 danPosition = dan.positionCom enemySize = enemy.sizeCom enemyPosition = enemy.positionCom danRect = QRectF(danPosition.x + 14, danPosition.y, danSize.width, danSize.height) enemyRect = QRectF(enemyPosition.x, enemyPosition.y, enemySize.width, enemySize.height) xCo = (danRect.right() > enemyRect.left() and danRect.right() < enemyRect.right()) or ( danRect.left() > enemyRect.left() and danRect.left() < enemyRect.right()) yCo = (danRect.top() > enemyRect.top() and danRect.top() < enemyRect.bottom()) or ( danRect.bottom() > enemyRect.top() and danRect.bottom() < enemyRect.bottom()) co = xCo and yCo if co: # scorexxx self.score.value += 10 dan.destory() enemy.healthCom.decrease() if enemy.healthCom.dead(): self.score.value += 300 removes.append(enemy) enemy_remove = set(removes) for enemy in enemy_remove: enemy.destroy()
class Callout(QGraphicsItem): def __init__(self, chart): super().__init__(chart) self.m_chart = chart self.m_text = "" self.m_textRect = QRectF() self.m_rect = QRectF() self.m_anchor = QPointF() self.m_font = QFont() def boundingRect(self): anchor = self.mapFromParent(self.m_chart.mapToPosition(self.m_anchor)) rect = QRectF() rect.setLeft(min(self.m_rect.left(), anchor.x())) rect.setRight(max(self.m_rect.right(), anchor.x())) rect.setTop(min(self.m_rect.top(), anchor.y())) rect.setBottom(max(self.m_rect.bottom(), anchor.y())) return rect def paint(self, painter, option, widget=None): path = QPainterPath() path.addRoundedRect(self.m_rect, 5, 5) anchor = self.mapFromParent(self.m_chart.mapToPosition(self.m_anchor)) if not self.m_rect.contains(anchor): point1 = QPointF() point2 = QPointF() # establish the position of the anchor point in relation to m_rect above = anchor.y() <= self.m_rect.top() aboveCenter = (anchor.y() > self.m_rect.top() and anchor.y() <= self.m_rect.center().y()) belowCenter = (anchor.y() > self.m_rect.center().y() and anchor.y() <= self.m_rect.bottom()) below = anchor.y() > self.m_rect.bottom() onLeft = anchor.x() <= self.m_rect.left() leftOfCenter = (anchor.x() > self.m_rect.left() and anchor.x() <= self.m_rect.center().x()) rightOfCenter = (anchor.x() > self.m_rect.center().x() and anchor.x() <= self.m_rect.right()) onRight = anchor.x() > self.m_rect.right() # get the nearest m_rect corner. x = (onRight + rightOfCenter) * self.m_rect.width() y = (below + belowCenter) * self.m_rect.height() cornerCase = ((above and onLeft) or (above and onRight) or (below and onLeft) or (below and onRight)) vertical = abs(anchor.x() - x) > abs(anchor.y() - y) x1 = (x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * int(not vertical) * (onLeft * 10 - onRight * 20)) y1 = (y + aboveCenter * 10 - belowCenter * 20 + cornerCase * int(vertical) * (above * 10 - below * 20)) point1.setX(x1) point1.setY(y1) x2 = (x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * int(not vertical) * (onLeft * 20 - onRight * 10)) y2 = (y + aboveCenter * 20 - belowCenter * 10 + cornerCase * int(vertical) * (above * 20 - below * 10)) point2.setX(x2) point2.setY(y2) path.moveTo(point1) path.lineTo(anchor) path.lineTo(point2) path = path.simplified() painter.setBrush(QColor(255, 255, 255)) painter.drawPath(path) painter.drawText(self.m_textRect, self.m_text) def mousePressEvent(self, event): event.setAccepted(True) def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: self.setPos( self.mapToParent(event.pos() - event.buttonDownPos(Qt.LeftButton))) event.setAccepted(True) else: event.setAccepted(False) def setText(self, text): self.m_text = text metrics = QFontMetrics(self.m_font) self.m_textRect = QRectF( metrics.boundingRect(QRect(0, 0, 150, 150), Qt.AlignLeft, self.m_text)) self.m_textRect.translate(5, 5) self.prepareGeometryChange() self.m_rect = self.m_textRect.adjusted(-5, -5, 5, 5) def setAnchor(self, point): self.m_anchor = point def updateGeometry(self): self.prepareGeometryChange() self.setPos( self.m_chart.mapToPosition(self.m_anchor) + QPoint(10, -50))
def paintEvent(self,event): global monster_data global dmg painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.drawPixmap(event.rect(),self.pixmap) if self.card is not None and self.card.ID is not 0: card = self.card # Draw card level at the bottom centered pen = QPen() if np.floor(card.lv) == monster_data[card.ID]['max_level']: lvstr = 'Lv.Max' brush = QBrush(QColor(252,232,131)) else: lvstr = 'Lv.%d' % np.floor(card.lv) brush = QBrush(Qt.white) path = QPainterPath() pen.setWidth(0); pen.setBrush(Qt.black) font = QFont() font.setPointSize(11) font.setWeight(QFont.Black) path.addText(event.rect().x(),event.rect().y()+48,font,lvstr) rect = path.boundingRect() target = (event.rect().x()+event.rect().width())/2 # center the rect in event.rect() path.translate(target-rect.center().x(), 0) painter.setPen(pen) painter.setBrush(QBrush(Qt.black)) painter.drawPath(path.translated(.5,.5)) painter.setPen(pen) painter.setBrush(brush) painter.drawPath(path) # Draw +eggs at the top right eggs = card.plus_atk+card.plus_hp+card.plus_rcv if eggs > 0: eggstr = '+%d' % eggs pen.setBrush(Qt.yellow) brush = QBrush(Qt.yellow) path = QPainterPath() pen.setWidth(0) pen.setBrush(Qt.black) font = QFont() font.setPointSize(11) font.setWeight(QFont.Black) path.addText(event.rect().x(),event.rect().y()+12,font,eggstr) path.translate(50-path.boundingRect().right()-3,0) #painter.setFont(font) painter.setPen(pen) painter.setBrush(QBrush(Qt.black)) painter.drawPath(path.translated(.5,.5)) painter.setPen(pen) painter.setBrush(brush) painter.drawPath(path) #painter.drawText(event.rect().adjusted(0,0,0,0),Qt.AlignRight, eggstr) # Draw awakenings at the top left in a green circle if card.current_awakening > 0: path = QPainterPath() rect = QRectF(event.rect()).adjusted(4,4,-36,-36) path.addEllipse(rect) painter.setBrush(QBrush(QColor(34,139,34))) pen.setBrush(Qt.white) pen.setWidth(1) painter.setPen(pen) painter.drawPath(path) path = QPainterPath() font.setPointSize(9) awkstr = ('%d' % card.current_awakening if card.current_awakening < card.max_awakening else '★') path.addText(rect.x(),rect.bottom(),font,awkstr) br = path.boundingRect() path.translate(rect.center().x()-br.center().x(), rect.center().y()-br.center().y()) pen.setBrush(QColor(0,0,0,0)) pen.setWidth(0) painter.setPen(pen) painter.setBrush(QBrush(Qt.yellow)) painter.drawPath(path) # Draw main attack damage #print(self.main_attack) if self.main_attack > 0: matkstr = '%d' % self.main_attack painter.setBrush(QBrush(COLORS[self.card.element[0]])) path = QPainterPath() font = QFont() font.setFamily('Helvetica') font.setWeight(QFont.Black) #font.setStretch(25) font.setPointSize(13) path.addText(rect.x(),rect.bottom(),font,matkstr) rect = QRectF(event.rect()) br = path.boundingRect() path.translate(rect.center().x()-br.center().x(), rect.center().y()-br.bottom()-1) # pen.setBrush(Qt.black) pen.setWidthF(.75) painter.setPen(pen) painter.drawPath(path) # Draw sub attack damage #print(self.main_attack) if self.sub_attack > 0: satkstr = '%d' % self.sub_attack painter.setBrush(QBrush(COLORS[self.card.element[1]])) path = QPainterPath() font = QFont() font.setFamily('Helvetica') font.setWeight(QFont.Black) #font.setStretch(25) font.setPointSize(12) path.addText(rect.x(),rect.bottom(),font,satkstr) rect = QRectF(event.rect()) br = path.boundingRect() path.translate(rect.center().x()-br.center().x(), rect.center().y()-br.top()+1) # pen.setBrush(Qt.black) pen.setWidthF(.75) painter.setPen(pen) painter.drawPath(path)
class Game(QObject): # 클래스 변수 update_signal = pyqtSignal() gameover_signal = pyqtSignal(int) def __init__(self, w): super().__init__() self.parent = w self.rect = w.rect() # 바둑판 사각형 self.outrect = QRectF(self.rect) gap = 10 self.outrect.adjust(gap, gap, -gap, -gap) # 바둑돌 놓는 사각형 self.inrect = QRectF(self.outrect) gap = 20 self.inrect.adjust(gap, gap, -gap, -gap) self.line = 19 self.size = self.inrect.width() / (self.line - 1) # 바둑돌 self.wdol = [] self.bdol = [] self.bTrun = True # 바둑돌 중간 교차점 x = self.inrect.left() y = self.inrect.top() self.cpt = [[ QPointF(x + (self.size * c), y + (self.size * r)) for c in range(self.line) ] for r in range(self.line)] #print(self.cpt) # 바둑판 상태 저장 0:돌없음, 1:흑돌, 2:백돌 self.state = [[0 for c in range(self.line)] for r in range(self.line)] #print(self.state) # 시그널, 슬롯 self.update_signal.connect(self.parent.update) self.gameover_signal.connect(self.parent.gameOver) def draw(self, qp): b = QBrush(QColor(175, 150, 75)) qp.setBrush(b) qp.drawRect(self.outrect) x = self.inrect.left() y = self.inrect.top() x1 = self.inrect.right() y1 = self.inrect.top() x2 = self.inrect.left() y2 = self.inrect.bottom() # 바둑판 줄 그리기 for i in range(self.line): qp.drawLine(x, y + (self.size * i), x1, y1 + (self.size * i)) qp.drawLine(x + (self.size * i), y, x2 + (self.size * i), y2) # 흑돌 그리기 b = QBrush(Qt.black) qp.setBrush(b) for dol in self.bdol: x = dol.x() - self.size / 2 y = dol.y() - self.size / 2 rect = QRectF(x, y, self.size, self.size) qp.drawEllipse(rect) # 백돌 그리기 b = QBrush(Qt.white) qp.setBrush(b) for dol in self.wdol: x = dol.x() - self.size / 2 y = dol.y() - self.size / 2 rect = QRectF(x, y, self.size, self.size) qp.drawEllipse(rect) def mouseDown(self, x, y): # 바둑판 안에 두었는지? #T = self.inrect.top() #B = self.inrect.bottom() #L = self.inrect.left() #R = self.inrect.right() #if (x>L and x<R) and (y>T and y<B): if self.inrect.contains(QPointF(x, y)): row, col = self.getCP(x, y) print('row:', row, 'col:', col) # 돌이 없으면 if self.state[row][col] == 0: if self.bTrun: self.state[row][col] = 1 self.bdol.append(self.cpt[row][col]) else: self.state[row][col] = 2 self.wdol.append(self.cpt[row][col]) self.bTrun = not self.bTrun self.update_signal.emit() # 판정 0:진행중, 1:흑돌승, 2:백돌승, 3:무승부 result = self.panjung() if result != 0: self.gameover_signal.emit(result) else: QMessageBox.warning(self.parent, '오류', '이미 돌이 있습니다', QMessageBox.Ok) else: QMessageBox.warning(self.parent, '오류', '바둑판 안에 돌을 놓으세요', QMessageBox.Ok) def getCP(self, x, y): s = self.size for r in range(self.line): for c in range(self.line): pt = self.cpt[r][c] _x = pt.x() _y = pt.y() rect = QRectF(_x - s / 2, _y - s / 2, s, s) if rect.contains(QPointF(x, y)): return r, c def panjung(self): # 판정 0:진행중, 1:흑돌승, 2:백돌승, 3:무승부 cnt = 0 for r in range(self.line): for c in range(self.line): # 무승부 if self.state[r][c] != 0: cnt += 1 # 흑돌 가로 판정 if c <= 14: if (self.state[r][c] == 1 and self.state[r][c + 1] == 1 and self.state[r][c + 2] == 1 and self.state[r][c + 3] == 1 and self.state[r][c + 4] == 1): return 1 # 흑돌 세로 판정 if r <= 14: if (self.state[r][c] == 1 and self.state[r + 1][c] == 1 and self.state[r + 2][c] == 1 and self.state[r + 3][c] == 1 and self.state[r + 4][c] == 1): return 1 # 흑돌 대각(좌우) 판정 if r <= 14 and c <= 14: if (self.state[r][c] == 1 and self.state[r + 1][c + 1] == 1 and self.state[r + 2][c + 2] == 1 and self.state[r + 3][c + 3] == 1 and self.state[r + 4][c + 4] == 1): return 1 # 흑돌 대각(우좌) 판정 if r <= 14 and c >= 4: if (self.state[r][c] == 1 and self.state[r + 1][c - 1] == 1 and self.state[r + 2][c - 2] == 1 and self.state[r + 3][c - 3] == 1 and self.state[r + 4][c - 4] == 1): return 1 # 백돌 가로 판정 if c <= 14: if (self.state[r][c] == 2 and self.state[r][c + 1] == 2 and self.state[r][c + 2] == 2 and self.state[r][c + 3] == 2 and self.state[r][c + 4] == 2): return 2 # 백돌 세로 판정 if r <= 14: if (self.state[r][c] == 2 and self.state[r + 1][c] == 2 and self.state[r + 2][c] == 2 and self.state[r + 3][c] == 2 and self.state[r + 4][c] == 2): return 2 # 백돌 대각(좌우) 판정 if r <= 14 and c <= 14: if (self.state[r][c] == 2 and self.state[r + 1][c + 1] == 2 and self.state[r + 2][c + 2] == 2 and self.state[r + 3][c + 3] == 2 and self.state[r + 4][c + 4] == 2): return 2 # 백돌 대각(우좌) 판정 if r <= 14 and c >= 4: if (self.state[r][c] == 2 and self.state[r + 1][c - 1] == 2 and self.state[r + 2][c - 2] == 2 and self.state[r + 3][c - 3] == 2 and self.state[r + 4][c - 4] == 2): return 2 if cnt == self.line * self.line: return 3 return 0
w = end_poly.boundingRect().width() yoffset = w if is_fwd else -w angle = -90 if is_fwd else 90 T = QTransform() T.translate(next_pt.x() + xoffset, next_pt.y() + yoffset) T.rotate(angle) path.addPolygon(T.map(end_poly)) return path # end def # create hash marks QPainterPaths only once LO_X = BASE_RECT.left() HI_X = BASE_RECT.right() BOT_Y = BASE_RECT.bottom() + BASE_WIDTH / 5 TOP_Y = BASE_RECT.top() - BASE_WIDTH / 5 WIDTH_X = BASE_WIDTH / 3 HEIGHT_Y = BASE_WIDTH / 3 FWD_L1 = QPointF(LO_X, BOT_Y) FWD_L2 = QPointF(LO_X + WIDTH_X, BOT_Y) FWD_L3 = QPointF(LO_X + WIDTH_X, BOT_Y - HEIGHT_Y) FWD_H1 = QPointF(HI_X, BOT_Y) FWD_H2 = QPointF(HI_X - WIDTH_X, BOT_Y) FWD_H3 = QPointF(HI_X - WIDTH_X, BOT_Y - HEIGHT_Y) REV_L1 = QPointF(LO_X, TOP_Y) REV_L2 = QPointF(LO_X + WIDTH_X, TOP_Y) REV_L3 = QPointF(LO_X + WIDTH_X, TOP_Y + HEIGHT_Y) REV_H1 = QPointF(HI_X, TOP_Y) REV_H2 = QPointF(HI_X - WIDTH_X, TOP_Y)