From c78f66f5e4a5f12b588fccda97bbd11eb7d9f6bb Mon Sep 17 00:00:00 2001
From: codeboss <2422523675@qq.com>
Date: Tue, 30 Jul 2024 22:30:59 +0800
Subject: [PATCH] update
---
.idea/workspace.xml | 60 ++--
frame/ContentView.py | 4 +
frame/MergeView.py | 3 +-
frame/__pycache__/MergeView.cpython-312.pyc | Bin 4234 -> 4403 bytes
graph/DataType.py | 51 ++++
graph/__init__.py | 0
graph/__pycache__/DataType.cpython-312.pyc | Bin 0 -> 3182 bytes
graph/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 144 bytes
graph/dagpresent/DAGGraph.py | 315 ++++++++++++++++++++
graph/dagpresent/__init__.py | 0
10 files changed, 404 insertions(+), 29 deletions(-)
create mode 100644 frame/ContentView.py
create mode 100644 graph/DataType.py
create mode 100644 graph/__init__.py
create mode 100644 graph/__pycache__/DataType.cpython-312.pyc
create mode 100644 graph/__pycache__/__init__.cpython-312.pyc
create mode 100644 graph/dagpresent/DAGGraph.py
create mode 100644 graph/dagpresent/__init__.py
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 9ff407a..fef8eda 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,10 +5,13 @@
+
+
+
+
+
-
-
-
+
@@ -40,6 +43,7 @@
"keyToString": {
"Python.CompareViews.executor": "Run",
"Python.CompareWindow.executor": "Run",
+ "Python.DAGGraph.executor": "Run",
"Python.MergeView.executor": "Run",
"Python.MileStone.executor": "Run",
"Python.NovelManage.executor": "Debug",
@@ -53,7 +57,7 @@
"last_opened_file_path": "D:/Projects/Python/StoryCheckTools"
}
}]]>
-
+
@@ -76,6 +80,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -120,28 +146,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -165,19 +169,19 @@
+
-
+
-
diff --git a/frame/ContentView.py b/frame/ContentView.py
new file mode 100644
index 0000000..6322abd
--- /dev/null
+++ b/frame/ContentView.py
@@ -0,0 +1,4 @@
+from PyQt5.QtWidgets import QApplication, QWidget
+from networkx import DiGraph
+import networkx as nx
+
diff --git a/frame/MergeView.py b/frame/MergeView.py
index 0b0613f..4519e79 100644
--- a/frame/MergeView.py
+++ b/frame/MergeView.py
@@ -22,10 +22,11 @@ class LinesMergeView(QWidget, MemorySkin):
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
- splitter = QSplitter(Qt.Orientation.Vertical, self)
+ splitter = QSplitter(Qt.Orientation.Horizontal, self)
layout.addWidget(splitter)
self.tabs_con: QTabWidget = QTabWidget(self)
+ self.tabs_con.setTabPosition(QTabWidget.TabPosition.West)
splitter.addWidget(self.tabs_con)
self.define_prev: QTextEdit = QTextEdit(self)
diff --git a/frame/__pycache__/MergeView.cpython-312.pyc b/frame/__pycache__/MergeView.cpython-312.pyc
index fd48e7e387f266b60fdf631fce0c87bb61499529..20e055ed29bdaa4bf7963e30da6e893ad6a15fb7 100644
GIT binary patch
delta 604
zcmeBD+^ocVnwOW00SHn&SESW$5@HZgB8j=a9I_Au&VZ3Ww4Y6}=TDmsLzUSbBJGaPai=cJf~5P`SvV
zvLx!ds`W)x>+7lx7gZgua5!$B$Z5jFXgK*8w+6Qv&?!aYAi`wwPi{>{`^m~Yij4Z3
zU3nTA%|Md3xJ&W_auPH1LQ*SAicG*F#ffF9@g@23xv9BD4nUD&CLp1pP$UJUid-lE
z;gx5!oGioF!DbH;SjDF)CBw>lK~C=j1CZV!a>35;qNV?55a+YaJ~naSH&K2PRnGc%V4sR4l+
zhSiKPRy-es1rp7Yfw2*Emi*)zHd98$$p^Sa#j(iOFlH%DzQ?90D+JS1!{ovco5H|Q
z%T}Te6Pw(?F2+*BmZb$WMr5)VyP~8bObS8Qume?q43m)nxbR%)cBJ8_}tXoB3qzHF%ytbP$-fDQbkUam-5LoT1-C5*TH53
z5eVkjl#*uUy&$LefdNSG5V>IIchS=SGl=uqYVtJxuZ+QydjyR5^ci_4gwM$O!T@A^
KNSS;_ARPcdZf0-*
diff --git a/graph/DataType.py b/graph/DataType.py
new file mode 100644
index 0000000..5446d1d
--- /dev/null
+++ b/graph/DataType.py
@@ -0,0 +1,51 @@
+from typing import List
+
+
+class Pos:
+ def __init__(self, x: float = 0, y: float = 0):
+ self.x_pos = x
+ self.y_pos = y
+ pass
+
+ def make_copy(self) -> 'Pos':
+ return Pos(self.x_pos, self.y_pos)
+
+
+class Point:
+ def __init__(self, name:str, pos: Pos = Pos()):
+ self.point_name = name
+ self.pos = pos
+ pass
+
+ def name(self) -> str:
+ return self.point_name
+
+ def make_copy(self) -> 'Point':
+ return Point(self.point_name, self.pos.make_copy())
+
+
+class Line:
+ def __init__(self, p0: Point, p1: Point):
+ self.point_set = [p0, p1]
+ pass
+
+ def points(self) -> List[Point]:
+ return self.point_set
+
+ def make_copy(self) -> 'Line':
+ return Line(self.points()[0].make_copy(), self.points()[1].make_copy())
+
+
+class Arrow(Line):
+ def __init__(self, start: Point, end: Point):
+ Line.__init__(self, start, end)
+ pass
+
+ def start_point(self):
+ return self.point_set[0]
+
+ def end_point(self):
+ return self.point_set[1]
+
+ pass
+
diff --git a/graph/__init__.py b/graph/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/graph/__pycache__/DataType.cpython-312.pyc b/graph/__pycache__/DataType.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4ec0844b689a94b5f80fa1828ec4418745bd1eb2
GIT binary patch
literal 3182
zcmb_eO>7fK6rR~Xubnt9#QZd+sZm4d5}KrewkRN^(x4tlLnPo(E9%O4Hza9dyR(bR
zS`l#Y0i;R8V7yQ1#Gj@4X~SrKGF&)Dt%cQz3Ecdo$imyjWC49m#KI-u%6v
z?|a@~lgR{uW?y+Y{h30@UpNVx+8i{l!k|hlV$pfBK(mw*iF`sVsX{DyRT8_h64xo9
zQ$wB1bsFemp-$mC9dvP9S(TfoRWD(5JjRJZePw*sar0tD$k3F2m{*BSveY73$C5v>Cm@pD;QGyu_
zW*%9g6NcogH_Q^;rFz&IvadMy{4~>G8;gN(@VZPU^L%J*bZU|n=j^=eOig<3OtCO^
z$t^PPavM^L5jlEzG*6S?&a52ZI^<>vV&-O}6&!zoUGe
z2s*v90cw&DO@s*~PB?T9236vQYl~#AnI2dO8))%Fqy-7OL|7b_44I)34B^2#K&(*K
z37vzNXIK)XA799QVVn74$tx$(`f!SeoC6?klRr{>mrhnk?~m5p@5&F#8>wS!`Z1P*
zHHa%i>$rdzYs_}9vjTtC&@eKE+=6YIe!?^tiq`dc9Cw=Lm)CRi%@uWezL;}a7v8cL
z#0udzbL)ip=mR?I^nj?4ZAn&>wIg)`-9xp3-O~hB2gEVl+#YskVPyw)60zk~&|sUU
z06}FJ1XWA1m1)UR0fw5TTN?B+FJ{Djb+R~HaD4@h_hkUmtD{n+Nb(aUypjis9Rh10
zYnUYV1`1RJ$P^*LK$W|=86ng#6#>+GdI4&wt!_k|zNL4Hupkaw@P-iJ1j3*Kfyf;6
z(H7u{vR+tXdqC{M4!5wJ5T;;2(zuSc1L9NN_io+2wWjy-_gkLE6!E7&hrz2teGWMr
zX+b`tG*B2iDSW_ya-6;34T7(n65fO?Kms@!5Xge`(&ego-(0qSnE8HYv;Wk3|EZ1s
z(T&vUHT^Vq6X}4F=IsxnLs;FFuuwTqQE4I6mx4)V6L435Eb(K
z@uAi8+X(jB#~k|0c@F(LK>w{J7(@3|qv{!P51wwJ0TKvX;{y;oG-#>43e;Z3R&Dup
z*wYsNP}K{zFO`Pj_g;JGqo=$eOV_Xr!%Q?!)T9;a#|7XW+YO-J2V2+?5N)8I#3w_f
zb3VKVI{{)nhiX@A2La|?{}0UBQ8Xh0a$n=tI^_;-hnQs0)E%JTynAy^fAb|k{}%Q~
zfQ|%+v;e*q#rV%5S|Tmj0c?HQdFkKI?-0b0CwT3CjP(H0@iuhq7zz%|NHDmv+{Ggi
zQUs4UAA&%NbT5rnFWkTI&8N$U*VBEQ>B05%;6{4*QQyx4KMwr#?k{wGm7*>f)Myv6VS^+l$QMjNpF}==UxIBh2CEqd;yonNyb}AkF)A0^PFRz^<-SwYR3!
zt~}^K;`AbMCaGW!p3cKBY$0>r0oBWxQz2Ou{xV2b^E4yos~<5|{K{7yH^*FGwhLAZ
zVZtgn!moA1_u8d)5x;Px;7f~=CYhkzaE~!E0hfkg%!Wa<;ZhejDI^m*dJnO`B4}rclAddJ<1!ot`ja(c-=X(G$1}%WSl+
z526kQ78<7yM1=@++8mt^k8pH80dyXI0+2jh*N7Syu*72%@JXe)UTL;)jma<JmtkIamWj77{q761!aBE Point:
+ return self.bind_node
+
+ def next_append(self, inst: 'DAGLayerHelper'):
+ self.next_points.append(inst)
+ inst.input_count += 1
+ pass
+
+ def next_nodes(self) -> List['DAGLayerHelper']:
+ return self.next_points
+
+ def make_copy(self) -> 'DAGLayerHelper':
+ temp_ps = []
+ for n in self.next_points:
+ temp_ps.append(n.make_copy())
+ pass
+
+ ins = DAGLayerHelper(self.bind_node.make_copy())
+ ins.input_count = self.input_count
+ ins.next_points = temp_ps
+ ins.layer_v = self.layer_v
+ ins.sort_v = self.sort_v
+ return ins
+
+
+class DAGOrderHelper:
+ def __init__(self, level:int = 0, relate:DAGLayerHelper|None = None, bind: DAGLayerHelper|None = None):
+ self.layer_bind = bind
+ self.relate_bind = relate
+ self.layer_number = level
+ self.sort_number:float = 0
+ self.__prev_layer_nodes: List['DAGOrderHelper'] = []
+
+ if bind is not None:
+ self.layer_number = bind.layer_v
+ pass
+ pass
+
+ def is_fake_node(self) -> bool:
+ return self.layer_bind is None
+
+ def get_previous_sibling_layer_nodes(self):
+ return self.__prev_layer_nodes
+
+ def append_previous_sibling_layer_node(self, node: 'DAGOrderHelper'):
+ self.__prev_layer_nodes.append(node)
+ pass
+
+
+class DAGGraph:
+ def __init__(self, orie: Direction):
+ self.orientation = orie
+ self.graph_inst: Dict[str, DAGLayerHelper] = {}
+ self.nodes_with_layout: List[DAGOrderHelper] = []
+ pass
+
+ def rebuild_from_edges(self, arrow_list: List[Arrow]) -> None:
+ """
+ 通过有序边构建有向图
+ :param arrow_list: 有向边集合
+ """
+ for arr in arrow_list:
+ start = arr.start_point()
+ start_helper = None
+ if start.point_name in self.graph_inst:
+ start_helper = self.graph_inst[start.point_name]
+ else:
+ start_helper = DAGLayerHelper(start)
+ self.graph_inst[start.point_name] = start_helper
+
+ end = arr.end_point()
+ end_helper = None
+ if end.point_name in self.graph_inst:
+ end_helper = self.graph_inst[end.point_name]
+ else:
+ end_helper = DAGLayerHelper(end)
+ self.graph_inst[end.point_name] = end_helper
+
+ start_helper.next_append(end_helper)
+ pass
+ pass
+
+ def __spawns_peak(self, refset: List[DAGLayerHelper]) -> Tuple[DAGLayerHelper, List[DAGLayerHelper]] | None:
+ """
+ 拓扑排序迭代处理
+ :param refset:
+ :return:
+ """
+ for inst in refset:
+ if inst.input_count == 0:
+ for it_nxt in inst.next_nodes():
+ refset.append(it_nxt)
+ it_nxt.input_count -= 1
+ pass
+
+ refset.remove(inst)
+ if inst.bind_point().point_name in self.graph_inst:
+ self.graph_inst.pop(inst.bind_point().point_name)
+
+ return inst, refset
+
+ for inst in self.graph_inst.values():
+ if inst.input_count == 0:
+ if inst in refset:
+ refset.remove(inst)
+
+ for it_nxt in inst.next_nodes():
+ refset.append(it_nxt)
+ it_nxt.input_count -= 1
+ pass
+
+ self.graph_inst.pop(inst.bind_point().point_name)
+ return inst, refset
+ pass
+
+ if len(self.graph_inst) > 0:
+ raise RuntimeError("有向无环图中发现环形结构!")
+
+ return None
+
+ def __graph_recovery(self, sort_seqs: List[DAGLayerHelper]) -> None:
+ """
+ 通过拓扑排序结果恢复数据图
+ :param sort_seqs: 有序序列
+ """
+
+ # 清空cache
+ for it in sort_seqs:
+ it.input_count = 0
+ pass
+
+ # 入度复原
+ for it in sort_seqs:
+ for nxt in it.next_nodes():
+ nxt.input_count += 1
+ pass
+ pass
+
+ # 数据图恢复
+ self.graph_inst.clear()
+ for it in sort_seqs:
+ self.graph_inst[it.bind_point().point_name] = it
+ pass
+
+ pass
+
+ def __node_layering(self, inst: DAGLayerHelper, layer_current: int = 0) -> int:
+ """
+ 节点分层处理,返回路径最大长度
+ :param inst: 当前节点
+ :param layer_current: 节点等级
+ :return: 最长路径长度
+ """
+ inst.layer_v = max(inst.layer_v, layer_current)
+
+ max_remains = inst.layer_v
+ values = inst.next_nodes()
+ for fork in values:
+ max_remains = max(self.__node_layering(fork, inst.layer_v + 1), max_remains)
+ pass
+
+ return max_remains + 1
+
+ def __tidy_graph_nodes(self) -> List[DAGOrderHelper]:
+ nodes_temp: Dict[str, DAGOrderHelper] = {}
+
+ # 注册所有数据图实节点
+ for node in self.graph_inst.values():
+ nodes_temp[node.bind_point().point_name] = DAGOrderHelper(bind=node, relate=node)
+ pass
+
+ temp_array: List[DAGOrderHelper] = []
+ temp_array.extend(nodes_temp.values())
+
+ # 生成链接fake-node节点,并执行链接
+ for node in self.graph_inst.values():
+ for next in node.next_nodes():
+ node_links = [nodes_temp[node.bind_point().point_name]]
+ for layer_index in range(node.layer_v + 1, next.layer_v):
+ node_links.append(DAGOrderHelper(layer_index, relate=node))
+ pass
+
+ node_links.append(nodes_temp[next.bind_point().point_name])
+
+ # 节点链接串已经构建完成,链接各层级节点
+ for idx in range(1, len(node_links)):
+ start_point = node_links[idx-1]
+ end_point = node_links[idx]
+ end_point.append_previous_sibling_layer_node(start_point)
+ pass
+
+ temp_array.extend(node_links[1:len(node_links)-1])
+ pass
+ pass
+
+ return temp_array
+
+ def __graph_layer_nodes_sort(self, layer_index:int, nodes: List[DAGOrderHelper]):
+ # 提取当前层次的节点
+ target_nodes_within_layer = []
+ for n in nodes:
+ if n.layer_number == layer_index:
+ target_nodes_within_layer.append(n)
+ pass
+ pass
+
+ # 当前层次没有节点,则不做处理
+ if len(target_nodes_within_layer) == 0:
+ return
+
+ if layer_index == 0:
+ for idx in range(0, len(target_nodes_within_layer)):
+ target_nodes_within_layer[idx].sort_number = idx
+ pass
+ pass
+
+ elif layer_index > 0:
+ # 计算排序系数
+ for target_node in target_nodes_within_layer:
+ prev_sorts = list(map(lambda n:n.sort_number, target_node.get_previous_sibling_layer_nodes()))
+ target_node.sort_number = sum(prev_sorts)/len(prev_sorts)
+ pass
+
+ def compare_item(a: DAGOrderHelper):
+ return a.sort_number
+
+ # 整理节点排序
+ target_nodes_within_layer.sort(key=compare_item)
+ for idx in range(0, len(target_nodes_within_layer)):
+ target_nodes_within_layer[idx].sort_number = idx
+ pass
+
+ pass
+
+ self.__graph_layer_nodes_sort(layer_index + 1, nodes)
+ pass
+
+
+ def graph_layout(self):
+ sort_seqs = []
+
+ # 拓扑排序
+ head = None
+ refs = []
+ while True:
+ peaks_result = self.__spawns_peak(refs)
+ if peaks_result is None:
+ break
+
+ head, refs = peaks_result
+ sort_seqs.append(head)
+ pass
+
+ # 数据图恢复
+ 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))
+ pass
+ pass
+
+ # 整理数据图节点
+ rich_nodes = self.__tidy_graph_nodes()
+ self.__graph_layer_nodes_sort(0, rich_nodes)
+ self.nodes_with_layout = rich_nodes
+
+ pass
+
+ def visible_nodes(self) -> List[DAGOrderHelper]:
+ retvs = []
+ for n in self.nodes_with_layout:
+ if not n.is_fake_node():
+ retvs.append(n)
+ pass
+ pass
+ return retvs
+
+
+if __name__ == "__main__":
+ graph = DAGGraph(Direction.RankLR)
+ arrows = [
+ Arrow(Point('a'), Point('b')),
+ Arrow(Point('a'), Point('c')),
+ Arrow(Point('c'), Point('d')),
+ Arrow(Point('a'), Point('d')),
+ ]
+ graph.rebuild_from_edges(arrows)
+
+ graph.graph_layout()
+
+ points = graph.visible_nodes()
+ for p in points:
+ print(f"{p.layer_bind.bind_point().point_name},level{p.layer_number},sort{p.sort_number}")
\ No newline at end of file
diff --git a/graph/dagpresent/__init__.py b/graph/dagpresent/__init__.py
new file mode 100644
index 0000000..e69de29