diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 6094081..b7fe87f 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,7 +5,12 @@ + + + + + - + + + + + + + - diff --git a/entry.py b/entry.py index 2b0f420..e6055c3 100644 --- a/entry.py +++ b/entry.py @@ -1,17 +1,9 @@ -from PyQt5.QtWidgets import QWidget, QApplication -from frame.CompareWindow import CompareWin -from sys import argv -from parse.ast_load import global_ast_path, AstParse +from parse.StoryMap import XAST_ParseTool, storyline_list2map +from parse.ast_load import global_ast_path -tool = AstParse(global_ast_path) -print(tool.generate_time()) -storys = tool.peak_storylines() -storys_2 = tool.peak_storylines() -defferent_list = tool.storylines_compare(storys, storys_2) - -app = QApplication(argv) -win = CompareWin() -win.load_differents(defferent_list, storys, storys_2) -win.show() -app.exec() \ No newline at end of file +astx = XAST_ParseTool(global_ast_path) +storys = astx.story_list +storys_map = storyline_list2map(storys) +astx.storylines_plait(storys_map) +print(storys_map) \ No newline at end of file diff --git a/frame/MergeView.py b/frame/MergeView.py new file mode 100644 index 0000000..bd2f648 --- /dev/null +++ b/frame/MergeView.py @@ -0,0 +1,71 @@ +from PyQt5.QtWidgets import QWidget, QTextEdit, QTabWidget, QSplitter, QApplication, QVBoxLayout +import sys +from frame.ReferView import MemoryNode, RelateReferView, EmptyNode, MemorySkin +from typing import List +from PyQt5.Qt import Qt + + +class MergeDestination: + def __init__(self, story_name:str, prev: MemoryNode, curr: MemoryNode): + self.prev_node = prev + self.curr_node = curr + self.storyline_name = story_name + pass + + +class LinesMergeView(QWidget, MemorySkin): + def __init__(self, fdefs: MergeDestination, refdefs: List[MergeDestination], parent): + QWidget.__init__(self, parent) + + self.define_line = fdefs + self.refers_line = refdefs + + layout = QVBoxLayout(self) + splitter = QSplitter(Qt.Orientation.Vertical, self) + layout.addWidget(splitter) + + self.tabs_con: QTabWidget = QTabWidget(self) + splitter.addWidget(self.tabs_con) + + self.define_prev: QTextEdit = QTextEdit(self) + self.define_prev.setReadOnly(True) + self.tabs_con.addTab(self.define_prev, fdefs.storyline_name) + self.refers_views: List[RelateReferView] = [] + for node in refdefs: + refer_view = RelateReferView(node.prev_node, node.curr_node, self) + self.tabs_con.addTab(refer_view, node.storyline_name) + self.refers_views.append(refer_view) + pass + + self.mut_enter = QTextEdit(self) + splitter.addWidget(self.mut_enter) + + pass + + def load_from_mem(self) -> None: + self.define_prev.setText(self.define_line.prev_node.get_from_memory()) + self.mut_enter.setText(self.define_line.curr_node.get_from_memory()) + + for v in self.refers_views: + v.load_from_mem() + pass + + pass + + def save_to_mem(self): + self.define_line.curr_node.set_to_memory(self.mut_enter.toPlainText()) + + for v in self.refers_views: + v.save_to_mem() + pass + + pass + + +if __name__ == "__main__": + app = QApplication(sys.argv) + defx = MergeDestination("page0", EmptyNode(), EmptyNode()) + refx0 = MergeDestination("pagex1", EmptyNode(), EmptyNode()) + view = LinesMergeView(defx, [refx0], None) + view.show() + app.exec() \ No newline at end of file diff --git a/frame/ReferView.py b/frame/ReferView.py new file mode 100644 index 0000000..0262cd7 --- /dev/null +++ b/frame/ReferView.py @@ -0,0 +1,82 @@ +from PyQt5.QtWidgets import QTextEdit, QWidget, QSplitter, QVBoxLayout, QApplication +from PyQt5.Qt import Qt +from abc import abstractmethod +from typing import List +import sys + + +class MemoryNode: + @abstractmethod + def get_from_memory(self) -> str: + raise NotImplementedError("get_from_memory") + + @abstractmethod + def set_to_memory(self, content: str): + raise NotImplementedError("set_to_memmory") + + +class MemorySkin: + @abstractmethod + def load_from_mem(self) -> None: + raise NotImplementedError("load_from_mem") + + @abstractmethod + def save_to_mem(self): + raise NotImplementedError("save_to_mem") + + +class RelateReferView(QWidget, MemorySkin): + def __init__(self, prev_frefs: MemoryNode, curr_frefs: MemoryNode, parent): + """ + 构建引用关系视图 + + :param prev_frefs: 上游节点 + :param curr_frefs: 当前引用节点 + """ + QWidget.__init__(self, parent) + self.prev_fragment = prev_frefs + self.curr_fragment = curr_frefs + layout = QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + + splitter_container = QSplitter(Qt.Orientation.Vertical, self) + layout.addWidget(splitter_container) + + self.immut_view: QTextEdit = QTextEdit(self) + self.immut_view.setReadOnly(True) + self.mut_refers: QTextEdit = QTextEdit(self) + splitter_container.addWidget(self.immut_view) + splitter_container.addWidget(self.mut_refers) + + pass + + def load_from_mem(self): + """ + 载入内存内容到视图 + """ + self.immut_view.setText(self.prev_fragment.get_from_memory()) + self.mut_refers.setText(self.curr_fragment.get_from_memory()) + pass + + def save_to_mem(self): + """ + 保存视图内容到内存 + """ + self.prev_fragment.set_to_memory(self.immut_view.toPlainText()) + self.curr_fragment.set_to_memory(self.mut_refers.toPlainText()) + pass + + +class EmptyNode(MemoryNode): + def get_from_memory(self) -> str: + return "站位视图内容" + + def set_to_memory(self, content: str): + pass + + +if __name__ == "__main__": + app = QApplication(sys.argv) + view = RelateReferView(EmptyNode(), EmptyNode(), None) + view.show() + app.exec() diff --git a/frame/__pycache__/CompareWindow.cpython-312.pyc b/frame/__pycache__/CompareWindow.cpython-312.pyc index 9f93294..8d10171 100644 Binary files a/frame/__pycache__/CompareWindow.cpython-312.pyc and b/frame/__pycache__/CompareWindow.cpython-312.pyc differ diff --git a/frame/__pycache__/ReferView.cpython-312.pyc b/frame/__pycache__/ReferView.cpython-312.pyc new file mode 100644 index 0000000..48187f7 Binary files /dev/null and b/frame/__pycache__/ReferView.cpython-312.pyc differ diff --git a/frame/__pycache__/__init__.cpython-312.pyc b/frame/__pycache__/__init__.cpython-312.pyc index 9cabe46..7a98443 100644 Binary files a/frame/__pycache__/__init__.cpython-312.pyc and b/frame/__pycache__/__init__.cpython-312.pyc differ diff --git a/parse/StoryMap.py b/parse/StoryMap.py new file mode 100644 index 0000000..7441793 --- /dev/null +++ b/parse/StoryMap.py @@ -0,0 +1,149 @@ +import xml.dom.minidom as mdom +from typing import List, Tuple, Dict +from enum import Enum +from frame.ReferView import EmptyNode, MemoryNode + + +class ModifyReason(Enum): + Removed = 0, + Append = 1, + Changed = 2, + Nothing = 4, + + +class FragmentSlice(EmptyNode): + def __init__(self, def_mark: bool = False, name: str= ""): + self.is_define_node = (def_mark, name) + self.story_refer = "" + self.fragm_refer = "" + + self.prev_node: 'FragmentSlice' = None # 上游节点 + self.next_node: 'FragmentSlice' = None # 下游节点 + self.refers_slice: Dict[str, 'FragmentSlice'] = {} # 引用切面 + + self.changed_prev_nodes: List[Tuple[ModifyReason, 'FragmentSlice']] = [] # 变更的上游节点 + self.text_sections: List[str] = [] # 文本段落 + pass + + def get_from_memory(self) -> str: + return r"\n".join(self.text_sections) + pass + + def set_to_memory(self, content: str): + self.text_sections = content.split(r"\n") + pass + + +class StoryMap: + def __init__(self, name: str): + self.story_name = name + self.sort_index = 0 + self.empty_head = FragmentSlice() + self.slice_list: List[FragmentSlice] = [self.empty_head] + pass + + def append_fragment_slice(self, node: FragmentSlice): + self.slice_list[-1].next_node = node + node.prev_node = self.slice_list[-1] + self.slice_list.append(node) + pass + + 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: + return fit + pass + pass + return None + + +class XAST_ParseTool: + def __init__(self, ast_path: str): + self.dom_tree = mdom.parse(ast_path) + self.story_list: List[StoryMap] = [] + names = self.story_names() + for nm in names: + vinst = self.__extract_empty_storymap(nm) + self.__storynode_initfrags(vinst[0], vinst[1]) + self.story_list.append(vinst[0]) + pass + + def story_names(self) -> List[str]: + """ + 获取所有故事名称 + :return: List[故事名] + """ + storys = self.dom_tree.getElementsByTagName("story") + values = [] + for n in storys: + values.append(n.getAttribute("name")) + pass + return values + + def __extract_empty_storymap(self, name: str) -> Tuple[StoryMap, mdom.Element]: + storys = self.dom_tree.getElementsByTagName("story") + for node in storys: + if node.getAttribute("name") == name: + mem_node = StoryMap(name) + mem_node.sort_index = int(node.getAttribute("sort")) + return (mem_node, node) + pass + return None + + def __storynode_initfrags(self, node: StoryMap, e: mdom.Element): + first_elem: mdom.Element = e.firstChild + while first_elem != None: + if hasattr(first_elem, "tagName"): + dom_tag = first_elem.tagName + if first_elem.tagName == "fragment": + fnode = FragmentSlice(True, first_elem.getAttribute("name")) + self.__fragmnode_init_desc(fnode, first_elem) + node.append_fragment_slice(fnode) + elif first_elem.tagName == "refer": + fnode = FragmentSlice() + fnode.story_refer = first_elem.getAttribute("story") + fnode.fragm_refer = first_elem.getAttribute("fragment") + self.__fragmnode_init_desc(fnode, first_elem) + node.append_fragment_slice(fnode) + pass + pass + first_elem = first_elem.nextSibling + pass + + def __fragmnode_init_desc(self, node: FragmentSlice, e: mdom.Element): + node_elem: mdom.Element = e.firstChild + while node_elem != None: + if hasattr(node_elem, "tagName") and node_elem.tagName == "text-section": + line = node_elem.getAttribute("text") + node.text_sections.append(line) + pass + node_elem = node_elem.nextSibling + pass + pass + + def storylines_plait(self, storys: Dict[str, StoryMap]): + for snode in storys.values(): + self.__storyline_plait_within(snode, storys) + pass + + def __storyline_plait_within(self, story: StoryMap, storys_map: Dict[str, StoryMap]): + for fnode in story.slice_list[1:]: + if not fnode.is_define_node[0]: + sname = fnode.story_refer + fname = fnode.fragm_refer + target_story = storys_map[sname] + target_fragment = target_story.get_fragment_defined(fname) + target_fragment.refers_slice[story.story_name] = fnode + pass + pass + pass + pass + + +def storyline_list2map(storylines: List[StoryMap]) -> Dict[str, StoryMap]: + retv = {} + for linst in storylines: + retv[linst.story_name] = linst + pass + return retv diff --git a/parse/StorylineCmp.py b/parse/StorylineCmp.py new file mode 100644 index 0000000..5c6e2ab --- /dev/null +++ b/parse/StorylineCmp.py @@ -0,0 +1,53 @@ +from parse.StoryMap import StoryMap, ModifyReason, FragmentSlice +from typing import Dict, List + + +class CmpTool: + def __init__(self): + pass + + def graph_compare(self, graph_new: Dict[str, StoryMap], graph_old: Dict[str, StoryMap]): + fragments_has_removed = [] + for story in graph_old.values(): + fragments_has_removed = fragments_has_removed + self.__event_remove_check(graph_new, story) + pass + + fragments_has_appended = [] + for story in graph_new.values(): + fragments_has_appended = fragments_has_appended + self.__event_append_check(graph_old, story) + pass + + + + + pass + + def __event_remove_check(self, graph_new: Dict[str, StoryMap], story_old: StoryMap) -> List[FragmentSlice]: + list_retv = [] + for slice in story_old.slice_list[1:]: + if slice.is_define_node[0]: + s_name = story_old.story_name + f_name = slice.is_define_node[1] + story_findout = graph_new[s_name] + if story_findout is None or story_findout.get_fragment_defined(f_name) is None: + list_retv.append(slice) + pass + pass + pass + return list_retv + + def __event_append_check(self, graph_old: Dict[str, StoryMap], story_new: StoryMap) -> List[FragmentSlice]: + list_retv = [] + for slice in story_new.slice_list[1:]: + if slice.is_define_node[0]: + s_name = story_new.story_name + f_name = slice.is_define_node[1] + story_findout = graph_old[s_name] + if story_findout is None or story_findout.get_fragment_defined(f_name) is None: + list_retv.append(slice) + pass + pass + pass + return list_retv + + def __event_changed_check(self,): \ No newline at end of file diff --git a/parse/__pycache__/StoryMap.cpython-312.pyc b/parse/__pycache__/StoryMap.cpython-312.pyc new file mode 100644 index 0000000..fa22917 Binary files /dev/null and b/parse/__pycache__/StoryMap.cpython-312.pyc differ diff --git a/parse/__pycache__/__init__.cpython-312.pyc b/parse/__pycache__/__init__.cpython-312.pyc index 08e7bfe..fd33a09 100644 Binary files a/parse/__pycache__/__init__.cpython-312.pyc and b/parse/__pycache__/__init__.cpython-312.pyc differ diff --git a/parse/__pycache__/ast_load.cpython-312.pyc b/parse/__pycache__/ast_load.cpython-312.pyc index ba6adab..d3627ca 100644 Binary files a/parse/__pycache__/ast_load.cpython-312.pyc and b/parse/__pycache__/ast_load.cpython-312.pyc differ