StoryCheckTools/graph/directed_acyclic_graph/DAGPresent.py

204 lines
5.9 KiB
Python
Raw Normal View History

2024-07-30 23:57:17 +00:00
from graph.directed_acyclic_graph.DAGLayout import DAGGraph, DAGOrderHelper
2024-07-30 15:52:02 +00:00
from graph.DataType import Arrow, Point
from typing import List
from PyQt5.QtWidgets import QWidget, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem
2024-07-30 23:57:17 +00:00
from PyQt5.QtCore import QRectF, Qt, QPointF
from PyQt5.QtGui import QFont, QBrush, QPen, QPainter
2024-07-30 15:52:02 +00:00
import sys
from enum import Enum
2024-07-30 23:57:17 +00:00
from abc import abstractmethod
2024-07-30 15:52:02 +00:00
2024-07-30 23:57:17 +00:00
class GraphNode:
@abstractmethod
def boundingRect(self) -> QRectF:
raise NotImplementedError("boundingRect")
@abstractmethod
def pos(self)->QPointF:
raise NotImplementedError("pos")
class StoryFragmentNode(QGraphicsItem, GraphNode):
2024-07-30 15:52:02 +00:00
"""
故事情节名称节点
"""
def __init__(self, name:str, font: QFont, parent):
QGraphicsItem.__init__(self, parent)
self.fragment_name = name
self.font_bind = font
self.data_bind: object = None
pass
def boundingRect(self) -> QRectF:
size = self.font_bind.pixelSize()
return QRectF(0, 0, size * len(self.fragment_name) + 10, size + 10)
def paint(self, painter, option, widget = ...):
outline = self.boundingRect()
text_rect = QRectF(outline.x()+5, outline.y()+5, outline.width()-10, outline.height() - 10)
painter.save()
painter.fillRect(outline, QBrush(Qt.gray))
painter.drawRect(outline)
painter.setFont(self.font_bind)
painter.drawText(text_rect, self.fragment_name)
painter.restore()
2024-07-30 23:57:17 +00:00
pass
2024-07-30 15:52:02 +00:00
# 添加自定义功能函数
def attach_data(self, data_inst: object):
self.data_bind = data_inst
pass
def get_data(self) -> object:
return self.data_bind
2024-07-30 23:57:17 +00:00
pass
class PenetrateNode(QGraphicsItem, GraphNode):
"""
贯穿连线节点
"""
def __init__(self, width:float, parent):
QGraphicsItem.__init__(self, parent)
self.width_store = width
pass
def resize(self, width: float):
self.width_store = width
self.update()
pass
def boundingRect(self):
return QRectF(0, 0, self.width_store, 8)
def paint(self, painter, option, widget = ...):
outline = self.boundingRect()
painter.save()
painter.fillRect(QRectF(0, 2, outline.width(), 4), Qt.black)
painter.restore()
pass
class CurveTransition(QGraphicsItem):
def __init__(self, start: GraphNode, end: GraphNode, parent):
QGraphicsItem.__init__(self, parent)
self.start_node = start
self.end_node = end
self.outline = QRectF()
pass
def layout_refresh(self):
self.setPos(self.start_node.boundingRect().topRight())
self.outline = QRectF(0, 0,
self.end_node.pos().x() - self.start_node.pos().x() - self.start_node.boundingRect().width(),
self.end_node.pos().y() + self.end_node.boundingRect().height() - self.start_node.pos().y())
pass
def boundingRect(self):
return self.outline
pass
def paint(self, painter, option, widget = ...):
outline = self.outline
painter.save()
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
start_rect = self.start_node.boundingRect()
end_rect = self.end_node.boundingRect()
start_pos = QPointF(0, start_rect.height()/2)
end_pos = QPointF(outline.width(), outline.height() - end_rect.height()/2)
npen = QPen(Qt.black)
npen.setWidthF(4)
painter.setPen(npen)
painter.drawLine(start_pos, end_pos)
painter.restore()
pass
2024-07-30 15:52:02 +00:00
class Direction(Enum):
RankLR = 0,
RankTB = 1,
class DAGActiveView(QGraphicsView):
def __init__(self, orie: Direction, parent):
QGraphicsView.__init__(self, parent)
2024-07-30 23:57:17 +00:00
self.transition_with = 200
2024-07-30 15:52:02 +00:00
self.orientation = orie
self.scene_bind = QGraphicsScene(self)
self.setScene(self.scene_bind)
font_global = QFont()
font_global.setPixelSize(20)
2024-07-30 23:57:17 +00:00
one = StoryFragmentNode("实例节点:示例情节", font_global, None)
self.scene_bind.addItem(one)
two = PenetrateNode(200, None)
two.setPos(QPointF(500, 400))
self.scene_bind.addItem(two)
connectx = CurveTransition(one, two, None)
self.scene_bind.addItem(connectx)
connectx.layout_refresh()
2024-07-30 15:52:02 +00:00
pass
def update_with_edges(self, arrows: List[Arrow]) -> None:
tools = DAGGraph()
tools.rebuild_from_edges(arrows)
tools.graph_layout()
total_nodes = tools.nodes_with_layout
2024-07-30 23:57:17 +00:00
for layer_idx in range(0, tools.max_layer_count):
current_level_nodes: List[DAGOrderHelper] = list(filter(lambda n:n.layer_number == layer_idx, total_nodes))
current_level_nodes.sort(key=lambda n:n.sort_number)
# 构建当前层节点
ypos_acc = 0
all_nodes = []
for node in current_level_nodes:
if node.is_fake_node():
node = PenetrateNode(20, None)
ypos_acc += node.boundingRect().height()
all_nodes.append(node)
self.scene_bind.addItem(node)
else:
node = StoryFragmentNode(node.layer_bind.bind_point().point_name, self.font(), None)
ypos_acc += node.boundingRect().height()
all_nodes.append(node)
self.scene_bind.addItem(node)
pass
pass
width_max = 0
for n in all_nodes:
width_max = max(width_max, n.boundingRect().width())
pass
for n in all_nodes:
if n is PenetrateNode:
n.resize(width_max)
pass
pass
pass
2024-07-30 15:52:02 +00:00
pass
if __name__ == "__main__":
app = QApplication(sys.argv)
view = DAGActiveView(Direction.RankLR, None)
view.show()
app.exec()