diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index ed77c0c..63b31c0 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,10 +5,9 @@
-
-
-
+
+
@@ -36,29 +35,29 @@
- {
+ "keyToString": {
+ "Python.CompareViews.executor": "Run",
+ "Python.CompareWindow.executor": "Run",
+ "Python.DAGGraph (1).executor": "Run",
+ "Python.DAGGraph.executor": "Run",
+ "Python.DAGLayout (1).executor": "Run",
+ "Python.DAGLayout.executor": "Run",
+ "Python.DAGPresent.executor": "Run",
+ "Python.MergeView.executor": "Run",
+ "Python.MileStone.executor": "Run",
+ "Python.NovelManage.executor": "Debug",
+ "Python.ReferView.executor": "Run",
+ "Python.StoryMap.executor": "Run",
+ "Python.ast_load.executor": "Debug",
+ "Python.entry.executor": "Run",
+ "RunOnceActivity.OpenProjectViewOnStart": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "git-widget-placeholder": "master",
+ "last_opened_file_path": "D:/Projects/Python/StoryCheckTools",
+ "settings.editor.selected.configurable": "reference.settings.ide.settings.new.ui"
}
-}]]>
+}
diff --git a/graph/directed_acyclic_graph/DAGLayout.py b/graph/directed_acyclic_graph/DAGLayout.py
index 1c042ec..1afc0bf 100644
--- a/graph/directed_acyclic_graph/DAGLayout.py
+++ b/graph/directed_acyclic_graph/DAGLayout.py
@@ -65,6 +65,7 @@ class DAGGraph:
def __init__(self):
self.graph_inst: Dict[str, DAGLayerHelper] = {}
self.nodes_with_layout: List[DAGOrderHelper] = []
+ self.max_layer_count = 0
pass
def rebuild_from_edges(self, arrow_list: List[Arrow]) -> None:
@@ -269,10 +270,9 @@ class DAGGraph:
self.__graph_recovery(sort_seqs)
# 数据图节点分层
- max_length = 0
for item in sort_seqs:
if item.input_count == 0:
- max_length = max(max_length, self.__node_layering(item))
+ self.max_layer_count = max(self.max_layer_count, self.__node_layering(item))
pass
pass
diff --git a/graph/directed_acyclic_graph/DAGPresent.py b/graph/directed_acyclic_graph/DAGPresent.py
index d962529..4e98714 100644
--- a/graph/directed_acyclic_graph/DAGPresent.py
+++ b/graph/directed_acyclic_graph/DAGPresent.py
@@ -1,14 +1,25 @@
-from graph.directed_acyclic_graph.DAGLayout import DAGGraph
+from graph.directed_acyclic_graph.DAGLayout import DAGGraph, DAGOrderHelper
from graph.DataType import Arrow, Point
from typing import List
from PyQt5.QtWidgets import QWidget, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem
-from PyQt5.QtCore import QRectF, Qt
-from PyQt5.QtGui import QFont, QBrush
+from PyQt5.QtCore import QRectF, Qt, QPointF
+from PyQt5.QtGui import QFont, QBrush, QPen, QPainter
import sys
from enum import Enum
+from abc import abstractmethod
-class StoryFragmentNode(QGraphicsItem):
+class GraphNode:
+ @abstractmethod
+ def boundingRect(self) -> QRectF:
+ raise NotImplementedError("boundingRect")
+
+ @abstractmethod
+ def pos(self)->QPointF:
+ raise NotImplementedError("pos")
+
+
+class StoryFragmentNode(QGraphicsItem, GraphNode):
"""
故事情节名称节点
"""
@@ -35,6 +46,8 @@ class StoryFragmentNode(QGraphicsItem):
painter.drawText(text_rect, self.fragment_name)
painter.restore()
+ pass
+
# 添加自定义功能函数
def attach_data(self, data_inst: object):
@@ -44,6 +57,72 @@ class StoryFragmentNode(QGraphicsItem):
def get_data(self) -> object:
return self.data_bind
+ 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
+
class Direction(Enum):
RankLR = 0,
@@ -54,13 +133,24 @@ class DAGActiveView(QGraphicsView):
def __init__(self, orie: Direction, parent):
QGraphicsView.__init__(self, parent)
+ self.transition_with = 200
+
self.orientation = orie
self.scene_bind = QGraphicsScene(self)
self.setScene(self.scene_bind)
font_global = QFont()
font_global.setPixelSize(20)
- self.scene_bind.addItem(StoryFragmentNode("实例节点:示例情节", font_global, None))
+
+ 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()
pass
@@ -70,6 +160,38 @@ class DAGActiveView(QGraphicsView):
tools.graph_layout()
total_nodes = tools.nodes_with_layout
+ 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
pass