This commit is contained in:
codeboss 2024-07-31 07:57:17 +08:00
parent b3fc5f325a
commit f86b7bf460
3 changed files with 153 additions and 32 deletions

View File

@ -5,10 +5,9 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="f609c0f2-cd0d-4eea-87f1-8caf02d3f04f" name="Changes" comment=""> <list default="true" id="f609c0f2-cd0d-4eea-87f1-8caf02d3f04f" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/graph/directed_acyclic_graph/__init__.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/graph/dagpresent/DAGGraph.py" beforeDir="false" afterPath="$PROJECT_DIR$/graph/directed_acyclic_graph/DAGLayout.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/graph/directed_acyclic_graph/DAGLayout.py" beforeDir="false" afterPath="$PROJECT_DIR$/graph/directed_acyclic_graph/DAGLayout.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/graph/dagpresent/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/graph/directed_acyclic_graph/DAGPresent.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/graph/directed_acyclic_graph/DAGPresent.py" beforeDir="false" afterPath="$PROJECT_DIR$/graph/directed_acyclic_graph/DAGPresent.py" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -36,29 +35,29 @@
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent"><![CDATA[{ <component name="PropertiesComponent">{
"keyToString": { &quot;keyToString&quot;: {
"Python.CompareViews.executor": "Run", &quot;Python.CompareViews.executor&quot;: &quot;Run&quot;,
"Python.CompareWindow.executor": "Run", &quot;Python.CompareWindow.executor&quot;: &quot;Run&quot;,
"Python.DAGGraph (1).executor": "Run", &quot;Python.DAGGraph (1).executor&quot;: &quot;Run&quot;,
"Python.DAGGraph.executor": "Run", &quot;Python.DAGGraph.executor&quot;: &quot;Run&quot;,
"Python.DAGLayout (1).executor": "Run", &quot;Python.DAGLayout (1).executor&quot;: &quot;Run&quot;,
"Python.DAGLayout.executor": "Run", &quot;Python.DAGLayout.executor&quot;: &quot;Run&quot;,
"Python.DAGPresent.executor": "Run", &quot;Python.DAGPresent.executor&quot;: &quot;Run&quot;,
"Python.MergeView.executor": "Run", &quot;Python.MergeView.executor&quot;: &quot;Run&quot;,
"Python.MileStone.executor": "Run", &quot;Python.MileStone.executor&quot;: &quot;Run&quot;,
"Python.NovelManage.executor": "Debug", &quot;Python.NovelManage.executor&quot;: &quot;Debug&quot;,
"Python.ReferView.executor": "Run", &quot;Python.ReferView.executor&quot;: &quot;Run&quot;,
"Python.StoryMap.executor": "Run", &quot;Python.StoryMap.executor&quot;: &quot;Run&quot;,
"Python.ast_load.executor": "Debug", &quot;Python.ast_load.executor&quot;: &quot;Debug&quot;,
"Python.entry.executor": "Run", &quot;Python.entry.executor&quot;: &quot;Run&quot;,
"RunOnceActivity.OpenProjectViewOnStart": "true", &quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
"RunOnceActivity.ShowReadmeOnStart": "true", &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
"git-widget-placeholder": "master", &quot;git-widget-placeholder&quot;: &quot;master&quot;,
"last_opened_file_path": "D:/Projects/Python/StoryCheckTools", &quot;last_opened_file_path&quot;: &quot;D:/Projects/Python/StoryCheckTools&quot;,
"settings.editor.selected.configurable": "reference.settings.ide.settings.new.ui" &quot;settings.editor.selected.configurable&quot;: &quot;reference.settings.ide.settings.new.ui&quot;
} }
}]]></component> }</component>
<component name="RunManager" selected="Python.DAGPresent"> <component name="RunManager" selected="Python.DAGPresent">
<configuration name="CompareViews" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> <configuration name="CompareViews" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="StoryTools" /> <module name="StoryTools" />

View File

@ -65,6 +65,7 @@ class DAGGraph:
def __init__(self): def __init__(self):
self.graph_inst: Dict[str, DAGLayerHelper] = {} self.graph_inst: Dict[str, DAGLayerHelper] = {}
self.nodes_with_layout: List[DAGOrderHelper] = [] self.nodes_with_layout: List[DAGOrderHelper] = []
self.max_layer_count = 0
pass pass
def rebuild_from_edges(self, arrow_list: List[Arrow]) -> None: def rebuild_from_edges(self, arrow_list: List[Arrow]) -> None:
@ -269,10 +270,9 @@ class DAGGraph:
self.__graph_recovery(sort_seqs) self.__graph_recovery(sort_seqs)
# 数据图节点分层 # 数据图节点分层
max_length = 0
for item in sort_seqs: for item in sort_seqs:
if item.input_count == 0: 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
pass pass

View File

@ -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 graph.DataType import Arrow, Point
from typing import List from typing import List
from PyQt5.QtWidgets import QWidget, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem from PyQt5.QtWidgets import QWidget, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem
from PyQt5.QtCore import QRectF, Qt from PyQt5.QtCore import QRectF, Qt, QPointF
from PyQt5.QtGui import QFont, QBrush from PyQt5.QtGui import QFont, QBrush, QPen, QPainter
import sys import sys
from enum import Enum 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.drawText(text_rect, self.fragment_name)
painter.restore() painter.restore()
pass
# 添加自定义功能函数 # 添加自定义功能函数
def attach_data(self, data_inst: object): def attach_data(self, data_inst: object):
@ -44,6 +57,72 @@ class StoryFragmentNode(QGraphicsItem):
def get_data(self) -> object: def get_data(self) -> object:
return self.data_bind 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): class Direction(Enum):
RankLR = 0, RankLR = 0,
@ -54,13 +133,24 @@ class DAGActiveView(QGraphicsView):
def __init__(self, orie: Direction, parent): def __init__(self, orie: Direction, parent):
QGraphicsView.__init__(self, parent) QGraphicsView.__init__(self, parent)
self.transition_with = 200
self.orientation = orie self.orientation = orie
self.scene_bind = QGraphicsScene(self) self.scene_bind = QGraphicsScene(self)
self.setScene(self.scene_bind) self.setScene(self.scene_bind)
font_global = QFont() font_global = QFont()
font_global.setPixelSize(20) 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 pass
@ -70,6 +160,38 @@ class DAGActiveView(QGraphicsView):
tools.graph_layout() tools.graph_layout()
total_nodes = tools.nodes_with_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 pass