def compute_tag_rects_with_range(self, lt: QPoint, height: int, tags_range: Tuple[int, int]) -> None: for tag_index in range(*tags_range): i_width = self.fontMetrics().horizontalAdvance( self.tags[tag_index].text) i_r = QRectF(lt, QSizeF(i_width, height)) i_r.translate(TAG_TEXT_HORIZONTAL_PADDING, 0) i_r.adjust( -TAG_TEXT_HORIZONTAL_PADDING, 0, TAG_TEXT_HORIZONTAL_PADDING + TAG_CROSS_LEFT_PADDING + TAG_CROSS_RIGHT_PADDING + TAG_CROSS_WIDTH, 0, ) # Check if we overflow and if so, move this tag to the next line in the input field. input_rect = self.input_field_rect() if i_r.topRight().x() >= input_rect.topRight().x(): i_r.setRect(input_rect.x(), i_r.y() + TAG_HEIGHT + TAG_VERTICAL_MARGIN, i_r.width(), i_r.height()) lt.setY(lt.y() + TAG_HEIGHT + TAG_VERTICAL_MARGIN) lt.setX(int(i_r.right() + TAG_HORIZONTAL_MARGIN)) self.tags[tag_index].rect = i_r
def drawRow(row, sx, sy, last_end=False): x = sx y = sy keys = row rw = w - sx i = 0 for k in keys: rect = QRectF(x, y, kw, kw) if i == len(keys) - 1 and last_end: rect.setWidth(rw) p.drawRoundedRect(rect, rx, rx) p.setPen(Qt.black) rect.adjust(5, 1, 0, 0) p.setFont(self.lowerFont) p.drawText( rect, Qt.AlignLeft | Qt.AlignBottom, self.regular_text(k)) p.setFont(self.upperFont) p.drawText( rect, Qt.AlignLeft | Qt.AlignTop, self.shift_text(k)) rw = rw - space - kw x = x + space + kw i = i + 1 p.setPen(pen) return (x, rw)
def draw_bg(self, qp, rect, text): path = QPainterPath() # add container path.addRoundedRect(rect, 4, 4) if self.isEnabled(): highlight_color, bg_color, text_color = ( self.settings['highlight'], self.settings['bg'], self.settings['text']) else: highlight_color, bg_color, text_color = ( self.settings['highlight_disabled'], self.settings['bg_disabled'], self.settings['text_disabled']) qp.setPen(QPen(highlight_color, 2)) qp.fillPath(path, bg_color) # add close button circle_size = rect.height() / 1.8 pen_size = 2 qp.setPen(QPen(text_color, pen_size, Qt.SolidLine)) rect = QRectF( rect.right() - circle_size - self.settings['padding-x'] / 2, rect.top() + (rect.height() - circle_size) / 2, circle_size, circle_size) path.addEllipse(rect) qp.drawPath(path) # draw cross inside_rect = QRectF(rect) inside_rect.adjust(pen_size, pen_size, -pen_size, -pen_size) qp.drawLine(inside_rect.topLeft(), inside_rect.bottomRight()) qp.drawLine(inside_rect.bottomLeft(), inside_rect.topRight()) self.close_rectangles[text] = rect
def updateSceneRect(self): mapSize = self.mMapDocument.renderer().mapSize() sceneRect = QRectF(0, 0, mapSize.width(), mapSize.height()) margins = self.mMapDocument.map().computeLayerOffsetMargins() sceneRect.adjust(-margins.left(), -margins.top(), margins.right(), margins.bottom()) self.setSceneRect(sceneRect) self.mDarkRectangle.setRect(sceneRect)
def updateSceneRect(self): mapSize = self.mMapDocument.renderer().mapSize() sceneRect = QRectF(0, 0, mapSize.width(), mapSize.height()) margins = self.mMapDocument.map().computeLayerOffsetMargins() sceneRect.adjust(-margins.left(), -margins.top(), margins.right(), margins.bottom()) self.setSceneRect(sceneRect) self.mDarkRectangle.setRect(sceneRect)
class PolygonBase(QGraphicsWidget): def __init__(self): super().__init__() self.MAR = 50 self.points = [] self.top_left = QPointF(float('Inf'), float('Inf')) self.bottom_right = QPointF(-float('Inf'), -float('Inf')) self.rect = QRectF() def boundingRect(self): return self.rect def update_bounding_rect(self, pt): """插入新顶点时,更新 bounding rect Args: pt (QPointF): 新插入的顶点 """ self.top_left = QPointF(min(self.top_left.x(), pt.x()), min(self.top_left.y(), pt.y())) self.bottom_right = QPointF(max(self.bottom_right.x(), pt.x()), max(self.bottom_right.y(), pt.y())) self.rect = QRectF(self.top_left, self.bottom_right).adjusted(-self.MAR, -self.MAR, self.MAR, self.MAR) self.prepareGeometryChange() def move_bounding_rect(self, offset): """移动多边形时,更新 bounding rect Args: offset (QPointF): 平移向量 """ self.top_left += offset self.bottom_right += offset self.rect.adjust(offset.x(), offset.y(), offset.x(), offset.y()) self.prepareGeometryChange() def get_points(self): """获取多边形中的顶点列表 Returns: points (list[QPointF]): 顶点列表 """ return self.points def get_vertices(self): """获取多边形中的顶点列表 Returns: vertices (list[list[float]]): 顶点列表 """ vertices = [[vertex.x(), vertex.y()] for vertex in self.points] return vertices
def paintEvent(self, event): QFrame.paintEvent(self, event) text = QDateTime.currentDateTime().toString(Qt.SystemLocaleLongDate) logicalRect = QRectF( QPointF(0, 0), QSizeF(QFontMetrics(self.font()).size(Qt.TextSingleLine, text))) physicalRect, frameWidth = QRectF(self.rect()), self.frameWidth() physicalRect.adjust(frameWidth, frameWidth, -frameWidth, -frameWidth) scaleForWidth = physicalRect.width() / logicalRect.width() scaleForHeight = physicalRect.height() / logicalRect.height() logicalRect.moveTo(frameWidth / scaleForWidth, frameWidth / scaleForHeight) painter = QStylePainter(self) painter.scale(scaleForWidth, scaleForHeight) painter.drawText(logicalRect, Qt.AlignCenter, text)
def paint(self, painter, option, widget): painter.save() painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL)) rect = QRectF(0, 0, self.p_width, self.p_height) # Draw rectangle pen = QPen(canvas.theme.box_pen_sel if self.isSelected() else canvas. theme.box_pen) pen.setWidthF(pen.widthF() + 0.00001) painter.setPen(pen) lineHinting = pen.widthF() / 2 if canvas.theme.box_bg_type == Theme.THEME_BG_GRADIENT: box_gradient = QLinearGradient(0, 0, 0, self.p_height) box_gradient.setColorAt(0, canvas.theme.box_bg_1) box_gradient.setColorAt(1, canvas.theme.box_bg_2) painter.setBrush(box_gradient) else: painter.setBrush(canvas.theme.box_bg_1) rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting) painter.drawRect(rect) # Draw plugin inline display if supported self.paintInlineDisplay(painter) # Draw pixmap header rect.setHeight(canvas.theme.box_header_height) if canvas.theme.box_header_pixmap: painter.setPen(Qt.NoPen) painter.setBrush(canvas.theme.box_bg_2) # outline rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting) painter.drawRect(rect) rect.adjust(1, 1, -1, 0) painter.drawTiledPixmap(rect, canvas.theme.box_header_pixmap, rect.topLeft()) # Draw text painter.setFont(self.m_font_name) if self.isSelected(): painter.setPen(canvas.theme.box_text_sel) else: painter.setPen(canvas.theme.box_text) if canvas.theme.box_use_icon: textPos = QPointF(25, canvas.theme.box_text_ypos) else: appNameSize = fontHorizontalAdvance(self.m_font_name, self.m_group_name) rem = self.p_width - appNameSize textPos = QPointF(rem / 2, canvas.theme.box_text_ypos) painter.drawText(textPos, self.m_group_name) self.repaintLines() painter.restore()
class EdgeItem(QGraphicsItem): LINE_WIDTH = 1 OFFSET = 8 # 方向线偏离中心线的距离 MIN_ARROW_WIDTH, MAX_ARROW_WIDTH = 1, 8 double_click_callback = EMPTY_FUNC def __init__(self, edge_id): super().__init__() self.setZValue(1) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) self.setAcceptHoverEvents(True) self.edge_id = edge_id self.text_item = QGraphicsSimpleTextItem('', self) self.text_item.setZValue(4) self.style = { 'name': f'Edge{edge_id}', 'color': Qt.black, 'width': 0.5, # 0~1 的中间值 'line': Qt.SolidLine, 'show_arrow': False, 'text': '', 'text_color': Qt.black, 'show_text': False, } self.hover = False def type(self): return QGraphicsItem.UserType + abs(hash(EdgeItem)) def boundingRect(self): return self.bounding_rect def shape(self): path = QPainterPath() path.addPolygon(self.shape_polygon) path.closeSubpath() return path # ------------------------------------------------------------------------- def adjust(self, src_p: QPointF, dst_p: QPointF): self.angle = getAngle(src_p, dst_p) self.src_p = src_p self.arrow_p = (src_p + 2 * dst_p) / 3 # 箭头开始位置, 前端2/3处 self.dst_p = dst_p W1 = 1 * self.OFFSET W2 = 2 * self.OFFSET W3 = 3 * self.OFFSET vec = getRightOffsetVector(self.angle) self.arrow_polygon = QPolygonF([ src_p + vec * W1, dst_p + vec * W1, self.arrow_p + vec * W2, self.arrow_p + vec * W1 ]) self.shape_polygon = QPolygonF( [src_p, src_p + vec * W2, dst_p + vec * W2, dst_p]) self.bounding_rect = QRectF(src_p, dst_p).normalized() # normalized 正方向 self.bounding_rect.adjust(-W3, -W3, W3, W3) self.text_p = ((src_p + dst_p) / 2) + vec * W1 self.text_item.setPos(self.text_p) self.prepareGeometryChange() # ------------------------------------------------------------------------- def paint(self, painter, option, widget=None): if self.style['show_arrow'] or self.hover: width = threshold(0.0, self.style['width'], 1.0) width = width * (self.MAX_ARROW_WIDTH - self.MIN_ARROW_WIDTH) + self.MIN_ARROW_WIDTH painter.setPen(QPen(self.style['color'], width, self.style['line'])) painter.setBrush(self.style['color']) painter.drawPolygon(self.arrow_polygon) else: # TODO 定制线类型 虚线或实线 painter.setPen(QPen(Qt.black, self.LINE_WIDTH)) painter.drawLine(self.src_p, self.dst_p) if (self.style['show_arrow'] and self.style['show_text']) or self.hover: self.text_item.setPen(self.style['text_color']) self.text_item.setText( f"{self.style['name']}\n{self.style['text']}") self.text_item.show() else: self.text_item.hide() # ------------------------------------------------------------------------- def mouseDoubleClickEvent(self, event): self.double_click_callback(self.edge_id) super().mouseDoubleClickEvent(event) def hoverEnterEvent(self, event): self.hover = True self.update() super().hoverEnterEvent(event) def hoverLeaveEvent(self, event): self.hover = False self.update() super().hoverLeaveEvent(event) # ------------------------------------------------------------------------- def setStyle(self, style) -> None: for key in self.style: try: self.style[key] = style[key] except KeyError: pass self.update()
class BrushItem(QGraphicsItem): ## # Constructor. ## def __init__(self): super().__init__() self.mMapDocument = None self.mBoundingRect = QRectF() self.mRegion = QRegion() self.setFlag(QGraphicsItem.ItemUsesExtendedStyleOption) ## # Sets the map document this brush is operating on. ## def setMapDocument(self, mapDocument): if (self.mMapDocument == mapDocument): return self.mMapDocument = mapDocument # The tiles in the stamp may no longer be valid self.clear() ## # Clears the tile layer and region set on this item. ## def clear(self): self.setTileLayer(None) ## # Sets a tile layer representing this brush. When no tile layer is set, # the brush only draws the selection color. # # The BrushItem does not take ownership over the tile layer, but makes a # personal copy of the tile layer. ## def setTileLayer(self, tileLayer): self.mTileLayer = tileLayer if (tileLayer): self.mRegion = tileLayer.region() else: self.mRegion = QRegion() self.updateBoundingRect() self.update() ## # Returns the current tile layer. ## def tileLayer(self): return self.mTileLayer ## # Changes the position of the tile layer, if one is set. ## def setTileLayerPosition(self, pos): if (not self.mTileLayer): return oldPosition = QPoint(self.mTileLayer.x(), self.mTileLayer.y()) if (oldPosition == pos): return self.mRegion.translate(pos - oldPosition) self.mTileLayer.setX(pos.x()) self.mTileLayer.setY(pos.y()) self.updateBoundingRect() ## # Sets the region of tiles that this brush item occupies. ## def setTileRegion(self, region): if type(region) != QRegion: region = QRegion(region) if (self.mRegion == region): return self.mRegion = region self.updateBoundingRect() ## # Sets the layer offset used by the currently active layer. ## def setLayerOffset(self, offset): self.setPos(offset) ## # Returns the region of the current tile layer or the region that was set # using setTileRegion. ## def tileRegion(self): return self.mRegion # QGraphicsItem def boundingRect(self): return self.mBoundingRect def paint(self, painter, option, widget=None): insideMapHighlight = QApplication.palette().highlight().color() insideMapHighlight.setAlpha(64) outsideMapHighlight = QColor(255, 0, 0, 64) mapWidth = self.mMapDocument.map().width() mapHeight = self.mMapDocument.map().height() mapRegion = QRegion(0, 0, mapWidth, mapHeight) insideMapRegion = self.mRegion.intersected(mapRegion) outsideMapRegion = self.mRegion.subtracted(mapRegion) renderer = self.mMapDocument.renderer() if (self.mTileLayer): opacity = painter.opacity() painter.setOpacity(0.75) renderer.drawTileLayer(painter, self.mTileLayer, option.exposedRect) painter.setOpacity(opacity) renderer.drawTileSelection(painter, insideMapRegion, insideMapHighlight, option.exposedRect) renderer.drawTileSelection(painter, outsideMapRegion, outsideMapHighlight, option.exposedRect) def updateBoundingRect(self): self.prepareGeometryChange() if (not self.mMapDocument): self.mBoundingRect = QRectF() return bounds = self.mRegion.boundingRect() self.mBoundingRect = QRectF( self.mMapDocument.renderer().boundingRect(bounds)) # Adjust for amount of pixels tiles extend at the top and to the right if (self.mTileLayer): map = self.mMapDocument.map() drawMargins = self.mTileLayer.drawMargins() drawMargins.setTop(drawMargins.top() - map.tileHeight()) drawMargins.setRight(drawMargins.right() - map.tileWidth()) # Since we're also drawing a tile selection, we should not apply # negative margins self.mBoundingRect.adjust(min(0, -drawMargins.left()), min(0, -drawMargins.top()), max(0, drawMargins.right()), max(0, drawMargins.bottom()))
class BrushItem(QGraphicsItem): ## # Constructor. ## def __init__(self): super().__init__() self.mMapDocument = None self.mBoundingRect = QRectF() self.mRegion = QRegion() self.setFlag(QGraphicsItem.ItemUsesExtendedStyleOption) ## # Sets the map document this brush is operating on. ## def setMapDocument(self, mapDocument): if (self.mMapDocument == mapDocument): return self.mMapDocument = mapDocument # The tiles in the stamp may no longer be valid self.clear() ## # Clears the tile layer and region set on this item. ## def clear(self): self.setTileLayer(None) ## # Sets a tile layer representing this brush. When no tile layer is set, # the brush only draws the selection color. # # The BrushItem does not take ownership over the tile layer, but makes a # personal copy of the tile layer. ## def setTileLayer(self, tileLayer): self.mTileLayer = tileLayer if (tileLayer): self.mRegion = tileLayer.region() else: self.mRegion = QRegion() self.updateBoundingRect() self.update() ## # Returns the current tile layer. ## def tileLayer(self): return self.mTileLayer ## # Changes the position of the tile layer, if one is set. ## def setTileLayerPosition(self, pos): if (not self.mTileLayer): return oldPosition = QPoint(self.mTileLayer.x(), self.mTileLayer.y()) if (oldPosition == pos): return self.mRegion.translate(pos - oldPosition) self.mTileLayer.setX(pos.x()) self.mTileLayer.setY(pos.y()) self.updateBoundingRect() ## # Sets the region of tiles that this brush item occupies. ## def setTileRegion(self, region): if type(region)!=QRegion: region = QRegion(region) if (self.mRegion == region): return self.mRegion = region self.updateBoundingRect() ## # Sets the layer offset used by the currently active layer. ## def setLayerOffset(self, offset): self.setPos(offset) ## # Returns the region of the current tile layer or the region that was set # using setTileRegion. ## def tileRegion(self): return self.mRegion # QGraphicsItem def boundingRect(self): return self.mBoundingRect def paint(self, painter, option, widget = None): insideMapHighlight = QApplication.palette().highlight().color() insideMapHighlight.setAlpha(64) outsideMapHighlight = QColor(255, 0, 0, 64) mapWidth = self.mMapDocument.map().width() mapHeight = self.mMapDocument.map().height() mapRegion = QRegion(0, 0, mapWidth, mapHeight) insideMapRegion = self.mRegion.intersected(mapRegion) outsideMapRegion = self.mRegion.subtracted(mapRegion) renderer = self.mMapDocument.renderer() if (self.mTileLayer): opacity = painter.opacity() painter.setOpacity(0.75) renderer.drawTileLayer(painter, self.mTileLayer, option.exposedRect) painter.setOpacity(opacity) renderer.drawTileSelection(painter, insideMapRegion, insideMapHighlight, option.exposedRect) renderer.drawTileSelection(painter, outsideMapRegion, outsideMapHighlight, option.exposedRect) def updateBoundingRect(self): self.prepareGeometryChange() if (not self.mMapDocument): self.mBoundingRect = QRectF() return bounds = self.mRegion.boundingRect() self.mBoundingRect = QRectF(self.mMapDocument.renderer().boundingRect(bounds)) # Adjust for amount of pixels tiles extend at the top and to the right if (self.mTileLayer): map = self.mMapDocument.map() drawMargins = self.mTileLayer.drawMargins() drawMargins.setTop(drawMargins.top() - map.tileHeight()) drawMargins.setRight(drawMargins.right() - map.tileWidth()) # Since we're also drawing a tile selection, we should not apply # negative margins self.mBoundingRect.adjust(min(0, -drawMargins.left()), min(0, -drawMargins.top()), max(0, drawMargins.right()), max(0, drawMargins.bottom()))
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