from PyQt5.QtWidgets import QWidget, QVBoxLayout, QTableView, QSplitter, QApplication, QMainWindow from PyQt5.QtGui import QStandardItemModel, QStandardItem from typing import List, Dict, Tuple from manage.MileStone import base_store_path, current_store_path from parse.StorylineCmp import ModifyReason, CmpTool from parse.StoryMap import FragmentSlice, StoryMap, XAST_ParseTool, storyline_list2map import sys from PyQt5.QtCore import pyqtSignal, pyqtSlot, QModelIndex from frame.MergeView import LinesMergeView,MergeDestination class ResultList(QTableView): def __init__(self, parent): QTableView.__init__(self, parent) self.model = QStandardItemModel(self) self.model.setHorizontalHeaderLabels(["故事名称", "情节名称", "状态"]) self.setModel(self.model) self.setSelectionMode(QTableView.SelectionMode.SingleSelection) self.setSelectionBehavior(QTableView.SelectionBehavior.SelectRows) pass def present_affected_fragments(self,graph_new: Dict[str, StoryMap], cmp_result: Dict[ModifyReason, List[FragmentSlice]]): """ 展示影响节点 :return: """ old_remove = cmp_result[ModifyReason.Removed] affected_list = [] for node in old_remove: affected_list = affected_list + self.__find_remove_affected_slice_within_new(graph_new, node) pass fragm_items = self.__filter_and_related_defines_seek(affected_list) self.__append_fragments_list(fragm_items, "REMOVE") curr_append = cmp_result[ModifyReason.Append] affected_list = self.__find_append_and_changed_affected_slice(curr_append) fragm_items = self.__filter_and_related_defines_seek(affected_list) self.__append_fragments_list(fragm_items, "APPEND") curr_changed = cmp_result[ModifyReason.Changed] affected_list = self.__find_append_and_changed_affected_slice(curr_changed) fragm_items = self.__filter_and_related_defines_seek(affected_list) self.__append_fragments_list(fragm_items, "UPDATE") pass def __find_remove_affected_slice_within_new(self, graph_new: Dict[str, StoryMap], peers_old: FragmentSlice) -> List[FragmentSlice]: """ 获取受到移除节点直接影响的节点 :param graph_new: 新图 :param peers_old: 旧图节点 :return: """ retvs = [] story_name = peers_old.parent_story # 移除了情节定义 if peers_old.is_define_node[0]: refer_list = peers_old.refers_slice for refer_slice_prev in refer_list.values(): retvs = retvs + self.__find_remove_affect_within_new(graph_new, refer_slice_prev) pass story_curr = graph_new[story_name] # 新图内故事线存在,非最后一个节点,计算影响 if story_curr is not None and peers_old.next_node is not None: o_frag_def = peers_old.next_node frag_curr_def = story_curr.get_fragment_defined(o_frag_def.is_define_node[1]) if frag_curr_def is not None: retvs.append(frag_curr_def) pass pass else: refer_story_new = graph_new[story_name] slice_affected = peers_old.next_node # 非最后一个,需要计算影响,新图中的故事线存在,计算影响 if slice_affected is not None and refer_story_new is not None: v_s_name = slice_affected.story_refer v_f_name = slice_affected.fragm_refer # 获取收到影响的情节片段 slice_refer_now = CmpTool.__locate_fragment_refer__(refer_story_new.slice_list[0], v_s_name, v_f_name) if slice_refer_now is not None: retvs.append(slice_refer_now) pass pass return retvs def __find_append_and_changed_affected_slice(self, peers_new: List[FragmentSlice]) -> List[FragmentSlice]: retvs = [] for curr_node in peers_new: next_slice = curr_node.next_node if next_slice is not None: retvs.append(next_slice) return retvs def __filter_and_related_defines_seek(self, affect_directed: List[FragmentSlice])-> List[Tuple[str, str]]: story_fragment_names: Dict[str, Tuple[str, str]] = {} for node in affect_directed: # 情节定义节点 if node.is_define_node[0]: fragm_key = f"{node.parent_story}#{node.is_define_node[1]}" story_fragment_names[fragm_key] = (node.parent_story, node.is_define_node[1]) # 情节引用节点 else: fragm_key = f"{node.story_refer}#{node.fragm_refer}" story_fragment_names[fragm_key] = (node.story_refer, node.fragm_refer) pass pass return list(story_fragment_names.values()) def __append_fragments_list(self, fragment_keys: List[Tuple[str, str]], type: str) -> None: for key_def in fragment_keys: row = [] row.append(QStandardItem(key_def[0])) row.append(QStandardItem(key_def[1])) row.append(QStandardItem(type)) for cell in row: cell.setEditable(False) self.model.appendRow(row) pass pass pass class CompareWindow(QWidget): def __init__(self, graph_new: Dict[str, StoryMap], cmp_result: Dict[ModifyReason, List[FragmentSlice]], parent): QWidget.__init__(self, parent) self.graph_new_refer = graph_new layout = QVBoxLayout(self) self.splitter = QSplitter(self) layout.addWidget(self.splitter) self.result_list = ResultList(self) self.splitter.addWidget(self.result_list) self.splitter.addWidget(QWidget(self)) self.result_list.present_affected_fragments(graph_new, cmp_result) self.result_list.clicked.connect(self.present_fragment_merge_content) pass def present_fragment_merge_content(self, index: QModelIndex): if not index.isValid(): return story_name = self.result_list.model.item(index.row(), 0).text() fragm_name = self.result_list.model.item(index.row(), 1).text() story_node = self.graph_new_refer[story_name] fragm_node = story_node.get_fragment_defined(fragm_name) main_point = MergeDestination(story_name, fragm_node.prev_node, fragm_node) elist = [] for refstory in fragm_node.refers_slice.keys(): refslice = fragm_node.refers_slice[refstory] elist.append(MergeDestination(refstory, refslice.prev_node, refslice)) new_view = LinesMergeView(main_point, elist, self) new_view.load_from_mem() self.splitter.replaceWidget(1, new_view) pass pass if __name__ == "__main__": app = QApplication(sys.argv) win = QMainWindow() ast_old = XAST_ParseTool(base_store_path) ast_new = XAST_ParseTool(current_store_path) map_old = storyline_list2map(ast_old.story_list) map_new = storyline_list2map(ast_new.story_list) ast_old.storylines_plait(map_old) ast_new.storylines_plait(map_new) cmp_tool = CmpTool() all_changed = cmp_tool.graph_compare(map_new, map_old) view = CompareWindow(map_new, all_changed, win) win.setCentralWidget(view) win.show() app.exec()