章节节点引用视图构建完成
This commit is contained in:
parent
d4bf6b186c
commit
9b59b0a458
|
@ -5,14 +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$/graph/undirected_graph/UDGLayout.py" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/graph/undirected_graph/UDGPresent.py" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/graph/undirected_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$/entry.py" beforeDir="false" afterPath="$PROJECT_DIR$/entry.py" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/frame/ContentView.py" beforeDir="false" afterPath="$PROJECT_DIR$/frame/ContentView.py" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/frame/ContentView.py" beforeDir="false" afterPath="$PROJECT_DIR$/frame/ContentView.py" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/graph/DataType.py" beforeDir="false" afterPath="$PROJECT_DIR$/graph/DataType.py" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/graph/undirected_graph/UDGPresent.py" beforeDir="false" afterPath="$PROJECT_DIR$/graph/undirected_graph/UDGPresent.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" />
|
<change beforePath="$PROJECT_DIR$/parse/StoryMap.py" beforeDir="false" afterPath="$PROJECT_DIR$/parse/StoryMap.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" />
|
||||||
|
@ -40,40 +36,40 @@
|
||||||
<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": {
|
"keyToString": {
|
||||||
"Python.CompareViews.executor": "Run",
|
"Python.CompareViews.executor": "Run",
|
||||||
"Python.CompareWindow.executor": "Run",
|
"Python.CompareWindow.executor": "Run",
|
||||||
"Python.ContentView.executor": "Run",
|
"Python.ContentView.executor": "Debug",
|
||||||
"Python.DAGGraph (1).executor": "Run",
|
"Python.DAGGraph (1).executor": "Run",
|
||||||
"Python.DAGGraph.executor": "Run",
|
"Python.DAGGraph.executor": "Run",
|
||||||
"Python.DAGLayout (1).executor": "Run",
|
"Python.DAGLayout (1).executor": "Run",
|
||||||
"Python.DAGLayout.executor": "Run",
|
"Python.DAGLayout.executor": "Run",
|
||||||
"Python.DAGPresent.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",
|
||||||
"Python.ReferView.executor": "Run",
|
"Python.ReferView.executor": "Run",
|
||||||
"Python.StoryMap.executor": "Run",
|
"Python.StoryMap.executor": "Run",
|
||||||
"Python.UDGLayout.executor": "Run",
|
"Python.UDGLayout.executor": "Run",
|
||||||
"Python.UDGPresent.executor": "Run",
|
"Python.UDGPresent.executor": "Run",
|
||||||
"Python.ast_load.executor": "Debug",
|
"Python.ast_load.executor": "Debug",
|
||||||
"Python.entry.executor": "Run",
|
"Python.entry.executor": "Run",
|
||||||
"Python.test.executor": "Run",
|
"Python.test.executor": "Run",
|
||||||
"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/StoryTools",
|
"last_opened_file_path": "D:/Projects/Python/StoryTools",
|
||||||
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable"
|
"settings.editor.selected.configurable": "debugger.dataViews"
|
||||||
}
|
}
|
||||||
}]]></component>
|
}</component>
|
||||||
<component name="PyDebuggerOptionsProvider">
|
<component name="PyDebuggerOptionsProvider">
|
||||||
<option name="mySaveCallSignatures" value="true" />
|
<option name="mySaveCallSignatures" value="true" />
|
||||||
<option name="mySupportGeventDebugging" value="true" />
|
<option name="mySupportGeventDebugging" value="true" />
|
||||||
<option name="myDropIntoDebuggerOnFailedTests" value="true" />
|
<option name="myDropIntoDebuggerOnFailedTests" value="true" />
|
||||||
<option name="myPyQtBackend" value="pyqt5" />
|
<option name="myPyQtBackend" value="pyqt5" />
|
||||||
</component>
|
</component>
|
||||||
<component name="RunManager" selected="Python.UDGPresent">
|
<component name="RunManager" selected="Python.ContentView">
|
||||||
<configuration name="ContentView" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
<configuration name="ContentView" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||||
<module name="StoryTools" />
|
<module name="StoryTools" />
|
||||||
<option name="ENV_FILES" value="" />
|
<option name="ENV_FILES" value="" />
|
||||||
|
@ -193,8 +189,8 @@
|
||||||
</list>
|
</list>
|
||||||
<recent_temporary>
|
<recent_temporary>
|
||||||
<list>
|
<list>
|
||||||
<item itemvalue="Python.UDGPresent" />
|
|
||||||
<item itemvalue="Python.ContentView" />
|
<item itemvalue="Python.ContentView" />
|
||||||
|
<item itemvalue="Python.UDGPresent" />
|
||||||
<item itemvalue="Python.test" />
|
<item itemvalue="Python.test" />
|
||||||
<item itemvalue="Python.UDGLayout" />
|
<item itemvalue="Python.UDGLayout" />
|
||||||
<item itemvalue="Python.DAGPresent" />
|
<item itemvalue="Python.DAGPresent" />
|
||||||
|
@ -222,4 +218,15 @@
|
||||||
<component name="UnknownFeatures">
|
<component name="UnknownFeatures">
|
||||||
<option featureType="com.intellij.fileTypeFactory" implementationName="*.bat" />
|
<option featureType="com.intellij.fileTypeFactory" implementationName="*.bat" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="XDebuggerManager">
|
||||||
|
<breakpoint-manager>
|
||||||
|
<breakpoints>
|
||||||
|
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
|
||||||
|
<url>file://$PROJECT_DIR$/graph/undirected_graph/UDGPresent.py</url>
|
||||||
|
<line>239</line>
|
||||||
|
<option name="timeStamp" value="2" />
|
||||||
|
</line-breakpoint>
|
||||||
|
</breakpoints>
|
||||||
|
</breakpoint-manager>
|
||||||
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -1,13 +1,16 @@
|
||||||
import sys
|
import sys, os
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
from PyQt5.QtCore import QPoint
|
from PyQt5.QtCore import QPoint, Qt
|
||||||
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout
|
from PyQt5.QtGui import QTransform
|
||||||
from PyQt5.QtWidgets import QMenu
|
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 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
|
from parse.ast_load import global_ast_path
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,7 +27,7 @@ class StorylinesView(QWidget):
|
||||||
self.fragment_view = DAGActiveView(self)
|
self.fragment_view = DAGActiveView(self)
|
||||||
layout.setContentsMargins(0,0,0,0)
|
layout.setContentsMargins(0,0,0,0)
|
||||||
layout.addWidget(self.fragment_view)
|
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] = {}
|
self.present_graph: Dict[str, StoryMap] = {}
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
|
@ -58,7 +61,7 @@ class StorylinesView(QWidget):
|
||||||
self.fragment_view.update_with_edges(arrows)
|
self.fragment_view.update_with_edges(arrows)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def highlisth_node_path(self, xpos, ypos, list):
|
def highlight_node_path(self, xpos, ypos, list):
|
||||||
if len(list) == 0:
|
if len(list) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -92,12 +95,12 @@ class StorylinesView(QWidget):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if len(story_list) == 1:
|
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:
|
elif len(story_list) > 1:
|
||||||
menu = QMenu()
|
menu = QMenu()
|
||||||
for story in story_list:
|
for story in story_list:
|
||||||
def trigger(story_name:str):
|
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))
|
menu.addAction(f"story/{story}", trigger(story))
|
||||||
pass
|
pass
|
||||||
|
@ -116,16 +119,70 @@ class StorylinesView(QWidget):
|
||||||
class ArticleRefsView(QWidget):
|
class ArticleRefsView(QWidget):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QWidget.__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
|
pass
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
view = StorylinesView(None)
|
#view = StorylinesView(None)
|
||||||
|
view = ArticleRefsView(None)
|
||||||
view.show()
|
view.show()
|
||||||
|
|
||||||
tool = XAST_ParseTool(global_ast_path)
|
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(["血脉的源头", "血脉的源头&待续"])
|
# view.fragment_view.highlight_graph_link(["血脉的源头", "血脉的源头&待续"])
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import sys
|
import sys, os
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import List, Dict, Tuple
|
from typing import List, Dict, Tuple
|
||||||
|
@ -7,7 +7,9 @@ import networkx as nx
|
||||||
from PyQt5.QtCore import QPointF, QRectF, Qt, pyqtSignal
|
from PyQt5.QtCore import QPointF, QRectF, Qt, pyqtSignal
|
||||||
from PyQt5.QtGui import QFont, QPainterPath, QPen, QPainter, QMouseEvent
|
from PyQt5.QtGui import QFont, QPainterPath, QPen, QPainter, QMouseEvent
|
||||||
from PyQt5.QtWidgets import QGraphicsItem, QGraphicsView, QApplication, QGraphicsScene
|
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
|
from graph.DataType import Point, Line
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,15 +38,20 @@ class GraphNode:
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def pos(self) -> QPointF:
|
def pos(self) -> QPointF:
|
||||||
raise NotImplementedError("pos")
|
raise NotImplementedError("pos")
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def relayout_exec(self, ppu: float = 1):
|
||||||
|
raise NotImplementedError("relayout_exec")
|
||||||
|
|
||||||
|
|
||||||
class PresentNode(QGraphicsItem, GraphNode):
|
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)
|
QGraphicsItem.__init__(self, parent)
|
||||||
self.node_name = name
|
self.node_name = name
|
||||||
self.__is_highlight_mark = False
|
self.__is_highlight_mark = False
|
||||||
self.__font_bind = font
|
self.__font_bind = font
|
||||||
self.__sibling_list = []
|
self.__sibling_list = []
|
||||||
|
self.__prim_pos: Tuple[float, float] = (prim_x, prim_y)
|
||||||
self.setZValue(10)
|
self.setZValue(10)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -85,7 +92,7 @@ class PresentNode(QGraphicsItem, GraphNode):
|
||||||
|
|
||||||
painter.save()
|
painter.save()
|
||||||
painter.setRenderHint(QPainter.Antialiasing)
|
painter.setRenderHint(QPainter.Antialiasing)
|
||||||
painter.drawRect(outline)
|
#painter.drawRect(outline)
|
||||||
|
|
||||||
if self.__is_highlight_mark:
|
if self.__is_highlight_mark:
|
||||||
brush = Qt.red
|
brush = Qt.red
|
||||||
|
@ -102,6 +109,13 @@ class PresentNode(QGraphicsItem, GraphNode):
|
||||||
painter.restore()
|
painter.restore()
|
||||||
pass
|
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):
|
class ConnectionNode(QGraphicsItem, GraphNode):
|
||||||
def __init__(self, p0: GraphNode, p1: GraphNode, parent):
|
def __init__(self, p0: GraphNode, p1: GraphNode, parent):
|
||||||
|
@ -124,7 +138,7 @@ class ConnectionNode(QGraphicsItem, GraphNode):
|
||||||
return self.__highlight_mark
|
return self.__highlight_mark
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def relayout_exec(self):
|
def relayout_exec(self, ppi: float = 1):
|
||||||
start_pos = self.__point0.pos()
|
start_pos = self.__point0.pos()
|
||||||
end_pos = self.__point1.pos()
|
end_pos = self.__point1.pos()
|
||||||
|
|
||||||
|
@ -135,6 +149,7 @@ class ConnectionNode(QGraphicsItem, GraphNode):
|
||||||
|
|
||||||
self.setPos(QPointF(start_x, start_y))
|
self.setPos(QPointF(start_x, start_y))
|
||||||
self.__outline = QRectF(0, 0, end_x - start_x, end_y - start_y)
|
self.__outline = QRectF(0, 0, end_x - start_x, end_y - start_y)
|
||||||
|
self.update()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def boundingRect(self):
|
def boundingRect(self):
|
||||||
|
@ -176,6 +191,8 @@ class UDGPresent(QGraphicsView):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QGraphicsView.__init__(self, parent)
|
QGraphicsView.__init__(self, parent)
|
||||||
|
self.pixel_per_unit = 5000
|
||||||
|
|
||||||
self.__highlight_nodes: List[GraphNode] = []
|
self.__highlight_nodes: List[GraphNode] = []
|
||||||
self.node_set: Dict[str, GraphNode] = {}
|
self.node_set: Dict[str, GraphNode] = {}
|
||||||
self.__layout_graph = nx.Graph()
|
self.__layout_graph = nx.Graph()
|
||||||
|
@ -190,69 +207,38 @@ class UDGPresent(QGraphicsView):
|
||||||
def rebuild_from_edges(self, line_set: List[Line]):
|
def rebuild_from_edges(self, line_set: List[Line]):
|
||||||
self.node_set.clear()
|
self.node_set.clear()
|
||||||
|
|
||||||
edge_set: Dict[str, Tuple[GraphNode, GraphNode]] = {}
|
|
||||||
|
|
||||||
for line in line_set:
|
for line in line_set:
|
||||||
start_node = line.points()[0]
|
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)
|
self.__layout_graph.add_node(start_node.point_name)
|
||||||
|
|
||||||
end_node = line.points()[1]
|
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_node(end_node.point_name)
|
||||||
|
|
||||||
self.__layout_graph.add_edge(start_node.point_name, 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
|
pass
|
||||||
|
|
||||||
pos_map = nx.spring_layout(self.__layout_graph)
|
pos_map = nx.spring_layout(self.__layout_graph)
|
||||||
scala_value:float = 0
|
for node_name in pos_map:
|
||||||
for name in pos_map:
|
node_prim_pos = pos_map[node_name]
|
||||||
primitive_pos = pos_map[name]
|
targetx_node = PresentNode(node_name, self.font(), node_prim_pos[0], node_prim_pos[1], None)
|
||||||
target_gnode: PresentNode = self.node_set[name]
|
self.node_set[node_name] = targetx_node
|
||||||
|
|
||||||
sibling_nodes = target_gnode.sibling_nodes()
|
self.scene().addItem(targetx_node)
|
||||||
for sib in sibling_nodes:
|
targetx_node.relayout_exec(self.pixel_per_unit)
|
||||||
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)
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
for edge in nx.edges(self.__layout_graph):
|
for edge in nx.edges(self.__layout_graph):
|
||||||
edge_start = edge[0]
|
edge_start = edge[0]
|
||||||
edge_end = edge[1]
|
edge_end = edge[1]
|
||||||
node_one = self.node_set[edge_start]
|
node_one: PresentNode = self.node_set[edge_start]
|
||||||
node_two = self.node_set[edge_end]
|
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)
|
connection = ConnectionNode(node_one, node_two, None)
|
||||||
self.scene().addItem(connection)
|
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
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -260,7 +246,7 @@ class UDGPresent(QGraphicsView):
|
||||||
QGraphicsView.mousePressEvent(self, event)
|
QGraphicsView.mousePressEvent(self, event)
|
||||||
if event.button() == Qt.MouseButton.LeftButton:
|
if event.button() == Qt.MouseButton.LeftButton:
|
||||||
item: GraphNode = self.itemAt(event.pos())
|
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
|
vnode: PresentNode = item
|
||||||
self.node_clicked.emit(vnode.node_name)
|
self.node_clicked.emit(vnode.node_name)
|
||||||
print(vnode.node_name)
|
print(vnode.node_name)
|
||||||
|
@ -268,6 +254,74 @@ class UDGPresent(QGraphicsView):
|
||||||
pass
|
pass
|
||||||
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__":
|
if __name__ == "__main__":
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -27,6 +27,24 @@ class FragmentSlice(EmptyNode):
|
||||||
pass
|
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:
|
class StoryMap:
|
||||||
def __init__(self, name: str):
|
def __init__(self, name: str):
|
||||||
self.story_name = name
|
self.story_name = name
|
||||||
|
@ -44,7 +62,7 @@ class StoryMap:
|
||||||
self.slice_list.append(node)
|
self.slice_list.append(node)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_fragment_defined(self, name: str) -> FragmentSlice:
|
def get_fragment_defined(self, name: str) -> FragmentSlice|None:
|
||||||
for fit in self.slice_list:
|
for fit in self.slice_list:
|
||||||
if fit.is_define_node[0]:
|
if fit.is_define_node[0]:
|
||||||
if fit.is_define_node[1] == name:
|
if fit.is_define_node[1] == name:
|
||||||
|
@ -140,6 +158,58 @@ class XAST_ParseTool:
|
||||||
self.storylines_plait(story_dict)
|
self.storylines_plait(story_dict)
|
||||||
return 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
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue