构建ActiveView构件

This commit is contained in:
codeboss 2024-07-30 23:52:02 +08:00
parent c78f66f5e4
commit b3fc5f325a
8 changed files with 129 additions and 69 deletions

View File

@ -5,13 +5,10 @@
</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$/frame/ContentView.py" afterDir="false" /> <change afterPath="$PROJECT_DIR$/graph/directed_acyclic_graph/__init__.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/graph/DataType.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/graph/__init__.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/graph/dagpresent/DAGGraph.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/graph/dagpresent/__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$/frame/MergeView.py" beforeDir="false" afterPath="$PROJECT_DIR$/frame/MergeView.py" 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/dagpresent/__init__.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" />
@ -43,7 +40,11 @@
"keyToString": { "keyToString": {
"Python.CompareViews.executor": "Run", "Python.CompareViews.executor": "Run",
"Python.CompareWindow.executor": "Run", "Python.CompareWindow.executor": "Run",
"Python.DAGGraph (1).executor": "Run",
"Python.DAGGraph.executor": "Run", "Python.DAGGraph.executor": "Run",
"Python.DAGLayout (1).executor": "Run",
"Python.DAGLayout.executor": "Run",
"Python.DAGPresent.executor": "Run",
"Python.MergeView.executor": "Run", "Python.MergeView.executor": "Run",
"Python.MileStone.executor": "Run", "Python.MileStone.executor": "Run",
"Python.NovelManage.executor": "Debug", "Python.NovelManage.executor": "Debug",
@ -54,10 +55,11 @@
"RunOnceActivity.OpenProjectViewOnStart": "true", "RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true", "RunOnceActivity.ShowReadmeOnStart": "true",
"git-widget-placeholder": "master", "git-widget-placeholder": "master",
"last_opened_file_path": "D:/Projects/Python/StoryCheckTools" "last_opened_file_path": "D:/Projects/Python/StoryCheckTools",
"settings.editor.selected.configurable": "reference.settings.ide.settings.new.ui"
} }
}]]></component> }]]></component>
<component name="RunManager" selected="Python.DAGGraph"> <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" />
<option name="ENV_FILES" value="" /> <option name="ENV_FILES" value="" />
@ -80,7 +82,7 @@
<option name="INPUT_FILE" value="" /> <option name="INPUT_FILE" value="" />
<method v="2" /> <method v="2" />
</configuration> </configuration>
<configuration name="DAGGraph" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> <configuration name="DAGLayout" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="StoryTools" /> <module name="StoryTools" />
<option name="ENV_FILES" value="" /> <option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" /> <option name="INTERPRETER_OPTIONS" value="" />
@ -89,11 +91,11 @@
<env name="PYTHONUNBUFFERED" value="1" /> <env name="PYTHONUNBUFFERED" value="1" />
</envs> </envs>
<option name="SDK_HOME" value="" /> <option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/graph/dagpresent" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/graph/directed_acyclic_graph" />
<option name="IS_MODULE_SDK" value="true" /> <option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" /> <option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" /> <option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/graph/dagpresent/DAGGraph.py" /> <option name="SCRIPT_NAME" value="$PROJECT_DIR$/graph/directed_acyclic_graph/DAGLayout.py" />
<option name="PARAMETERS" value="" /> <option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" /> <option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" /> <option name="EMULATE_TERMINAL" value="false" />
@ -102,7 +104,7 @@
<option name="INPUT_FILE" value="" /> <option name="INPUT_FILE" value="" />
<method v="2" /> <method v="2" />
</configuration> </configuration>
<configuration name="MileStone" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> <configuration name="DAGPresent" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="StoryTools" /> <module name="StoryTools" />
<option name="ENV_FILES" value="" /> <option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" /> <option name="INTERPRETER_OPTIONS" value="" />
@ -111,12 +113,12 @@
<env name="PYTHONUNBUFFERED" value="1" /> <env name="PYTHONUNBUFFERED" value="1" />
</envs> </envs>
<option name="SDK_HOME" value="" /> <option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/manage" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/graph/directed_acyclic_graph" />
<option name="IS_MODULE_SDK" value="true" /> <option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" /> <option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" /> <option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/manage/MileStone.py" /> <option name="SCRIPT_NAME" value="$PROJECT_DIR$/graph/directed_acyclic_graph/DAGPresent.py" />
<option name="PARAMETERS" value="milestone:new" /> <option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" /> <option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" /> <option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" /> <option name="MODULE_MODE" value="false" />
@ -146,49 +148,25 @@
<option name="INPUT_FILE" value="" /> <option name="INPUT_FILE" value="" />
<method v="2" /> <method v="2" />
</configuration> </configuration>
<configuration name="entry" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="StoryTools" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/entry.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<list> <list>
<item itemvalue="Python.DAGGraph" /> <item itemvalue="Python.DAGLayout" />
<item itemvalue="Python.DAGPresent" />
<item itemvalue="Python.CompareViews" /> <item itemvalue="Python.CompareViews" />
<item itemvalue="Python.NovelManage" /> <item itemvalue="Python.NovelManage" />
<item itemvalue="Python.entry" />
<item itemvalue="Python.MileStone" />
</list> </list>
<recent_temporary> <recent_temporary>
<list> <list>
<item itemvalue="Python.DAGGraph" /> <item itemvalue="Python.DAGPresent" />
<item itemvalue="Python.DAGLayout" />
<item itemvalue="Python.CompareViews" /> <item itemvalue="Python.CompareViews" />
<item itemvalue="Python.NovelManage" /> <item itemvalue="Python.NovelManage" />
<item itemvalue="Python.MileStone" />
<item itemvalue="Python.entry" />
</list> </list>
</recent_temporary> </recent_temporary>
</component> </component>
<component name="SharedIndexes"> <component name="SharedIndexes">
<attachedChunks> <attachedChunks>
<set> <set>
<option value="bundled-python-sdk-5b207ade9991-746f403e7f0c-com.jetbrains.pycharm.community.sharedIndexes.bundled-PC-241.17890.14" /> <option value="bundled-python-sdk-50da183f06c8-d3b881c8e49f-com.jetbrains.pycharm.community.sharedIndexes.bundled-PC-233.13135.95" />
</set> </set>
</attachedChunks> </attachedChunks>
</component> </component>

