Compare commits

..

No commits in common. "9b59b0a4585e2d0d194e8fb7be1e802a21d19abe" and "b7f71fb29f17ac3d4b736f84bca6fd5e29715fa0" have entirely different histories.

15 changed files with 95 additions and 819 deletions

View File

@ -6,9 +6,7 @@
<component name="ChangeListManager">
<list default="true" id="f609c0f2-cd0d-4eea-87f1-8caf02d3f04f" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frame/ContentView.py" beforeDir="false" afterPath="$PROJECT_DIR$/frame/ContentView.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$/parse/StoryMap.py" beforeDir="false" afterPath="$PROJECT_DIR$/parse/StoryMap.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" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -40,7 +38,7 @@
&quot;keyToString&quot;: {
&quot;Python.CompareViews.executor&quot;: &quot;Run&quot;,
&quot;Python.CompareWindow.executor&quot;: &quot;Run&quot;,
&quot;Python.ContentView.executor&quot;: &quot;Debug&quot;,
&quot;Python.ContentView.executor&quot;: &quot;Run&quot;,
&quot;Python.DAGGraph (1).executor&quot;: &quot;Run&quot;,
&quot;Python.DAGGraph.executor&quot;: &quot;Run&quot;,
&quot;Python.DAGLayout (1).executor&quot;: &quot;Run&quot;,
@ -51,25 +49,38 @@
&quot;Python.NovelManage.executor&quot;: &quot;Debug&quot;,
&quot;Python.ReferView.executor&quot;: &quot;Run&quot;,
&quot;Python.StoryMap.executor&quot;: &quot;Run&quot;,
&quot;Python.UDGLayout.executor&quot;: &quot;Run&quot;,
&quot;Python.UDGPresent.executor&quot;: &quot;Run&quot;,
&quot;Python.ast_load.executor&quot;: &quot;Debug&quot;,
&quot;Python.entry.executor&quot;: &quot;Run&quot;,
&quot;Python.test.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;last_opened_file_path&quot;: &quot;D:/Projects/Python/StoryTools&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;debugger.dataViews&quot;
&quot;last_opened_file_path&quot;: &quot;D:/Projects/Python/StoryCheckTools&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;reference.settings.ide.settings.new.ui&quot;
}
}</component>
<component name="PyDebuggerOptionsProvider">
<option name="mySaveCallSignatures" value="true" />
<option name="mySupportGeventDebugging" value="true" />
<option name="myDropIntoDebuggerOnFailedTests" value="true" />
<option name="myPyQtBackend" value="pyqt5" />
</component>
<component name="RunManager" selected="Python.ContentView">
<configuration name="CompareViews" 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$/frame" />
<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$/frame/CompareViews.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>
<configuration name="ContentView" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="StoryTools" />
<option name="ENV_FILES" value="" />
@ -92,6 +103,28 @@
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="DAGLayout" 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$/graph/directed_acyclic_graph" />
<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$/graph/directed_acyclic_graph/DAGLayout.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>
<configuration name="DAGPresent" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="StoryTools" />
<option name="ENV_FILES" value="" />
@ -114,7 +147,7 @@
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="UDGLayout" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<configuration name="NovelManage" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="StoryTools" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
@ -123,56 +156,12 @@
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/graph/undirected_graph" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/manage" />
<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$/graph/undirected_graph/UDGLayout.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>
<configuration name="UDGPresent" 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$/graph/undirected_graph" />
<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$/graph/undirected_graph/UDGPresent.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>
<configuration name="test" 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$/graph/undirected_graph" />
<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$/graph/undirected_graph/test.py" />
<option name="PARAMETERS" value="" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/manage/NovelManage.py" />
<option name="PARAMETERS" value="wnss -cmp" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
@ -181,26 +170,26 @@
<method v="2" />
</configuration>
<list>
<item itemvalue="Python.UDGPresent" />
<item itemvalue="Python.test" />
<item itemvalue="Python.UDGLayout" />
<item itemvalue="Python.ContentView" />
<item itemvalue="Python.DAGLayout" />
<item itemvalue="Python.DAGPresent" />
<item itemvalue="Python.CompareViews" />
<item itemvalue="Python.NovelManage" />
</list>
<recent_temporary>
<list>
<item itemvalue="Python.ContentView" />
<item itemvalue="Python.UDGPresent" />
<item itemvalue="Python.test" />
<item itemvalue="Python.UDGLayout" />
<item itemvalue="Python.DAGPresent" />
<item itemvalue="Python.DAGLayout" />
<item itemvalue="Python.CompareViews" />
<item itemvalue="Python.NovelManage" />
</list>
</recent_temporary>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-python-sdk-975db3bf15a3-31b6be0877a2-com.jetbrains.pycharm.community.sharedIndexes.bundled-PC-241.18034.82" />
<option value="bundled-python-sdk-5b207ade9991-746f403e7f0c-com.jetbrains.pycharm.community.sharedIndexes.bundled-PC-241.17890.14" />
</set>
</attachedChunks>
</component>
@ -218,15 +207,4 @@
<component name="UnknownFeatures">
<option featureType="com.intellij.fileTypeFactory" implementationName="*.bat" />
</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>

