From 9b59b0a4585e2d0d194e8fb7be1e802a21d19abe Mon Sep 17 00:00:00 2001
From: codeboss <2422523675@qq.com>
Date: Fri, 2 Aug 2024 17:07:41 +0800
Subject: [PATCH] =?UTF-8?q?=E7=AB=A0=E8=8A=82=E8=8A=82=E7=82=B9=E5=BC=95?=
=?UTF-8?q?=E7=94=A8=E8=A7=86=E5=9B=BE=E6=9E=84=E5=BB=BA=E5=AE=8C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/workspace.xml | 75 +++++----
frame/ContentView.py | 81 +++++++--
graph/undirected_graph/UDGPresent.py | 156 ++++++++++++------
.../__pycache__/UDGPresent.cpython-312.pyc | Bin 0 -> 18839 bytes
.../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 161 bytes
parse/StoryMap.py | 72 +++++++-
parse/__pycache__/StoryMap.cpython-312.pyc | Bin 8312 -> 11712 bytes
7 files changed, 286 insertions(+), 98 deletions(-)
create mode 100644 graph/undirected_graph/__pycache__/UDGPresent.cpython-312.pyc
create mode 100644 graph/undirected_graph/__pycache__/__init__.cpython-312.pyc
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index c563925..ee5e252 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,14 +5,10 @@
-
-
-
-
-
-
+
+
@@ -40,40 +36,40 @@
- {
+ "keyToString": {
+ "Python.CompareViews.executor": "Run",
+ "Python.CompareWindow.executor": "Run",
+ "Python.ContentView.executor": "Debug",
+ "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.UDGLayout.executor": "Run",
+ "Python.UDGPresent.executor": "Run",
+ "Python.ast_load.executor": "Debug",
+ "Python.entry.executor": "Run",
+ "Python.test.executor": "Run",
+ "RunOnceActivity.OpenProjectViewOnStart": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "git-widget-placeholder": "master",
+ "last_opened_file_path": "D:/Projects/Python/StoryTools",
+ "settings.editor.selected.configurable": "debugger.dataViews"
}
-}]]>
+}
-
+
@@ -193,8 +189,8 @@
-
+
@@ -222,4 +218,15 @@
+
+
+
+
+ file://$PROJECT_DIR$/graph/undirected_graph/UDGPresent.py
+ 239
+
+
+
+
+
\ No newline at end of file
diff --git a/frame/ContentView.py b/frame/ContentView.py
index 330e7dc..2811631 100644
--- a/frame/ContentView.py
+++ b/frame/ContentView.py
@@ -1,13 +1,16 @@
-import sys
+import sys, os
from typing import Dict, List
-from PyQt5.QtCore import QPoint
-from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout
-from PyQt5.QtWidgets import QMenu
+from PyQt5.QtCore import QPoint, Qt
+from PyQt5.QtGui import QTransform
+from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QSlider, QDoubleSpinBox
+from PyQt5.QtWidgets import QMenu, QHBoxLayout
-from graph.DataType import Arrow, Point
+sys.path.append(os.path.dirname(os.path.dirname(__file__)))
+from graph.DataType import Arrow, Point, Line
from graph.directed_acyclic_graph.DAGPresent import DAGActiveView
-from parse.StoryMap import StoryMap, XAST_ParseTool
+from graph.undirected_graph.UDGPresent import UDGPresent
+from parse.StoryMap import StoryMap, XAST_ParseTool, ArticleSlice
from parse.ast_load import global_ast_path
@@ -24,7 +27,7 @@ class StorylinesView(QWidget):
self.fragment_view = DAGActiveView(self)
layout.setContentsMargins(0,0,0,0)
layout.addWidget(self.fragment_view)
- self.fragment_view.nodes_clicked.connect(self.highlisth_node_path)
+ self.fragment_view.nodes_clicked.connect(self.highlight_node_path)
self.present_graph: Dict[str, StoryMap] = {}
pass
pass
@@ -58,7 +61,7 @@ class StorylinesView(QWidget):
self.fragment_view.update_with_edges(arrows)
pass
- def highlisth_node_path(self, xpos, ypos, list):
+ def highlight_node_path(self, xpos, ypos, list):
if len(list) == 0:
return
@@ -92,12 +95,12 @@ class StorylinesView(QWidget):
pass
if len(story_list) == 1:
- self.highlisth_node_path(xpos, ypos, [("node", story_list[0])])
+ self.highlight_node_path(xpos, ypos, [("node", story_list[0])])
elif len(story_list) > 1:
menu = QMenu()
for story in story_list:
def trigger(story_name:str):
- return lambda : self.highlisth_node_path(xpos, ypos, [("node", story_name)])
+ return lambda : self.highlight_node_path(xpos, ypos, [("node", story_name)])
menu.addAction(f"story/{story}", trigger(story))
pass
@@ -116,16 +119,70 @@ class StorylinesView(QWidget):
class ArticleRefsView(QWidget):
def __init__(self, parent):
QWidget.__init__(self, parent)
+ self.refer_view = UDGPresent(self)
+ layout = QGridLayout(self)
+ layout.setContentsMargins(0, 0, 0, 0)
+ layout.setSpacing(0)
+ layout.addWidget(self.refer_view)
+
+ self.ppu_slider = QSlider(Qt.Orientation.Horizontal, self)
+ self.scala_slider = QSlider(Qt.Orientation.Vertical, self)
+ self.scala_slider.setRange(0, 1000)
+ self.scala_slider.setValue(100)
+ layout.addWidget(self.scala_slider, 0, 1)
+
+ slide_panel = QWidget(self)
+ layout.addWidget(slide_panel, 1, 0, 1, 2)
+ bottom_layout = QHBoxLayout(slide_panel)
+ bottom_layout.setSpacing(0)
+ bottom_layout.setContentsMargins(0, 0, 0, 0)
+ bottom_layout.addWidget(self.ppu_slider)
+
+ self.ppu_max = QDoubleSpinBox(self)
+ self.ppu_max.setRange(0, 2**31)
+ self.ppu_max.setValue(10000)
+ self.ppu_slider.setRange(0, 10000)
+ self.ppu_slider.setValue(int(self.refer_view.pixel_per_unit))
+ bottom_layout.addWidget(self.ppu_max)
+
+ self.refer_view.node_clicked.connect(self.highlight_sibling_nodes)
+
+ def scala_view(times:float):
+ tm = QTransform()
+ tm.scale(times, times)
+ return tm
+
+ self.scala_slider.valueChanged.connect(lambda iproc: self.refer_view.setTransform(scala_view(iproc/100.0)))
+ self.ppu_slider.valueChanged.connect(lambda ppu:self.refer_view.refresh_with_ppu(ppu))
+ self.ppu_slider.sliderReleased.connect(lambda : self.refer_view.update_scene_rect())
+ pass
+
+ def present_volumes_graph(self, ref_graph: List[ArticleSlice]):
+ node_edges = []
+
+ for line in ref_graph:
+ for target in line.refer_target:
+ node_edges.append(Line(Point(line.article_fullname), Point(target)))
+ pass
+ pass
+
+ self.refer_view.rebuild_from_edges(node_edges)
+ pass
+
+ def highlight_sibling_nodes(self, nodename: str):
+ self.refer_view.highlight_sibling_nodes(nodename)
pass
if __name__ == "__main__":
app = QApplication(sys.argv)
- view = StorylinesView(None)
+ #view = StorylinesView(None)
+ view = ArticleRefsView(None)
view.show()
tool = XAST_ParseTool(global_ast_path)
- view.present_stories_graph(tool.get_story_graph())
+# view.present_stories_graph(tool.get_story_graph())
+ view.present_volumes_graph(tool.get_article_nodes())
# view.fragment_view.highlight_graph_link(["血脉的源头", "血脉的源头&待续"])
diff --git a/graph/undirected_graph/UDGPresent.py b/graph/undirected_graph/UDGPresent.py
index b1bcd1f..55658bf 100644
--- a/graph/undirected_graph/UDGPresent.py
+++ b/graph/undirected_graph/UDGPresent.py
@@ -1,4 +1,4 @@
-import sys
+import sys, os
from abc import abstractmethod
from enum import Enum
from typing import List, Dict, Tuple
@@ -7,7 +7,9 @@ import networkx as nx
from PyQt5.QtCore import QPointF, QRectF, Qt, pyqtSignal
from PyQt5.QtGui import QFont, QPainterPath, QPen, QPainter, QMouseEvent
from PyQt5.QtWidgets import QGraphicsItem, QGraphicsView, QApplication, QGraphicsScene
+from PyQt5.QtWidgets import QSplitter, QHBoxLayout
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
from graph.DataType import Point, Line
@@ -36,15 +38,20 @@ class GraphNode:
@abstractmethod
def pos(self) -> QPointF:
raise NotImplementedError("pos")
+
+ @abstractmethod
+ def relayout_exec(self, ppu: float = 1):
+ raise NotImplementedError("relayout_exec")
class PresentNode(QGraphicsItem, GraphNode):
- def __init__(self, name: str, font: QFont, parent):
+ def __init__(self, name: str, font: QFont, prim_x:float, prim_y:float, parent):
QGraphicsItem.__init__(self, parent)
self.node_name = name
self.__is_highlight_mark = False
self.__font_bind = font
self.__sibling_list = []
+ self.__prim_pos: Tuple[float, float] = (prim_x, prim_y)
self.setZValue(10)
pass
@@ -85,7 +92,7 @@ class PresentNode(QGraphicsItem, GraphNode):
painter.save()
painter.setRenderHint(QPainter.Antialiasing)
- painter.drawRect(outline)
+ #painter.drawRect(outline)
if self.__is_highlight_mark:
brush = Qt.red
@@ -102,6 +109,13 @@ class PresentNode(QGraphicsItem, GraphNode):
painter.restore()
pass
+ def relayout_exec(self, ppu: float = 1):
+ pos_x = ppu * self.__prim_pos[0]
+ pos_y = ppu * self.__prim_pos[1]
+ self.setPos(pos_x, pos_y)
+ self.update()
+ pass
+
class ConnectionNode(QGraphicsItem, GraphNode):
def __init__(self, p0: GraphNode, p1: GraphNode, parent):
@@ -124,7 +138,7 @@ class ConnectionNode(QGraphicsItem, GraphNode):
return self.__highlight_mark
pass
- def relayout_exec(self):
+ def relayout_exec(self, ppi: float = 1):
start_pos = self.__point0.pos()
end_pos = self.__point1.pos()
@@ -135,6 +149,7 @@ class ConnectionNode(QGraphicsItem, GraphNode):
self.setPos(QPointF(start_x, start_y))
self.__outline = QRectF(0, 0, end_x - start_x, end_y - start_y)
+ self.update()
pass
def boundingRect(self):
@@ -176,6 +191,8 @@ class UDGPresent(QGraphicsView):
def __init__(self, parent):
QGraphicsView.__init__(self, parent)
+ self.pixel_per_unit = 5000
+
self.__highlight_nodes: List[GraphNode] = []
self.node_set: Dict[str, GraphNode] = {}
self.__layout_graph = nx.Graph()
@@ -190,69 +207,38 @@ class UDGPresent(QGraphicsView):
def rebuild_from_edges(self, line_set: List[Line]):
self.node_set.clear()
- edge_set: Dict[str, Tuple[GraphNode, GraphNode]] = {}
-
for line in line_set:
start_node = line.points()[0]
- if start_node.point_name not in self.node_set:
- self.node_set[start_node.point_name] = PresentNode(start_node.point_name, self.font(), None)
- pass
-
self.__layout_graph.add_node(start_node.point_name)
end_node = line.points()[1]
- if start_node.point_name == end_node.point_name:
- continue
- if end_node.point_name not in self.node_set:
- self.node_set[end_node.point_name] = PresentNode(end_node.point_name, self.font(), None)
- pass
-
self.__layout_graph.add_node(end_node.point_name)
+
self.__layout_graph.add_edge(start_node.point_name, end_node.point_name)
-
- start_force_point: PresentNode = self.node_set[start_node.point_name]
- other_force_point: PresentNode = self.node_set[end_node.point_name]
-
- if other_force_point not in start_force_point.sibling_nodes():
- start_force_point.sibling_append(other_force_point)
- if start_force_point not in other_force_point.sibling_nodes():
- other_force_point.sibling_append(start_force_point)
-
pass
-
+
pos_map = nx.spring_layout(self.__layout_graph)
- scala_value:float = 0
- for name in pos_map:
- primitive_pos = pos_map[name]
- target_gnode: PresentNode = self.node_set[name]
+ for node_name in pos_map:
+ node_prim_pos = pos_map[node_name]
+ targetx_node = PresentNode(node_name, self.font(), node_prim_pos[0], node_prim_pos[1], None)
+ self.node_set[node_name] = targetx_node
- sibling_nodes = target_gnode.sibling_nodes()
- for sib in sibling_nodes:
- sib_primitive_pos = pos_map[sib.node_name]
- prim_x_span = primitive_pos[0] - sib_primitive_pos[0]
- prim_y_span = primitive_pos[1] - sib_primitive_pos[1]
-
- target_rect = target_gnode.boundingRect()
- scala_value = max(scala_value, target_rect.width()/prim_x_span)
- scala_value = max(scala_value, target_rect.height()/prim_y_span)
- pass
- pass
-
- for name in pos_map:
- primitive_pos = pos_map[name]
- target_gnode: PresentNode = self.node_set[name]
- target_gnode.setPos(primitive_pos[0] * scala_value, primitive_pos[1] * scala_value)
- self.__scene_bind.addItem(target_gnode)
+ self.scene().addItem(targetx_node)
+ targetx_node.relayout_exec(self.pixel_per_unit)
pass
for edge in nx.edges(self.__layout_graph):
edge_start = edge[0]
edge_end = edge[1]
- node_one = self.node_set[edge_start]
- node_two = self.node_set[edge_end]
+ node_one: PresentNode = self.node_set[edge_start]
+ node_two: PresentNode = self.node_set[edge_end]
+ node_one.sibling_append(node_two)
+ node_two.sibling_append(node_one)
+
connection = ConnectionNode(node_one, node_two, None)
self.scene().addItem(connection)
- connection.relayout_exec()
+ connection.relayout_exec(self.pixel_per_unit)
+ self.node_set[f"conn::{edge_start}&{edge_end}"] = connection
pass
pass
@@ -260,7 +246,7 @@ class UDGPresent(QGraphicsView):
QGraphicsView.mousePressEvent(self, event)
if event.button() == Qt.MouseButton.LeftButton:
item: GraphNode = self.itemAt(event.pos())
- if item.node_type() == PresentNodeType.PresentNode:
+ if item is not None and item.node_type() == PresentNodeType.PresentNode:
vnode: PresentNode = item
self.node_clicked.emit(vnode.node_name)
print(vnode.node_name)
@@ -268,6 +254,74 @@ class UDGPresent(QGraphicsView):
pass
pass
+ def refresh_with_ppu(self, ppu: float):
+ self.pixel_per_unit = ppu
+
+ for node in self.node_set.values():
+ if node.node_type() == PresentNodeType.PresentNode:
+ node.relayout_exec(ppu)
+ pass
+ pass
+
+ for node in self.node_set.values():
+ if node.node_type() == PresentNodeType.ConnectionNode:
+ node.relayout_exec(ppu)
+ pass
+ pass
+
+ self.scene().update()
+ self.update()
+ pass
+
+ def update_scene_rect(self):
+ minx = 2*64
+ miny = 2*64
+ maxx = -2**64
+ maxy = -2**64
+ for item in self.node_set.values():
+ minx = min(item.pos().x(), minx)
+ miny = min(item.pos().y(), miny)
+ maxx = max(item.pos().x() + item.boundingRect().width(), maxx)
+ maxy = max(item.pos().y() + item.boundingRect().height(), maxy)
+ pass
+
+ self.scene().setSceneRect(minx, miny, maxx - minx, maxy - miny)
+ pass
+
+ def highlight_sibling_nodes(self, center_node: str):
+ for node in self.__highlight_nodes:
+ node.highlight(False)
+ pass
+ self.__highlight_nodes.clear()
+
+ target_node: PresentNode = self.node_set[center_node]
+ if target_node is None:
+ return
+
+ target_node.highlight(True)
+ self.__highlight_nodes.append(target_node)
+ for sib_node in target_node.sibling_nodes():
+ sib_node.highlight(True)
+ self.__highlight_nodes.append(sib_node)
+
+ sib_name = sib_node.node_name
+ constr1 = f"conn::{center_node}&{sib_name}"
+ constr2 = f"conn::{sib_name}&{center_node}"
+ if constr1 in self.node_set:
+ self.node_set[constr1].highlight(True)
+ self.__highlight_nodes.append(self.node_set[constr1])
+ elif constr2 in self.node_set:
+ self.node_set[constr2].highlight(True)
+ self.__highlight_nodes.append(self.node_set[constr2])
+ pass
+ pass
+
+ self.scene().update()
+ self.update()
+ pass
+
+
+
if __name__ == "__main__":
app = QApplication(sys.argv)
diff --git a/graph/undirected_graph/__pycache__/UDGPresent.cpython-312.pyc b/graph/undirected_graph/__pycache__/UDGPresent.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1feb6352911537e89646810a126e780cea512437
GIT binary patch
literal 18839
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
literal 0
HcmV?d00001
diff --git a/graph/undirected_graph/__pycache__/__init__.cpython-312.pyc b/graph/undirected_graph/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..df9b4c2b919fff61fe6ac91d8480dd43b035cdb9
GIT binary patch
literal 161
zcmX@j%ge<81m^EorGeJmtkIamWj77{q762gaDa8N)
literal 0
HcmV?d00001
diff --git a/parse/StoryMap.py b/parse/StoryMap.py
index f90d359..15e2540 100644
--- a/parse/StoryMap.py
+++ b/parse/StoryMap.py
@@ -27,6 +27,24 @@ class FragmentSlice(EmptyNode):
pass
+class ArticleSlice(EmptyNode):
+ def __init__(self, a_name: str, v_name: str):
+ self.article_name = a_name
+ self.volume_blongs = v_name
+ self.article_fullname = f"{v_name}@{a_name}"
+
+ self.refer_target: List[str] = [] # 引用切面
+ self.text_sections: List[str] = [] # 文本段落
+ pass
+
+ def get_from_memory(self) -> str:
+ return "\n".join(self.text_sections)
+
+ def set_to_memory(self, content: str):
+ self.text_sections = content.split("\n")
+ pass
+
+
class StoryMap:
def __init__(self, name: str):
self.story_name = name
@@ -44,7 +62,7 @@ class StoryMap:
self.slice_list.append(node)
pass
- def get_fragment_defined(self, name: str) -> FragmentSlice:
+ def get_fragment_defined(self, name: str) -> FragmentSlice|None:
for fit in self.slice_list:
if fit.is_define_node[0]:
if fit.is_define_node[1] == name:
@@ -140,6 +158,58 @@ class XAST_ParseTool:
self.storylines_plait(story_dict)
return story_dict
+ def get_article_nodes(self) -> List[ArticleSlice]:
+ retvalues = []
+
+ hangout_nodes = ArticleSlice("悬空节点", "")
+ retvalues.append(hangout_nodes)
+
+ fragments = self.__get_all_fragment_names()
+ volumes = self.dom_tree.getElementsByTagName("volume")
+ for vnode in volumes:
+ child_articles = self.__volume_node_parse(vnode)
+ retvalues.extend(child_articles)
+ for child in child_articles:
+ for refn in child.refer_target:
+ if refn in fragments:
+ fragments.remove(refn)
+ pass
+ pass
+ pass
+ pass
+
+ hangout_nodes.refer_target.extend(fragments)
+ return retvalues
+
+ def __get_all_fragment_names(self) -> List[str]:
+ values = []
+
+ frags = self.dom_tree.getElementsByTagName("fragment")
+ for frag in frags:
+ story: mdom.Element = frag.parentNode
+ values.append(f"{story.getAttribute("name")}&{frag.getAttribute("name")}")
+ pass
+
+ return values
+
+ def __volume_node_parse(self, vnode: mdom.Element)-> List[ArticleSlice]:
+ retvalues = []
+
+ vname = vnode.getAttribute("name")
+ articles = vnode.getElementsByTagName("article")
+ for anode in articles:
+ aname = anode.getAttribute("name")
+ node_inst = ArticleSlice(aname, vname)
+ retvalues.append(node_inst)
+ refsnode = anode.getElementsByTagName("refer")
+ for refnode in refsnode:
+ ref_story = refnode.getAttribute("story")
+ ref_fragment = refnode.getAttribute("fragment")
+ node_inst.refer_target.append(f"{ref_story}&{ref_fragment}")
+ pass
+ pass
+
+ return retvalues
pass
diff --git a/parse/__pycache__/StoryMap.cpython-312.pyc b/parse/__pycache__/StoryMap.cpython-312.pyc
index 23db8e76a363c63c844e766b6620b237d2b0a975..edc8c0b18098f6591cc38dccc2babcdc0574c548 100644
GIT binary patch
delta 3924
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
delta 1163
zcmZ{jO>7fa5XX1cYp=cjNc6>*@sg>~I=l5p*GjHa-
z*|n*^ha!)|VMXA-?^Yko4kni(DOg_0PZU>0G77?EP!!Gx75^=v68P5^swkRQ3#Pfh
z64YcZtOaL9+RaLp&@E{)(f~F5@b?#DL=_!rzOL6;l6zHr;u!%G_T?(mvd4c%&b
zMApUyY&ZTa4RXa(Y1)@1Lk`b)zro)kA^bSds_^tao?dRq_IN-B$l)br5VH0?WgLRN
zq>mE{xE0JpALc@*A&>K+IdMkBR$l@)LU*7S>)|Olh>yb;&X!a^`;a2`67~`H6H){}
zp`UP^pc1G@wu{iUgATqh3pf|)YJDrn-b7PF?wO@%Qd}4Cm&m%*h^jIBua?6AN3bV)
zJA8=yRd2KD0bGj~;h61-Ekk>eJDkLvW{ntHRjXNMu`ynrE0wDnJA`)p7!2d9cov53
zNMaO7C`5eitGfVX`if5k^#t2l0*1@N6Dfj~2z|JcoQCtbm23q1NDJDG
z2Y-kq?4`Dtn5X=z;wZB^Gp$osbfe6b7{y01f)wrCYhZN}2;HAkYIDzhmPUn024+=^*X7+nv$%&}APcG|TMzJwZf
zwqJu1{?xwUJI5tX9>0&a;lWIr8$ZdMg$6EXj=-q>BqKxkC}pBq=EwEoA06>d_caq_
zE|A&Zd<6FGJ6}Y<4Sh3QzwPTiJ4ZNH4MF`O&hvteN-)9Wv57MpuPJ7+(o&w
z??y?DzkirKq#h@61NXbeiDa=@+!}5)8sT;VZ)b=I|uh2tMl6e1sbZ
z9w~x1&>X{)_`FMNal3$KyB^lSsqPMCf#L{1;?3@XJuOblV%6xW)T=#Jy{7Yj$3u{}
zcWelsb@v~3>voTHzY{mfe+zo&KT)k$77T5Geao{06*YmM6>{m#-V`L>2ppUJ7Xboq
M1mbHZ;{8JIA6!xjod5s;