View File

@ -1,11 +1,5 @@
from graph.DataType import Point, Arrow from graph.DataType import Point, Arrow
from typing import List, Dict, Tuple from typing import List, Dict, Tuple
from enum import Enum
class Direction(Enum):
RankLR = 0,
RankTB = 1,
class DAGLayerHelper: class DAGLayerHelper:
@ -46,6 +40,7 @@ class DAGOrderHelper:
def __init__(self, level:int = 0, relate:DAGLayerHelper|None = None, bind: DAGLayerHelper|None = None): def __init__(self, level:int = 0, relate:DAGLayerHelper|None = None, bind: DAGLayerHelper|None = None):
self.layer_bind = bind self.layer_bind = bind
self.relate_bind = relate self.relate_bind = relate
self.layer_number = level self.layer_number = level
self.sort_number:float = 0 self.sort_number:float = 0
self.__prev_layer_nodes: List['DAGOrderHelper'] = [] self.__prev_layer_nodes: List['DAGOrderHelper'] = []
@ -67,8 +62,7 @@ class DAGOrderHelper:
class DAGGraph: class DAGGraph:
def __init__(self, orie: Direction): def __init__(self):
self.orientation = orie
self.graph_inst: Dict[str, DAGLayerHelper] = {} self.graph_inst: Dict[str, DAGLayerHelper] = {}
self.nodes_with_layout: List[DAGOrderHelper] = [] self.nodes_with_layout: List[DAGOrderHelper] = []
pass pass
@ -99,37 +93,39 @@ class DAGGraph:
pass pass
pass pass
def __spawns_peak(self, refset: List[DAGLayerHelper]) -> Tuple[DAGLayerHelper, List[DAGLayerHelper]] | None: def __spawns_peak(self, ref_set: List[DAGLayerHelper]) -> Tuple[DAGLayerHelper, List[DAGLayerHelper]] | None:
""" """
拓扑排序迭代处理 拓扑排序迭代处理
:param refset: :param ref_set:
:return: :return:
""" """
for inst in refset: for inst in ref_set:
if inst.input_count == 0: if inst.input_count == 0:
for it_nxt in inst.next_nodes(): for it_nxt in inst.next_nodes():
refset.append(it_nxt)
it_nxt.input_count -= 1 it_nxt.input_count -= 1
if it_nxt not in ref_set:
ref_set.append(it_nxt)
pass
pass pass
refset.remove(inst) ref_set.remove(inst)
if inst.bind_point().point_name in self.graph_inst: self.graph_inst.pop(inst.bind_point().point_name)
self.graph_inst.pop(inst.bind_point().point_name) return inst, ref_set
return inst, refset
for inst in self.graph_inst.values(): for inst in self.graph_inst.values():
if inst.input_count == 0: if inst.input_count == 0:
if inst in refset: if inst in ref_set:
refset.remove(inst) ref_set.remove(inst)
for it_nxt in inst.next_nodes(): for it_nxt in inst.next_nodes():
refset.append(it_nxt)
it_nxt.input_count -= 1 it_nxt.input_count -= 1
if it_nxt not in ref_set:
ref_set.append(it_nxt)
pass
pass pass
self.graph_inst.pop(inst.bind_point().point_name) self.graph_inst.pop(inst.bind_point().point_name)
return inst, refset return inst, ref_set
pass pass
if len(self.graph_inst) > 0: if len(self.graph_inst) > 0:
@ -254,7 +250,6 @@ class DAGGraph:
self.__graph_layer_nodes_sort(layer_index + 1, nodes) self.__graph_layer_nodes_sort(layer_index + 1, nodes)
pass pass
def graph_layout(self): def graph_layout(self):
sort_seqs = [] sort_seqs = []
@ -299,17 +294,22 @@ class DAGGraph:
if __name__ == "__main__": if __name__ == "__main__":
graph = DAGGraph(Direction.RankLR) graph = DAGGraph()
arrows = [ arrows = [
Arrow(Point('a'), Point('b')), Arrow(Point('a'), Point('b')),
Arrow(Point('a'), Point('c')), Arrow(Point('a'), Point('c')),
Arrow(Point('c'), Point('d')), Arrow(Point('c'), Point('d')),
Arrow(Point('a'), Point('d')), Arrow(Point('a'), Point('d')),
Arrow(Point('c'), Point('e')),
Arrow(Point('c'), Point('f')),
] ]
graph.rebuild_from_edges(arrows) graph.rebuild_from_edges(arrows)
graph.graph_layout() graph.graph_layout()
points = graph.visible_nodes() points = graph.nodes_with_layout
for p in points: for p in points:
print(f"{p.layer_bind.bind_point().point_name},level{p.layer_number},sort{p.sort_number}") if not p.is_fake_node():
print(f"{p.layer_bind.bind_point().point_name},level{p.layer_number},sort{p.sort_number}")
else:
print(f"'{p.relate_bind.bind_point().point_name}',level{p.layer_number},sort{p.sort_number}")

View File

@ -0,0 +1,82 @@
from graph.directed_acyclic_graph.DAGLayout import DAGGraph
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
import sys
from enum import Enum
class StoryFragmentNode(QGraphicsItem):
"""
故事情节名称节点
"""
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()
# 添加自定义功能函数
def attach_data(self, data_inst: object):
self.data_bind = data_inst
pass
def get_data(self) -> object:
return self.data_bind
class Direction(Enum):
RankLR = 0,
RankTB = 1,
class DAGActiveView(QGraphicsView):
def __init__(self, orie: Direction, parent):
QGraphicsView.__init__(self, parent)
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))
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
pass
if __name__ == "__main__":
app = QApplication(sys.argv)
view = DAGActiveView(Direction.RankLR, None)
view.show()
app.exec()