From cec7faf053f961b17f3c462873fd4dc09ba5dbb9 Mon Sep 17 00:00:00 2001 From: codeboss <2422523675@qq.com> Date: Mon, 29 Jul 2024 21:25:38 +0800 Subject: [PATCH] diff-view --- .idea/workspace.xml | 69 ++++--- frame/CompareViews.py | 195 ++++++++++++++++++ frame/CompareWindow.py | 56 ----- frame/MergeView.py | 1 + .../__pycache__/CompareWindow.cpython-312.pyc | Bin 3042 -> 0 bytes frame/__pycache__/MergeView.cpython-312.pyc | Bin 0 -> 4234 bytes manage/__pycache__/MileStone.cpython-312.pyc | Bin 0 -> 903 bytes manage/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 145 bytes parse/StoryMap.py | 6 +- parse/StorylineCmp.py | 90 ++++++-- parse/__pycache__/StoryMap.cpython-312.pyc | Bin 7756 -> 7969 bytes .../__pycache__/StorylineCmp.cpython-312.pyc | Bin 0 -> 6558 bytes 12 files changed, 314 insertions(+), 103 deletions(-) create mode 100644 frame/CompareViews.py delete mode 100644 frame/CompareWindow.py delete mode 100644 frame/__pycache__/CompareWindow.cpython-312.pyc create mode 100644 frame/__pycache__/MergeView.cpython-312.pyc create mode 100644 manage/__pycache__/MileStone.cpython-312.pyc create mode 100644 manage/__pycache__/__init__.cpython-312.pyc create mode 100644 parse/__pycache__/StorylineCmp.cpython-312.pyc diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 13f6fbb..a316b03 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,7 +5,12 @@ - + + + + + + - + + + + - - - + - + - @@ -199,9 +205,14 @@ - file://$PROJECT_DIR$/manage/NovelManage.py - 4 - + + file://$PROJECT_DIR$/frame/CompareViews.py + 164 + diff --git a/frame/CompareViews.py b/frame/CompareViews.py new file mode 100644 index 0000000..9006bf6 --- /dev/null +++ b/frame/CompareViews.py @@ -0,0 +1,195 @@ +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() \ No newline at end of file diff --git a/frame/CompareWindow.py b/frame/CompareWindow.py deleted file mode 100644 index 8b3f761..0000000 --- a/frame/CompareWindow.py +++ /dev/null @@ -1,56 +0,0 @@ -from PyQt5.QtWidgets import QWidget, QTreeView, QVBoxLayout -from PyQt5.QtGui import QStandardItemModel, QStandardItem -from typing import List -from parse.ast_load import StoryLine, FragmentSlice, items_map - - -class CompareWin(QWidget): - def __init__(self): - QWidget.__init__(self) - self.setWindowTitle("故事线比较") - - self.__compare_model = QStandardItemModel(self) - self.__compare_view = QTreeView(self) - self.__compare_model.setHorizontalHeaderLabels(["节点名称", "比对参数"]) - self.__compare_view.setModel(self.__compare_model) - - layout = QVBoxLayout(self) - layout.addWidget(self.__compare_view) - pass - - def load_differents(self, changed: List[str], base_list: List[StoryLine], other_list: List[StoryLine]): - base_map = items_map(base_list) - other_map = items_map(other_list) - - for changed_item in changed: - item = QStandardItem(changed_item) - item2 = QStandardItem() - item.setEditable(False) - item2.setEditable(False) - self.__compare_model.appendRow([item, item2]) - - if changed_item in other_map: - story_origin = other_map[changed_item] - item_o = QStandardItem(story_origin.signature) - if not changed_item in base_map: - item2.setText("append") - else: - item2.setText("modify") - item_o2 = QStandardItem(story_origin.file_path) - item_o.setEditable(False) - item_o2.setEditable(False) - item.appendRow([item_o, item_o2]) - pass - else: - if changed_item in base_map: - story_modif = base_map[changed_item] - item_ext = QStandardItem(story_modif.signature) - item2.setText("remove") - item_ext2 = QStandardItem(story_modif.file_path) - item_ext.setEditable(False) - item_ext2.setEditable(False) - item.appendRow([item_ext, item_ext2]) - pass - pass - pass - pass diff --git a/frame/MergeView.py b/frame/MergeView.py index bd2f648..0b0613f 100644 --- a/frame/MergeView.py +++ b/frame/MergeView.py @@ -21,6 +21,7 @@ class LinesMergeView(QWidget, MemorySkin): self.refers_line = refdefs layout = QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) splitter = QSplitter(Qt.Orientation.Vertical, self) layout.addWidget(splitter) diff --git a/frame/__pycache__/CompareWindow.cpython-312.pyc b/frame/__pycache__/CompareWindow.cpython-312.pyc deleted file mode 100644 index 8d1017109f69e54c3f9d72ba508b008b810484b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3042 zcmahLOKcm*b(UN%hvbr?qD^E;w53XRB-)m08;xVdu>x3f;-Io(#Y)_=YPM_bNLrTU zs=F)MbXP#(I)IQk2#qw5fL_>BS@;ki;~bhC0|WuIAcJKGDr}(sBtQ;Am0WVl%uq{8 zwh|4nGjHE}^X9!b?=xRSqhUZs@3$XJe+dEjAN}!0U=!GQivY_&1uB;TSuVw8eJNko zpYn4Q=gaWfKq`x6&PGy^te6rx-~+D!HEWp+8AK4vt^c)EQ*q(D;Ob(3={%j3eW3p4P97DtUe0O8W3q;}wT(!#^;^EbQ7QCN;f}=BUbEiMnjU-;_YeZxX9UD`e_f}Sc$A1 zPHo7_gCsvTPt||;;oJ8=dH2yLpFaHPZ(slWuk&=<*Y8|=bnVWA!h4VYd7B{!?ZHQP z9=w0;;fJ@A5rbY5hO(^XG)tCo%tRKsOI5!-p;;MZP_N;3xpB*~JXO1KIZH#*pl-)) zJ2EWLsBR+y&+3MDRnJ*U<}6ZFWQ;18P{zcNkXT$B^u}>WQPo;jkm%=4l$pjthGm0E zW2UR?hhL4nFnQk4(`d>vC(q}tSv@z&0)Il+Gv?&9p=8lyLseqLz+B#-p1Io6Ko5Av z(i<{wkm)aA4K}%Oj|2CtNENuR3Pl%oIIv?i(O-cB_dAc?-e2x~eu=NZ#M-{z690Fd z!X1n+;lP9ok2~=A$Fa|2tIuDoz~9sbxNyG%_ZKJJ1B1?i!3uogesf}FqTJlQBvfI{ zJJM}C?6e)Oz<#oCgXjZraB5b`%^($rE-5CGGbGD#Shr@8!QiQSuoGtLJeF#7-kNH5 zJ#-iOMgrP?FLV|IHa7wa-0v>|uqaqm1{=0e=Lj~iw>GJZ&<^gFqBh)>1ElK-xha?| z=}-gqAUkNoZAmEDfdydPumd(LOpBq0j&>-`3ca_tw$`l)+k(w6aK=+azvy-z`lr_Y z#erP&Pw>>e$AN9<#-5Z{KR05BclAePSB~J0-U-{0t=`G~nb`=81)JaWkGMro@n`)L z{Rx|S&ysC6i|L=UIc7(9wJ5eL$IdoK?bueELm)(=_y*zuDTRTtm$9JC&7qu%1yZ84 z={yz;l+_nd5*oBM7_@)j2q`{iRLxQ@kt#blv@UtM%JWXeDK=R znW`9f#e^d!R^F(HJyo&U756&g-W604_cL0TBX+ssK}S4T8o7O-bgCi_-fQYu$vaJb z)y}?B!s$F-5ZBtCE!uAHDW~^TxoxN*R9kzBSDe;gR;9M(b2rZw`@WJ6-SgmzbZD); zwlrwyr+1k5ywfkV{MW_2j;n!=OhfBY6`%gRlr^}tgg|qdO>1kd4r9Zj@!_L5P zxof2GYE??OQlBIBm7cChM900ZlyszI=|Dv~QkC{BkKG(Ao-JvsJtr#CAVH7=|0BlN zPZ1BU4}nnBpiil>B%~cTN4gd1xM7)g(s>eFG8Sd|PxDHqMq!Q1piK(0f|}VRvoNI>sg%)#ed0T?ha7MFP?F_MSkrXqATlkzWS{{CMKCJ4Vb5AemB6NZeF1V>+kW)GRx{T#>L1t%SF@-7(s YFKE3Bh8!^Top6+k7FxdtWMM*n1R|`gBLDyZ diff --git a/frame/__pycache__/MergeView.cpython-312.pyc b/frame/__pycache__/MergeView.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd48e7e387f266b60fdf631fce0c87bb61499529 GIT binary patch literal 4234 zcmb_fU2Gf25#A$@6;oED3GVlEO{hF zD@6lzCG76a?#|54%ziWcOCaDQ(9)m$KJl8DkbmHZ-Q3m5_BDx+J47RzlqMNTl`;<1 zk#VZdjI7ETm+H#6Rkwuej_}jbtus`KPa($t_)o=W`22G$LERNtVg+d^D#qtMw8~#|?IgB^bYwWWTh$=Q8s~ zWv#mJ@(0OmM6%=yN!@_aF>TB?DqV|(exTkVjHr@EREH)pr{-X?=45iht;siJ)s=U_ zHQFW_pJU@pHm*bz<3ghFS7FXN#6RyTJKu)weU>sRz=>%^Fu6z%6d!f@& zlYVI5j*cI{dXeW+Y}U}PUd$Ws=dxEXqxLhoTw1@H;Bbel;(}<*;rYBpW3gm5X~bg7 zoy`{?wwK^}5xpC_6|&jcyDHymv4_y%Rk=jFH>t7>%Onc+1BPg+Sj_UpVws$_kjA+Z zi~V9Dp02Jqb%S%{%8~1R!h3Mugoq%Bu7t)Cy5-?aOm=u4 z<4bt{$I#y)M$ONunvY>ewG|*1Y^;!)B0uIdr6_pVY?;g?VC&c7g~243E4TT3EyAR$(> zXu&Z~ey$O6-EI8PK2>n(eba1?~Kn2SWZw`SE(a-Y3(_>s09~_~< z+M_e$Y%XiCtf5cF`CKxqb9{>&gT(Sr@g%HN0GwW7+<yqN_f z1}ZQfp&apsqt*Ej#Gd&$hb7CM7D>;dx}6zikpX-{Kuqw*%ObX8t3V`rVfnyLh^k>V zW(NHXg!~G5N?Xgc!=xQ+!L`K_J@l-*AJMvFvwJ{{&)a*}W{T|ttL{zOQl?>(hS$UE z(;L#pScwij-P`lKOP^l5d#&7i#OyutP%G{|u}Z~G!Ly@82c8cOZy3eFBajjTo3yP= zyG+`(cI4BOcTbjRLQsB7x+*>BSR^Q$r6oX(6-!! z=Mpk$Xw4}1jhKBS<-TKP-?4Jvd9&|)v2UV8FFb4CS8g9L+XucO4v1Wc#K7&Tn^PsF z7vs+bv@J@!mHK7(2>TQ0^_>xh_iSn@yXa}4LzMiVBdo#CwGyK)aCbj@DxMYS7yxI- zj9jB%Ex(bwn2sm2A_#wrlI%5>DE zqYr~$h8~5=V-x1sL~#tAx|_EEQEpAW?5v2xa&u-Z@N5)ty%(hL5O!F4REk%$i^yOc zBaoxE97~LbBKIJEVuvM}eh4}&a=TrDuR_CH1jfg~uTDkHV^iDsv3;CE7xdWWE*``( zI|DX@(%Lm2pJStz8;7N_NVnB`F+Xh_9iBESnNF|dRD>Uq^eS1>gP$UiI`zD6wThnx z!>S4%TGW;z5^m+{;$CPIONNZRXgMJlS~BGECCddV zZE4h!F{iy_UOr}$;ygV?Ko0^OEVlBBQ6yJhn3--z#P5-pPG z-^l^^|F_#E1^z{#d(k!`1wMXfE%k!HWJ{BXtgJ5o)!F&PxBp}P&eCs|ioX7pvrqip zYj1w`(|bRA6#3#uMgOUl@h3{p+D|^q-}}|0^IuF9mD4Ndp7=Z02G{qkdp3ea|M1HA erc6IN|Ka%?6PvQ(_5@1&G`wT>2kFLQ)3+ literal 0 HcmV?d00001 diff --git a/manage/__pycache__/MileStone.cpython-312.pyc b/manage/__pycache__/MileStone.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98b4483c0e30f67cd559c41c39dd5385abba2b31 GIT binary patch literal 903 zcmaJ<&1(}u6o0e3+1+dsi>(z}1xtHLF0CI3Z4gg-v{q6)#D!(eOxjKNBg}4W5-LD`_s1k+9d(s&L zf))~AA$uYUi|u>Qi{@K9B(CEVs}fLRdX7uNDDcT_(+;EdO%C8GZ3#qDVrtc@(!gU% z)rtb9QW7<&Upb$U%u%?P`5De~w++$xTL9FqqY@~<25g`UeI+QM#e&4-iicUM64YA{ z3lbGnRB(o(eQN6T!Ly>dM1vZsM4`FVidF;PED4dzLEwg_XZ!XFF<?zP#=)A7-ZamFXcb1Jfn0rx1&`yT4_*9{OEd34kMaq5F>osBbLQdb?W#LQ*65O zj>#_ZnL?TlFVq6Z{|+?6*{RTcsz31p+;GWLdWWY%TKJW_SVjmPgK-SEVim?7Z;l*| z7xrN^RuP)%&&0sPPALhyrJ=}T9h7XReIQQ^lyqmNm+$7cUJf*)^Qbr9o&Wg0j}Ekn zfu8NW`mAwE-T1WrVZ9Ufn%!m}^`*|np>}s?iCk8_|* HCZhiWNblOe literal 0 HcmV?d00001 diff --git a/manage/__pycache__/__init__.cpython-312.pyc b/manage/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f36462bd5206040b955bc7c4c83a66545099ee4b GIT binary patch literal 145 zcmX@j%ge<81c9u}(?IlN5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!GH|hq2`I|XN=+^) zjtQtN$;i)(2`&& str: - return r"\n".join(self.text_sections) + return "\n".join(self.text_sections) pass def set_to_memory(self, content: str): - self.text_sections = content.split(r"\n") + self.text_sections = content.split("\n") pass @@ -37,6 +38,7 @@ class StoryMap: def append_fragment_slice(self, node: FragmentSlice): self.slice_list[-1].next_node = node + node.parent_story = self.story_name node.prev_node = self.slice_list[-1] node.fragm_sort_i = len(self.slice_list) self.slice_list.append(node) diff --git a/parse/StorylineCmp.py b/parse/StorylineCmp.py index b1360d0..a368b3e 100644 --- a/parse/StorylineCmp.py +++ b/parse/StorylineCmp.py @@ -14,20 +14,20 @@ class CmpTool: def __init__(self): pass - def graph_compare(self, graph_new: Dict[str, StoryMap], graph_old: Dict[str, StoryMap]) -> Dict[ModifyReason, FragmentSlice]: + def graph_compare(self, graph_new: Dict[str, StoryMap], graph_old: Dict[str, StoryMap]) -> Dict[ModifyReason, List[FragmentSlice]]: fragments_has_removed = [] for story in graph_old.values(): - fragments_has_removed = fragments_has_removed + self.__event_remove_check(graph_new, story) + fragments_has_removed = fragments_has_removed + self.__slice_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) + fragments_has_appended = fragments_has_appended + self.__slice_append_check(graph_old, story) pass fragments_has_changed = [] for story in graph_new.values(): - fragments_has_changed = fragments_has_changed + self.__event_changed_check(graph_old, story) + fragments_has_changed = fragments_has_changed + self.__slice_changed_check(graph_old, story) pass return { @@ -36,35 +36,78 @@ class CmpTool: ModifyReason.Changed: fragments_has_changed, } - def __event_remove_check(self, graph_new: Dict[str, StoryMap], story_old: StoryMap) -> List[FragmentSlice]: + def __slice_remove_check(self, graph_new: Dict[str, StoryMap], story_old: StoryMap) -> List[FragmentSlice]: list_retv = [] + s_name = story_old.story_name + story_curr = graph_new[s_name] + for slice in story_old.slice_list[1:]: + if story_curr is None: + list_retv.append(slice) + continue + + # 确定情节定义节点 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: + if story_curr.get_fragment_defined(f_name) is None: + list_retv.append(slice) + pass + pass + # 确定情节引用节点 + else: + s_refer_name = slice.story_refer + f_refer_name = slice.fragm_refer + refer_curr = CmpTool.__locate_fragment_refer__(story_curr.slice_list[0], s_refer_name, f_refer_name) + if refer_curr 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]: + def __slice_append_check(self, graph_old: Dict[str, StoryMap], story_new: StoryMap) -> List[FragmentSlice]: list_retv = [] + s_name = story_new.story_name + story_prev = graph_old[s_name] + for slice in story_new.slice_list[1:]: + if story_prev is None: + list_retv.append(slice) + continue + + # 确定情节定义节点 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: + if story_prev.get_fragment_defined(f_name) is None: + list_retv.append(slice) + pass + pass + # 确定情节引用节点 + else: + s_refer_name = slice.story_refer + f_refer_name = slice.fragm_refer + refer_prev = CmpTool.__locate_fragment_refer__(story_prev.slice_list[0], s_refer_name, f_refer_name) + if refer_prev is None: list_retv.append(slice) pass pass pass + return list_retv - def __event_changed_check(self, graph_old: Dict[str, StoryMap], story_new: StoryMap) -> List[FragmentSlice]: + @classmethod + def __locate_fragment_refer__(cls, ref_story_head: FragmentSlice, story_self: str, fragm_self: str) -> FragmentSlice: + if ref_story_head is None: + return None + + # 确定属于引用节点 + if not ref_story_head.is_define_node[0]: + if ref_story_head.story_refer == story_self and ref_story_head.fragm_refer == fragm_self: + return ref_story_head + + return cls.__locate_fragment_refer__(ref_story_head.next_node, story_self, fragm_self) + + def __slice_changed_check(self, graph_old: Dict[str, StoryMap], story_new: StoryMap) -> List[FragmentSlice]: story_old = graph_old[story_new.story_name] if story_old is not None: return self.__event_changed_chack_for(story_old, story_new) @@ -73,19 +116,34 @@ class CmpTool: def __event_changed_chack_for(self, story_old: StoryMap, story_new: StoryMap) -> List[FragmentSlice]: list_retv = [] for fragm_curr in story_new.slice_list[1:]: + # 情节定义只比较定义节点本身 if fragm_curr.is_define_node[0]: fragm_prev = story_old.get_fragment_defined(fragm_curr.is_define_node[1]) + # 确定新旧图都定义了该情节 if fragm_prev is not None: if fragm_curr.fragm_sort_i != fragm_prev.fragm_sort_i: list_retv.append(fragm_curr) - pass - - if len(fragm_curr.text_sections) != len(fragm_prev.text_sections): + elif len(fragm_curr.text_sections) != len(fragm_prev.text_sections): list_retv.append(fragm_curr) elif fragm_curr.text_sections != fragm_prev.text_sections: list_retv.append(fragm_curr) pass pass pass + else: + refer_curr = fragm_curr + refer_prev = CmpTool.__locate_fragment_refer__(story_old.slice_list[0], refer_curr.story_refer, refer_curr.fragm_refer) + # 确定新旧图都定义了该引用节点 + if refer_prev is not None: + if refer_prev.fragm_sort_i != refer_curr.fragm_sort_i: + list_retv.append(refer_curr) + elif len(refer_prev.text_sections) != len(refer_curr.text_sections): + list_retv.append(refer_curr) + elif refer_prev.text_sections != refer_curr.text_sections: + list_retv.append(refer_curr) + pass + pass + pass pass + return list_retv \ No newline at end of file diff --git a/parse/__pycache__/StoryMap.cpython-312.pyc b/parse/__pycache__/StoryMap.cpython-312.pyc index 376af5411c922cd0e069783861d4c40df6b345eb..c7824f1175106aa0bc04860f0fa10bb0bd5dd3ac 100644 GIT binary patch delta 1958 zcmZ`)>uVHO6rVdYyF1xu9+S;(_Nh&(qj{(i+k`YVT9s1W1d=`!+v%E}#Id`x%bkgB zf>bS46a7nhhkk3J28D#71^ojGn??`!xKox}IgZhDDoj<;_qk znzajT8+o!{e0?Xkpyf>fpkQmqCHZRPa%4taP!ji*_N8R@=Fp9y8r|{K&CHF=Y~w;4 zPF)HAk<8qZ??i4#?j?8B4k$tVc2P+$DCrsJzS6d&1Z(Y1f_wIl6lJ<{v5hAk0gEQp z2*lpG(zxl^I2Kt>V_n_Lq?_%l8#;@8gE z$iUwk$yL$g3C#ezAgW=nCp6o+B7msY`#kDwQ&j*lmwJSP`nfA~Fm}D6dA{2p5=k$6 zC3qpU2W*
G_7_h9w~eycd~E$Y#u0@vKKMV@Lp012@t$8ViqDK&N2w0mdnU+JlVYOZn`k+Gycx7Cr zWR&=0V&n4!93e}%5o62sG4`NdBg4$!Fd60v@K1;_CSiH=w;LM8EtrPpcm3lgwVnMZ z&0@jzoy^;LD2X10z1lS9nA@~X<776Vu@OTAEaM|*#5_?ntqDk<&l^UdZ?jHyJBhQS z>Rxh;eWCsZgLSF-$s6czI_SVxowhONm}SeAQPX_%IDYpo0uRa?fbS1)GSy+V-hj0T zccU4IP>t>-7TekKp_kV;#C~X*go4{*-K{(wY!tjr_}V^miA%9PWQu(i>#2Qpc>d3r zj|}!_EMPq=TXAe{D(Pk35_WC0M#N!<;L|qmD9`9z!bcc-D{tGy= Bw?F^@ delta 1636 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