|
|
import math
|
|
|
from PySide6.QtCore import Qt, Signal, QPointF, QObject
|
|
|
from PySide6.QtGui import QPen, QBrush, QColor, QPainterPath
|
|
|
from PySide6.QtWidgets import QGraphicsEllipseItem, QGraphicsPathItem, \
|
|
|
QGraphicsTextItem, QGraphicsItem,QGraphicsLineItem
|
|
|
|
|
|
|
|
|
class CustomLineItem(QGraphicsPathItem, QObject):
|
|
|
pressMouse = Signal(object, int) # 定义鼠标按下信号
|
|
|
|
|
|
def __init__(self, sx, sy, ex, ey, edge_id, is_path=False, seq=2, parent=None, **kwargs):
|
|
|
QGraphicsPathItem.__init__(self, parent)
|
|
|
QObject.__init__(self, **kwargs)
|
|
|
|
|
|
self.x = (ex + sx) // 2
|
|
|
self.y = (ey + sy) // 2
|
|
|
self.id = edge_id
|
|
|
self.seq = seq
|
|
|
|
|
|
if ex - sx != 0:
|
|
|
m = (ey - sy) / (ex - sx) # 求得线段的斜率
|
|
|
self.angle_rad = math.atan(m) # 求弧度
|
|
|
self.angle = math.degrees(self.angle_rad) # 计算度数
|
|
|
else:
|
|
|
self.angle_rad = math.pi / 2
|
|
|
self.angle = 90
|
|
|
|
|
|
self.off_x_center = math.cos(self.angle_rad + math.pi / 2) * 30
|
|
|
self.off_y_center = math.sin(self.angle_rad + math.pi / 2) * 30
|
|
|
|
|
|
self.pen_line = QPen(QColor(200, 200, 200, 100))
|
|
|
self.pen_line.setWidth(3)
|
|
|
|
|
|
self.selected_pen_line = QPen(QColor(100, 255, 100, 255))
|
|
|
self.selected_pen_line.setWidth(3)
|
|
|
|
|
|
if is_path: # 如果是路径
|
|
|
self.pen_line = QPen(QColor(0, 200, 200, 100))
|
|
|
self.pen_line.setWidth(5) # 设置画笔的宽度为5
|
|
|
|
|
|
# 创建画笔路径
|
|
|
path = QPainterPath()
|
|
|
if self.seq == 0:
|
|
|
path.moveTo(sx, sy) # 设置起点
|
|
|
path.lineTo(ex, ey) # 设置终点
|
|
|
elif self.seq == 1:
|
|
|
path.moveTo(sx, sy) # 设置路径的起点
|
|
|
path.lineTo(self.x+self.off_x_center, self.y+self.off_y_center) # 设置路径的终点
|
|
|
path.lineTo(ex, ey) # 设置终点
|
|
|
elif self.seq == 2:
|
|
|
path.moveTo(sx, sy) # 设置路径的起点
|
|
|
path.lineTo(self.x-self.off_x_center, self.y-self.off_y_center) # 设置路径的终点
|
|
|
path.lineTo(ex, ey) # 设置终点
|
|
|
# 将画笔路径设置给GraphicsPathItem
|
|
|
self.setPath(path) # 设置路径
|
|
|
self.setPen(self.pen_line) # 设置画笔
|
|
|
self.setZValue(-1) # 设置z轴坐标
|
|
|
self.setFlag(QGraphicsItem.ItemIsSelectable, True) # 将连线设置为可选
|
|
|
|
|
|
# self.set_text("ID: " + str(self.id))
|
|
|
|
|
|
def set_text(self, text):
|
|
|
# 清除之前的文本
|
|
|
if hasattr(self, 'text_item'):
|
|
|
self.scene().removeItem(self.text_item)
|
|
|
del self.text_item
|
|
|
# 添加文本
|
|
|
self.text_item = QGraphicsTextItem(text, parent=self)
|
|
|
# 修改字体属性
|
|
|
font = self.text_item.font()
|
|
|
font.setPointSize(12) # 设置字体大小
|
|
|
self.text_item.setFont(font)
|
|
|
# 计算理想的文本位置
|
|
|
tw = self.text_item.boundingRect().width()/2
|
|
|
th = self.text_item.boundingRect().height()/2
|
|
|
self.text_item.setRotation(self.angle) # 调整显示角度
|
|
|
# 调整路径信息显示方向和位置
|
|
|
ly = tw*math.sin(self.angle_rad)
|
|
|
lx = tw*math.cos(self.angle_rad)
|
|
|
my = th*math.sin(self.angle_rad+math.pi/2)
|
|
|
mx = th*math.cos(self.angle_rad+math.pi/2)
|
|
|
if self.seq == 0:
|
|
|
self.text_item.setPos(self.x-lx-mx ,self.y-ly-my)
|
|
|
elif self.seq == 1:
|
|
|
self.text_item.setPos(self.x+self.off_x_center-lx-mx, self.y+self.off_y_center-ly-my)
|
|
|
elif self.seq == 2:
|
|
|
self.text_item.setPos(self.x-self.off_x_center-lx-mx, self.y-self.off_y_center-ly-my)
|
|
|
|
|
|
|
|
|
def mousePressEvent(self, event):
|
|
|
print("连接线")
|
|
|
if event.button() == Qt.LeftButton: # 如果是鼠标左键按下
|
|
|
self.pressMouse.emit(event, self.id) # 鼠标左键按下时发送鼠标坐标信号和标签
|
|
|
for item in self.scene().items(): # 遍历场景中的所有图形项
|
|
|
if isinstance(item, (CustomLineItem, CustomEllipseItem)) and item.isSelected(): # 如果是线或者圆形并且被选中
|
|
|
item.setSelected(False) # 取消选中
|
|
|
self.setSelected(True) # 选中当前图形项
|
|
|
|
|
|
def itemChange(self, change, value):
|
|
|
if change == QGraphicsItem.ItemSelectedChange:
|
|
|
if value:
|
|
|
self.setPen(self.selected_pen_line)
|
|
|
else:
|
|
|
self.setPen(self.pen_line)
|
|
|
return super().itemChange(change, value)
|
|
|
|
|
|
# 取消选中时恢复以前的状态
|
|
|
|
|
|
|
|
|
class CustomEllipseItem(QGraphicsEllipseItem, QObject):
|
|
|
# 圆形对象
|
|
|
pressMouse = Signal(object, int) # 定义鼠标按下信号
|
|
|
releaseMouse = Signal(object) # 定义鼠标释放信号
|
|
|
collideMouse = Signal(int, int) # 定义碰撞信号发送自身节点的id和连线碰撞的节点id
|
|
|
|
|
|
def __init__(self, x, y, id, text, is_must=False, radius=18, parent=None, **kwargs):
|
|
|
'''
|
|
|
:param radius: 半径
|
|
|
:param color: 颜色带透明度
|
|
|
'''
|
|
|
super().__init__(x - radius, y - radius, radius * 2, radius * 2, parent)
|
|
|
QObject.__init__(self, **kwargs) # 为了获得自定义信号和槽的能力
|
|
|
|
|
|
pen_circle = QPen(QColor(0, 0, 0, 255)) # 黑色
|
|
|
pen_circle.setWidth(0) # 设置画笔的宽度为0
|
|
|
self.brush_circle = QBrush(QColor(255, 255, 0, 255)) # 创建一个黄色色且透明度为255的刷子
|
|
|
|
|
|
if is_must: # 必经点设置为红色
|
|
|
self.brush_circle = QBrush(QColor(255, 100, 100, 255)) # 创建一个红色且透明度为100的刷子
|
|
|
|
|
|
self.setPen(pen_circle)
|
|
|
self.setBrush(self.brush_circle)
|
|
|
self.x = x
|
|
|
self.y = y
|
|
|
self.text = str(text)
|
|
|
self.id = id
|
|
|
# self.setFlag(QGraphicsEllipseItem.ItemIsMovable, True)
|
|
|
self.setFlag(QGraphicsEllipseItem.ItemIsSelectable, True) # 设置为可选
|
|
|
# 设置鼠标悬停时的光标为抓手
|
|
|
self.setAcceptHoverEvents(True) # 设置接受鼠标悬停事件
|
|
|
self.set_text(self.text) # 设置文本
|
|
|
self.mouse_pressed = False # 鼠标按下标志
|
|
|
self.line_item = None # 初始化线
|
|
|
|
|
|
def set_text(self, text):
|
|
|
# 清除之前的文本
|
|
|
if hasattr(self, 'text_item'):
|
|
|
self.scene().removeItem(self.text_item)
|
|
|
del self.text_item
|
|
|
self.text_item = QGraphicsTextItem(text, parent=self) # 添加文本
|
|
|
font = self.text_item.font() # 修改字体属性
|
|
|
font.setPointSize(20) # 设置字体大小
|
|
|
self.text_item.setFont(font) # 设置格式
|
|
|
self.text_item.setPos(self.x - self.text_item.boundingRect().width() / 2, # 设置位置
|
|
|
self.y - self.text_item.boundingRect().height() / 2)
|
|
|
|
|
|
def __str__(self):
|
|
|
return f"圆形对象:x={self.x}, y={self.y}"
|
|
|
|
|
|
def mousePressEvent(self, event): # 鼠标按下执行
|
|
|
if event.button() == Qt.LeftButton:
|
|
|
self.pressMouse.emit(event, self.id) # 鼠标左键按下时发送鼠标坐标信号和标签
|
|
|
|
|
|
self.mouse_pressed = True
|
|
|
|
|
|
for item in self.scene().items():
|
|
|
if isinstance(item, (CustomLineItem, CustomEllipseItem)) and item.isSelected():
|
|
|
item.setSelected(False)
|
|
|
self.setSelected(True)
|
|
|
|
|
|
self.setSelected(True)
|
|
|
|
|
|
def mouseMoveEvent(self, event):
|
|
|
if self.mouse_pressed:
|
|
|
# 鼠标按住的情况下更新直线
|
|
|
mouse_x = event.scenePos().x()
|
|
|
mouse_y = event.scenePos().y()
|
|
|
|
|
|
if self.line_item is not None: # 如果存在
|
|
|
self.scene().removeItem(self.line_item) # 删除线
|
|
|
|
|
|
pen_circle = QPen(QColor(200, 200, 200, 255))
|
|
|
pen_circle.setWidth(2)
|
|
|
self.line_item = QGraphicsLineItem(self.x, self.y, mouse_x, mouse_y)
|
|
|
# print(mouse_x, mouse_y) # 鼠标移动时的坐标
|
|
|
self.line_item.setPen(pen_circle)
|
|
|
self.line_item.setZValue(-1)
|
|
|
self.scene().addItem(self.line_item) # 重新画线
|
|
|
|
|
|
# 获取线的端点坐标
|
|
|
# line_start = QPointF(self.x, self.y)
|
|
|
line_end = QPointF(mouse_x, mouse_y)
|
|
|
|
|
|
# 检查线的端点是否与其他图形项碰撞
|
|
|
for item in self.scene().items():
|
|
|
if isinstance(item, CustomEllipseItem) and item != self:
|
|
|
if item.contains(line_end): # 如果线的端点与其他图形项碰撞
|
|
|
# print(f"Line endpoint collides with {item}")
|
|
|
self.collideMouse.emit(self.id, item.id)
|
|
|
|
|
|
def mouseReleaseEvent(self, event): # 鼠标释放执行
|
|
|
if event.button() == Qt.LeftButton:
|
|
|
self.mouse_pressed = False
|
|
|
# 鼠标松开时删除直线
|
|
|
if self.line_item is not None:
|
|
|
self.scene().removeItem(self.line_item)
|
|
|
self.line_item = None
|
|
|
|
|
|
self.releaseMouse.emit(event)
|
|
|
|
|
|
def hoverEnterEvent(self, event):
|
|
|
# 鼠标悬停时设置光标为抓手
|
|
|
self.setCursor(Qt.PointingHandCursor)
|
|
|
# 鼠标悬停时显示带背景的文字
|
|
|
info_text = QGraphicsTextItem(f"{self.id}-({self.x},{self.y})", self)
|
|
|
info_text.setPos(self.x - 20, self.y + 20)
|
|
|
self.info_text = info_text
|
|
|
|
|
|
def hoverLeaveEvent(self, event):
|
|
|
# 鼠标离开时恢复光标
|
|
|
self.unsetCursor()
|
|
|
# 鼠标离开时移除信息
|
|
|
if hasattr(self, 'info_text'):
|
|
|
self.scene().removeItem(self.info_text)
|
|
|
del self.info_text
|
|
|
|
|
|
def contextMenuEvent(self, event):
|
|
|
# 在右键点击时选中图形项
|
|
|
self.setSelected(True)
|
|
|
event.accept()
|
|
|
|
|
|
def itemChange(self, change, value):
|
|
|
if change == QGraphicsItem.ItemSelectedChange:
|
|
|
if value:
|
|
|
self.setBrush(QBrush(QColor(100, 255, 100, 255))) # 选中时设置为绿色
|
|
|
else:
|
|
|
self.setBrush(self.brush_circle) # 取消选中时恢复原来的颜色
|
|
|
return super().itemChange(change, value)
|