基础的无向图呈现
This commit is contained in:
parent
b7f71fb29f
commit
d4bf6b186c
|
@ -5,8 +5,14 @@
|
||||||
</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$/graph/directed_acyclic_graph/DAGLayout.py" beforeDir="false" afterPath="$PROJECT_DIR$/graph/directed_acyclic_graph/DAGLayout.py" 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$/graph/DataType.py" beforeDir="false" afterPath="$PROJECT_DIR$/graph/DataType.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" />
|
||||||
</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" />
|
||||||
|
@ -34,53 +40,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">{
|
<component name="PropertiesComponent"><![CDATA[{
|
||||||
"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": "Run",
|
||||||
"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.ast_load.executor": "Debug",
|
"Python.UDGLayout.executor": "Run",
|
||||||
"Python.entry.executor": "Run",
|
"Python.UDGPresent.executor": "Run",
|
||||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
"Python.ast_load.executor": "Debug",
|
||||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
"Python.entry.executor": "Run",
|
||||||
"git-widget-placeholder": "master",
|
"Python.test.executor": "Run",
|
||||||
"last_opened_file_path": "D:/Projects/Python/StoryCheckTools",
|
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||||
"settings.editor.selected.configurable": "reference.settings.ide.settings.new.ui"
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
|
"git-widget-placeholder": "master",
|
||||||
|
"last_opened_file_path": "D:/Projects/Python/StoryTools",
|
||||||
|
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable"
|
||||||
}
|
}
|
||||||
}</component>
|
}]]></component>
|
||||||
<component name="RunManager" selected="Python.ContentView">
|
<component name="PyDebuggerOptionsProvider">
|
||||||
<configuration name="CompareViews" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
<option name="mySaveCallSignatures" value="true" />
|
||||||
<module name="StoryTools" />
|
<option name="mySupportGeventDebugging" value="true" />
|
||||||
<option name="ENV_FILES" value="" />
|
<option name="myDropIntoDebuggerOnFailedTests" value="true" />
|
||||||
<option name="INTERPRETER_OPTIONS" value="" />
|
<option name="myPyQtBackend" value="pyqt5" />
|
||||||
<option name="PARENT_ENVS" value="true" />
|
</component>
|
||||||
<envs>
|
<component name="RunManager" selected="Python.UDGPresent">
|
||||||
<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">
|
<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="" />
|
||||||
|
@ -103,28 +96,6 @@
|
||||||
<option name="INPUT_FILE" value="" />
|
<option name="INPUT_FILE" value="" />
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</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">
|
<configuration name="DAGPresent" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||||
<module name="StoryTools" />
|
<module name="StoryTools" />
|
||||||
<option name="ENV_FILES" value="" />
|
<option name="ENV_FILES" value="" />
|
||||||
|
@ -147,7 +118,7 @@
|
||||||
<option name="INPUT_FILE" value="" />
|
<option name="INPUT_FILE" value="" />
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
<configuration name="NovelManage" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
<configuration name="UDGLayout" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||||
<module name="StoryTools" />
|
<module name="StoryTools" />
|
||||||
<option name="ENV_FILES" value="" />
|
<option name="ENV_FILES" value="" />
|
||||||
<option name="INTERPRETER_OPTIONS" value="" />
|
<option name="INTERPRETER_OPTIONS" value="" />
|
||||||
|
@ -156,12 +127,56 @@
|
||||||
<env name="PYTHONUNBUFFERED" value="1" />
|
<env name="PYTHONUNBUFFERED" value="1" />
|
||||||
</envs>
|
</envs>
|
||||||
<option name="SDK_HOME" value="" />
|
<option name="SDK_HOME" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/manage" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/graph/undirected_graph" />
|
||||||
<option name="IS_MODULE_SDK" value="true" />
|
<option name="IS_MODULE_SDK" value="true" />
|
||||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/manage/NovelManage.py" />
|
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/graph/undirected_graph/UDGLayout.py" />
|
||||||
<option name="PARAMETERS" value="wnss -cmp" />
|
<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="SHOW_COMMAND_LINE" value="false" />
|
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||||
<option name="EMULATE_TERMINAL" value="false" />
|
<option name="EMULATE_TERMINAL" value="false" />
|
||||||
<option name="MODULE_MODE" value="false" />
|
<option name="MODULE_MODE" value="false" />
|
||||||
|
@ -170,26 +185,26 @@
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
<list>
|
<list>
|
||||||
|
<item itemvalue="Python.UDGPresent" />
|
||||||
|
<item itemvalue="Python.test" />
|
||||||
|
<item itemvalue="Python.UDGLayout" />
|
||||||
<item itemvalue="Python.ContentView" />
|
<item itemvalue="Python.ContentView" />
|
||||||
<item itemvalue="Python.DAGLayout" />
|
|
||||||
<item itemvalue="Python.DAGPresent" />
|
<item itemvalue="Python.DAGPresent" />
|
||||||
<item itemvalue="Python.CompareViews" />
|
|
||||||
<item itemvalue="Python.NovelManage" />
|
|
||||||
</list>
|
</list>
|
||||||
<recent_temporary>
|
<recent_temporary>
|
||||||
<list>
|
<list>
|
||||||
|
<item itemvalue="Python.UDGPresent" />
|
||||||
<item itemvalue="Python.ContentView" />
|
<item itemvalue="Python.ContentView" />
|
||||||
|
<item itemvalue="Python.test" />
|
||||||
|
<item itemvalue="Python.UDGLayout" />
|
||||||
<item itemvalue="Python.DAGPresent" />
|
<item itemvalue="Python.DAGPresent" />
|
||||||
<item itemvalue="Python.DAGLayout" />
|
|
||||||
<item itemvalue="Python.CompareViews" />
|
|
||||||
<item itemvalue="Python.NovelManage" />
|
|
||||||
</list>
|
</list>
|
||||||
</recent_temporary>
|
</recent_temporary>
|
||||||
</component>
|
</component>
|
||||||
<component name="SharedIndexes">
|
<component name="SharedIndexes">
|
||||||
<attachedChunks>
|
<attachedChunks>
|
||||||
<set>
|
<set>
|
||||||
<option value="bundled-python-sdk-5b207ade9991-746f403e7f0c-com.jetbrains.pycharm.community.sharedIndexes.bundled-PC-241.17890.14" />
|
<option value="bundled-python-sdk-975db3bf15a3-31b6be0877a2-com.jetbrains.pycharm.community.sharedIndexes.bundled-PC-241.18034.82" />
|
||||||
</set>
|
</set>
|
||||||
</attachedChunks>
|
</attachedChunks>
|
||||||
</component>
|
</component>
|
||||||
|
|
10
entry.py
10
entry.py
|
@ -1,9 +1 @@
|
||||||
from parse.StoryMap import XAST_ParseTool, storyline_list2map
|
print(__file__)
|
||||||
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)
|
|
|
@ -2,7 +2,7 @@ import sys
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
from PyQt5.QtCore import QPoint
|
from PyQt5.QtCore import QPoint
|
||||||
from PyQt5.QtWidgets import QApplication, QMainWindow
|
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout
|
||||||
from PyQt5.QtWidgets import QMenu
|
from PyQt5.QtWidgets import QMenu
|
||||||
|
|
||||||
from graph.DataType import Arrow, Point
|
from graph.DataType import Arrow, Point
|
||||||
|
@ -17,13 +17,15 @@ class FragmentPoint(Point):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ContentWindow(QMainWindow):
|
class StorylinesView(QWidget):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QMainWindow.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
self.fragment_view = DAGActiveView(self)
|
self.fragment_view = DAGActiveView(self)
|
||||||
self.setCentralWidget(self.fragment_view)
|
layout.setContentsMargins(0,0,0,0)
|
||||||
self.fragment_view.nodes_clicked.connect(self.print_node_list)
|
layout.addWidget(self.fragment_view)
|
||||||
self.present_graph: Dict[str, StoryMap] = None
|
self.fragment_view.nodes_clicked.connect(self.highlisth_node_path)
|
||||||
|
self.present_graph: Dict[str, StoryMap] = {}
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -56,7 +58,7 @@ class ContentWindow(QMainWindow):
|
||||||
self.fragment_view.update_with_edges(arrows)
|
self.fragment_view.update_with_edges(arrows)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def print_node_list(self, xpos, ypos, list):
|
def highlisth_node_path(self, xpos, ypos, list):
|
||||||
if len(list) == 0:
|
if len(list) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -90,12 +92,12 @@ class ContentWindow(QMainWindow):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if len(story_list) == 1:
|
if len(story_list) == 1:
|
||||||
self.print_node_list(xpos, ypos, [("node", story_list[0])])
|
self.highlisth_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.print_node_list(xpos, ypos, [("node", story_name)])
|
return lambda : self.highlisth_node_path(xpos, ypos, [("node", story_name)])
|
||||||
|
|
||||||
menu.addAction(f"story/{story}", trigger(story))
|
menu.addAction(f"story/{story}", trigger(story))
|
||||||
pass
|
pass
|
||||||
|
@ -111,9 +113,15 @@ class ContentWindow(QMainWindow):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleRefsView(QWidget):
|
||||||
|
def __init__(self, parent):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
view = ContentWindow(None)
|
view = StorylinesView(None)
|
||||||
view.show()
|
view.show()
|
||||||
|
|
||||||
tool = XAST_ParseTool(global_ast_path)
|
tool = XAST_ParseTool(global_ast_path)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import math
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,6 +8,44 @@ class Pos:
|
||||||
self.y_pos = y
|
self.y_pos = y
|
||||||
pass
|
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':
|
def make_copy(self) -> 'Pos':
|
||||||
return Pos(self.x_pos, self.y_pos)
|
return Pos(self.x_pos, self.y_pos)
|
||||||
|
|
||||||
|
@ -23,6 +62,16 @@ class Point:
|
||||||
return Point(self.point_name)
|
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:
|
class Line:
|
||||||
def __init__(self, p0: Point, p1: Point):
|
def __init__(self, p0: Point, p1: Point):
|
||||||
self.point_set = [p0, p1]
|
self.point_set = [p0, p1]
|
||||||
|
@ -46,5 +95,3 @@ class Arrow(Line):
|
||||||
def end_point(self):
|
def end_point(self):
|
||||||
return self.point_set[1]
|
return self.point_set[1]
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -422,18 +422,19 @@ class DAGActiveView(QGraphicsView):
|
||||||
def mousePressEvent(self, event: QMouseEvent):
|
def mousePressEvent(self, event: QMouseEvent):
|
||||||
QGraphicsView.mousePressEvent(self, event)
|
QGraphicsView.mousePressEvent(self, event)
|
||||||
|
|
||||||
gitems = self.items(event.pos())
|
if event.button() == Qt.MouseButton.LeftButton:
|
||||||
noderef_names = []
|
gitems = self.items(event.pos())
|
||||||
for gnode in gitems:
|
noderef_names = []
|
||||||
if gnode.node_key_bind[0].startswith("node"):
|
for gnode in gitems:
|
||||||
noderef_names.append(gnode.node_key_bind)
|
if gnode.node_key_bind[0].startswith("node"):
|
||||||
|
noderef_names.append(gnode.node_key_bind)
|
||||||
|
pass
|
||||||
pass
|
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
|
pass
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
view = DAGActiveView(None)
|
view = DAGActiveView(None)
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,167 @@
|
||||||
|
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
|
|
@ -0,0 +1,285 @@
|
||||||
|
import sys
|
||||||
|
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 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")
|
||||||
|
|
||||||
|
|
||||||
|
class PresentNode(QGraphicsItem, GraphNode):
|
||||||
|
def __init__(self, name: str, font: QFont, parent):
|
||||||
|
QGraphicsItem.__init__(self, parent)
|
||||||
|
self.node_name = name
|
||||||
|
self.__is_highlight_mark = False
|
||||||
|
self.__font_bind = font
|
||||||
|
self.__sibling_list = []
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
||||||
|
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)
|
||||||
|
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.__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()
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
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)
|
||||||
|
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]
|
||||||
|
connection = ConnectionNode(node_one, node_two, None)
|
||||||
|
self.scene().addItem(connection)
|
||||||
|
connection.relayout_exec()
|
||||||
|
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.node_type() == PresentNodeType.PresentNode:
|
||||||
|
vnode: PresentNode = item
|
||||||
|
self.node_clicked.emit(vnode.node_name)
|
||||||
|
print(vnode.node_name)
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
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()
|
|
@ -0,0 +1,21 @@
|
||||||
|
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()
|
Loading…
Reference in New Issue