View File

@ -1 +1,9 @@
print(__file__)
from parse.StoryMap import XAST_ParseTool, storyline_list2map
from parse.ast_load import global_ast_path
astx = XAST_ParseTool(global_ast_path)
storys = astx.story_list
storys_map = storyline_list2map(storys)
astx.storylines_plait(storys_map)
print(storys_map)

View File

@ -1,16 +1,13 @@
import sys, os
import sys
from typing import Dict, List
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 PyQt5.QtCore import QPoint
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtWidgets import QMenu
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from graph.DataType import Arrow, Point, Line
from graph.DataType import Arrow, Point
from graph.directed_acyclic_graph.DAGPresent import DAGActiveView
from graph.undirected_graph.UDGPresent import UDGPresent
from parse.StoryMap import StoryMap, XAST_ParseTool, ArticleSlice
from parse.StoryMap import StoryMap, XAST_ParseTool
from parse.ast_load import global_ast_path
@ -20,15 +17,13 @@ class FragmentPoint(Point):
pass
class StorylinesView(QWidget):
class ContentWindow(QMainWindow):
def __init__(self, parent):
QWidget.__init__(self, parent)
layout = QVBoxLayout(self)
QMainWindow.__init__(self, parent)
self.fragment_view = DAGActiveView(self)
layout.setContentsMargins(0,0,0,0)
layout.addWidget(self.fragment_view)
self.fragment_view.nodes_clicked.connect(self.highlight_node_path)
self.present_graph: Dict[str, StoryMap] = {}
self.setCentralWidget(self.fragment_view)
self.fragment_view.nodes_clicked.connect(self.print_node_list)
self.present_graph: Dict[str, StoryMap] = None
pass
pass
@ -61,7 +56,7 @@ class StorylinesView(QWidget):
self.fragment_view.update_with_edges(arrows)
pass
def highlight_node_path(self, xpos, ypos, list):
def print_node_list(self, xpos, ypos, list):
if len(list) == 0:
return
@ -95,12 +90,12 @@ class StorylinesView(QWidget):
pass
if len(story_list) == 1:
self.highlight_node_path(xpos, ypos, [("node", story_list[0])])
self.print_node_list(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.highlight_node_path(xpos, ypos, [("node", story_name)])
return lambda : self.print_node_list(xpos, ypos, [("node", story_name)])
menu.addAction(f"story/{story}", trigger(story))
pass
@ -116,73 +111,13 @@ class StorylinesView(QWidget):
pass
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 = ArticleRefsView(None)
view = ContentWindow(None)
view.show()
tool = XAST_ParseTool(global_ast_path)
# view.present_stories_graph(tool.get_story_graph())
view.present_volumes_graph(tool.get_article_nodes())
view.present_stories_graph(tool.get_story_graph())
# view.fragment_view.highlight_graph_link(["血脉的源头", "血脉的源头&待续"])

View File

@ -1,4 +1,3 @@
import math
from typing import List
@ -8,44 +7,6 @@ class Pos:
self.y_pos = y
pass
def __add__(self, other: 'Pos') -> 'Pos':
return Pos(self.x_pos + other.x_pos, self.y_pos + other.y_pos)
def __sub__(self, other: 'Pos') -> 'Pos':
return Pos(self.x_pos - other.x_pos, self.y_pos - other.y_pos)
def __iadd__(self, other: 'Pos') -> 'Pos':
self.x_pos += other.x_pos
self.y_pos += other.y_pos
return self
def __isub__(self, other: 'Pos') -> 'Pos':
self.x_pos -= other.x_pos
self.y_pos -= other.y_pos
return self
def __mul__(self, t: float) -> 'Pos':
return Pos(self.x_pos * t, self.y_pos * t)
def __imul__(self, t: float) -> 'Pos':
self.x_pos *= t
self.y_pos *= t
return self
def vec_length(self) -> float:
return math.sqrt(self.x_pos**2 + self.y_pos**2)
def normalized(self) -> 'Pos':
dist = self.vec_length()
if dist == 0:
return Pos(0, 0)
return self * (1/dist)
def to_text(self) -> str:
return f"pos<{self.__hash__()}>{{{self.x_pos}, {self.y_pos}}}"
def make_copy(self) -> 'Pos':
return Pos(self.x_pos, self.y_pos)
@ -62,16 +23,6 @@ class Point:
return Point(self.point_name)
class PositionalPoint(Point, Pos):
def __init__(self, name: str, x: float, y: float):
Point.__init__(self, name)
Pos.__init__(self, x, y)
pass
def make_copy(self) -> 'PositionalPoint':
return PositionalPoint(self.name(), self.x_pos, self.y_pos)
class Line:
def __init__(self, p0: Point, p1: Point):
self.point_set = [p0, p1]
@ -95,3 +46,5 @@ class Arrow(Line):
def end_point(self):
return self.point_set[1]
pass

View File

@ -422,19 +422,18 @@ class DAGActiveView(QGraphicsView):
def mousePressEvent(self, event: QMouseEvent):
QGraphicsView.mousePressEvent(self, event)
if event.button() == Qt.MouseButton.LeftButton:
gitems = self.items(event.pos())
noderef_names = []
for gnode in gitems:
if gnode.node_key_bind[0].startswith("node"):
noderef_names.append(gnode.node_key_bind)
pass
gitems = self.items(event.pos())
noderef_names = []
for gnode in gitems:
if gnode.node_key_bind[0].startswith("node"):
noderef_names.append(gnode.node_key_bind)
pass
pass
self.nodes_clicked.emit(event.pos().x(), event.pos().y(), noderef_names[0:1])
self.nodes_clicked.emit(event.pos().x(), event.pos().y(), noderef_names[0:1])
pass
if __name__ == "__main__":
app = QApplication(sys.argv)
view = DAGActiveView(None)

View File

@ -1,167 +0,0 @@
import sys, os
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
from graph.DataType import PositionalPoint, Line, Pos, Point
from typing import List, Dict
import random as rd
class ForceCalcHelper:
"""
力导向计算辅助节点
"""
def __init__(self, init_v: PositionalPoint):
self.bind_point = init_v
self.sibling_nodes: Dict[str, 'ForceCalcHelper'] = {}
pass
def bind_point_name(self) -> str:
return self.bind_point.point_name
def get_sibling_nodes(self) -> Dict[str, 'ForceCalcHelper']:
return self.sibling_nodes
pass
def sibling_append(self, node: 'ForceCalcHelper'):
self.sibling_nodes[node.bind_point_name()] = node
pass
class UDGGraph:
force2accx_rates = 1
def __init__(self):
self.random_gen = rd.Random()
self.node_set: Dict[str, ForceCalcHelper] = {}
pass
def rebuild_from_edges(self, line_set: List[Line]):
self.node_set.clear()
for line in line_set:
start_node = line.points()[0]
if start_node.point_name not in self.node_set:
pos_node = PositionalPoint(start_node.point_name, 0, 0)
self.node_set[start_node.point_name] = ForceCalcHelper(pos_node)
pass
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:
pos_node = PositionalPoint(end_node.point_name, 0, 0)
self.node_set[end_node.point_name] = ForceCalcHelper(pos_node)
pass
start_force_point = self.node_set[start_node.point_name]
other_force_point = self.node_set[end_node.point_name]
start_force_point.sibling_append(other_force_point)
other_force_point.sibling_append(start_force_point)
pass
pass
def __eject_with_item(self, curr: ForceCalcHelper, node_set:Dict[str, ForceCalcHelper]) -> Pos:
init_value = Pos(0, 0)
for node in node_set.values():
if curr.bind_point_name() == node.bind_point_name():
continue
coord_span = curr.current_pos() - node.current_pos()
distance = coord_span.vec_length()
force_scalar = ForceCalcHelper.eject_k / (distance**2)
force_vec = coord_span.normalized() * force_scalar
init_value += force_vec
pass
return init_value
def __attract_with_item_sibs(self, curr: ForceCalcHelper) -> Pos:
init_value = Pos(0, 0)
for node in curr.get_sibling_nodes().values():
coord_span = curr.current_pos() - node.current_pos()
distance = coord_span.vec_length()
force_scalar = distance * ForceCalcHelper.attract_k
force_vec = coord_span.normalized() * force_scalar
init_value -= force_vec
pass
return init_value
def __calculate_item_force(self, curr: ForceCalcHelper, node_set:Dict[str, ForceCalcHelper]) -> float:
"""
计算指定节点对整个数据图节点的合力
:param times: 次数初始迭代有特殊处理
:param curr: 当前节点
:param node_set: 所有节点
:return: 合力标量
"""
eject_vec2 = self.__eject_with_item(curr, node_set)
attract_vec2 = self.__attract_with_item_sibs(curr)
curr.force_with_direction = eject_vec2 + attract_vec2
# 阻尼计算 f=fxG(fxg=1,m=1)
fr = ForceCalcHelper.damping_k * 10
if curr.force_with_direction.vec_length() > fr:
curr.force_with_direction -= curr.force_with_direction.normalized() * fr
pass
else:
curr.force_with_direction = Pos()
pass
return curr.force_with_direction.vec_length()
def __item_position_adjust(self, curr: ForceCalcHelper) -> None:
if curr.force_with_direction.vec_length() == 0:
return
vec_speed = curr.force_with_direction.normalized()
curr.move_by(vec_speed)
pass
def graph_layout(self):
for curr in self.node_set.values():
random_pos = Pos(self.random_gen.random() * 100, self.random_gen.random() * 100)
curr.move_by(random_pos)
pass
for idx in range(0, 10):
for curr in self.node_set.values():
self.__calculate_item_force(curr, self.node_set)
pass
for curr in self.node_set.values():
self.__item_position_adjust(curr)
pass
pass
pass
def visible_positon_set(self) -> List[PositionalPoint]:
retvs = []
for node in self.node_set.values():
retvs.append(node.bind_point)
pass
return retvs
if __name__ == "__main__":
list_in = [
Line(Point("a"), Point("b")),
Line(Point("a"), Point("c")),
Line(Point("a"), Point("d")),
Line(Point("a"), Point("e")),
Line(Point("d"), Point("e")),
Line(Point("f"), Point("c")),
]
graph = UDGGraph()
graph.rebuild_from_edges(list_in)
graph.graph_layout()
for p in graph.visible_positon_set():
print(f"node:{p.name()}<{p.x_pos},{p.y_pos}>")
pass
pass

View File

@ -1,339 +0,0 @@
import sys, os
from abc import abstractmethod
from enum import Enum
from typing import List, Dict, Tuple
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
class PresentNodeType(Enum):
PresentNode = 0,
ConnectionNode = 1,
class GraphNode:
@abstractmethod
def node_type(self) -> PresentNodeType:
raise NotImplementedError("node_type")
@abstractmethod
def highlight(self, flag: bool):
raise NotImplementedError("highlight")
@abstractmethod
def is_highlighted(self) -> bool:
raise NotImplementedError("is_highlighted")
@abstractmethod
def boundingRect(self) -> QRectF:
raise NotImplementedError("boundingRect")
@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, 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
def sibling_append(self, node: 'PresentNode'):
if node not in self.__sibling_list:
self.__sibling_list.append(node)
pass
pass
def sibling_nodes(self) -> List['PresentNode']:
return self.__sibling_list
def node_type(self) -> PresentNodeType:
return PresentNodeType.PresentNode
def highlight(self, flag: bool):
self.__is_highlight_mark = flag
pass
def is_highlighted(self) -> bool:
return self.__is_highlight_mark
pass
def boundingRect(self) -> QRectF:
width_x = self.__font_bind.pixelSize() * (len(self.node_name)+1)
height_x = self.__font_bind.pixelSize()
return QRectF(0, 0, width_x + 10, height_x + 10)
pass
def paint(self, painter, option, widget = ...):
outline = self.boundingRect()
path_icon = QPainterPath()
path_icon.lineTo(outline.height()/2 - 5, outline.height() -5)
path_icon.lineTo(outline.height()/2, outline.height()/2)
path_icon.lineTo(outline.height() - 5, outline.height()/2 - 5)
path_icon.lineTo(0, 0)
painter.save()
painter.setRenderHint(QPainter.Antialiasing)
#painter.drawRect(outline)
if self.__is_highlight_mark:
brush = Qt.red
painter.setPen(Qt.red)
else:
brush = Qt.black
painter.setPen(Qt.black)
pass
painter.fillPath(path_icon, brush)
painter.translate(outline.height(), 5)
painter.drawText(outline, self.node_name)
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):
QGraphicsItem.__init__(self, parent)
self.__highlight_mark = False
self.__point0 = p0
self.__point1 = p1
self.__outline = QRectF()
self.setZValue(1)
pass
def node_type(self) -> PresentNodeType:
return PresentNodeType.ConnectionNode
def highlight(self, flag: bool):
self.__highlight_mark = flag
pass
def is_highlighted(self) -> bool:
return self.__highlight_mark
pass
def relayout_exec(self, ppi: float = 1):
start_pos = self.__point0.pos()
end_pos = self.__point1.pos()
start_x = min(start_pos.x(), end_pos.x())
start_y = min(start_pos.y(), end_pos.y())
end_x = max(start_pos.x(), end_pos.x())
end_y = max(start_pos.y(), end_pos.y())
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):
return self.__outline
pass
def paint(self, painter, option, widget = ...):
start_pos = self.__point0.pos()
end_pos = self.__point1.pos()
outline = self.boundingRect()
painter.save()
painter.setRenderHint(QPainter.Antialiasing)
if self.__highlight_mark:
pen = QPen(Qt.red)
else:
pen = QPen(Qt.lightGray)
pen.setWidthF(3)
painter.setPen(pen)
if start_pos.y() < end_pos.y():
if start_pos.x() < end_pos.x():
painter.drawLine(outline.topLeft(), outline.bottomRight())
else:
painter.drawLine(outline.topRight(), outline.bottomLeft())
else:
if start_pos.x() < end_pos.x():
painter.drawLine(outline.bottomLeft(), outline.topRight())
else:
painter.drawLine(outline.topLeft(), outline.bottomRight())
painter.restore()
pass
class UDGPresent(QGraphicsView):
node_clicked = pyqtSignal(str)
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()
self.__scene_bind = QGraphicsScene(self)
self.setScene(self.__scene_bind)
font = QFont()
font.setPixelSize(20)
self.setFont(font)
pass
def rebuild_from_edges(self, line_set: List[Line]):
self.node_set.clear()
for line in line_set:
start_node = line.points()[0]
self.__layout_graph.add_node(start_node.point_name)
end_node = line.points()[1]
self.__layout_graph.add_node(end_node.point_name)
self.__layout_graph.add_edge(start_node.point_name, end_node.point_name)
pass
pos_map = nx.spring_layout(self.__layout_graph)
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
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: 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(self.pixel_per_unit)
self.node_set[f"conn::{edge_start}&{edge_end}"] = connection
pass
pass
def mousePressEvent(self, event: QMouseEvent):
QGraphicsView.mousePressEvent(self, event)
if event.button() == Qt.MouseButton.LeftButton:
item: GraphNode = self.itemAt(event.pos())
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)
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__":
app = QApplication(sys.argv)
view = UDGPresent(None)
view.show()
list_in = [
Line(Point('a中古'), Point('b')),
Line(Point('a中古'), Point('c')),
Line(Point('a中古'), Point('d')),
]
view.rebuild_from_edges(list_in)
app.exec()

View File

@ -1,21 +0,0 @@
import sys, os
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
import networkx as nx
import matplotlib.pyplot as plt
g = nx.Graph()
g.add_node('a')
g.add_node('b')
g.add_node('c')
g.add_node('d')
g.add_edge('a', 'b')
g.add_edge('b','c')
g.add_edge('b','d')
posx = nx.spring_layout(g)
print(posx)
for n in posx:
print(n)
nx.draw(g, posx)
plt.show()

View File

@ -27,24 +27,6 @@ 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
@ -62,7 +44,7 @@ class StoryMap:
self.slice_list.append(node)
pass
def get_fragment_defined(self, name: str) -> FragmentSlice|None:
def get_fragment_defined(self, name: str) -> FragmentSlice:
for fit in self.slice_list:
if fit.is_define_node[0]:
if fit.is_define_node[1] == name:
@ -158,58 +140,6 @@ 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