需求:將xmind檔案轉為Excel檔案,并添加UI界面操作以降低操作難度,
這個需求一句話就講清楚了,但實際上還需要做很多作業:
1,了解Xmind檔案結構
2,提取Xmind檔案分支內容(重點)
3,UI界面(非必要)
一,了解Xmind檔案結構
1,xmind檔案形式:樹形分支結構(可以先思考如何提取各分支內容),

Excel提取為何種形式:主干與分支連接,用“\”號連接,(了解原理后也可以寫成其他你想要的形式)
舉例:
https\Tunnel to含義\Tunnel(隧道),中間物體
https\Tunnel to含義\隱藏:規則-隱藏連接
二,提取Xmind檔案分支內容(重點)
那么如何決議上述為具體的資料結構呢?用“from xmindparser import xmind_to_dict”,用xmindparser包將其轉化為字典格式再做處理,
再舉個例子:

這個xmind檔案通過xmind_to_dict可以轉換為什么樣呢?
{'title': 'A', 'topics': [{'title': 'B1'}, {'title': 'B2', 'topics': [{'title': 'C1'}, {'title': 'C2'}]}, {'title': 'B3'}]}
還是不夠直觀,用json分析器展開再看:
[
{
'title': '畫布 1',
'topic': {
'title': 'A',
'topics': [
{
'title': 'B1'
},
{
'title': 'B2',
'topics': [
{
'title': 'C1'
},
{
'title': 'C2'
}
]
},
{
'title': 'B3'
}
]
},
'structure': 'org.xmind.ui.map.unbalanced'
}
]
內容裝在一個list里,字典最外層title為“畫布1”,topic裝有我們需要的內容(可以看出其他位置叫topics),這是xmind檔案中沒有的,經驗證這是個固定值,可以忽略,然后通過取list,A是字典中value值,key為title,其子節點裝在topics的分支中,處理后是這樣,最外層是dict:

那么問題來了,如何將其處理為想要的形式呢?一層層的取key、value嗎?
當然不是,那作業量將非常龐大,而且不利于閱讀和修改,還記得剛才的思考內容嗎?這里我們就需要用到你所想的,沒錯,遞回,
為什么要用遞回,我們仔細看看結構:
A的三個子節點分別為B1,B2,B3,B2 的子節點為C1C2,參看上圖你想到了什么?
1,標題和子節點放在不同的item里,即topic放根節點,那topics則放其子節點,
2,topics無子節點呢?像C1C2那樣的點沒有子節點,我們發現,它也沒有topics;同時,沒有子節點證明它是這條分支的最后一層了;試想,如果A沒子節點,就只有它一個呢?正如你所想,它只有一個title,沒有topics,
3,A的子節點是一個list,list可以有多個子節點,子節點若還有子節點則有topics,若沒有則沒有topics,
4,title永遠裝的是string,而topics可以裝list,list里還有dict,也可以沒有topics,
看了四點還是覺得很亂?
我們需要一層層取子節點才可以得到我們想要的A\B2\C1、A\B2\C2,節點值永遠在title里,而子節點卻在dict、list里的dict里,而且子節點里有的只有一個title,有的有一個title加一個topics,前文已經說過,有topics的是還有其子節點的,
那么現在應該清晰點了吧?還不清楚?那偽代碼再來一遍:
如果 輸入字典:
當字典只有有title時:
操作1
當字典有title和topics時:
操作2
如果 輸入list:
遍歷list里的字典:
對每個字典進行操作3
如果 輸入的既不是字典也不是list:
輸出值
思路框架已經搭建了,那操作1、2、3是什么呢?其實就是本框架的重復操作,呼叫下自己就可以了,不嚴謹的說這就是遞回的通俗解釋,
我們貼下代碼:
1 def TraversalXmind(root,rootstring): 2 if isinstance(root, dict): 3 if len(root) == 2: 4 TraversalXmind(root['topics'], str(rootstring) ) 5 if len(root) == 1: 6 TraversalXmind(root['title'],str(rootstring) ) 7 8 elif isinstance(root, list): 9 for sonroot in root: 10 TraversalXmind(sonroot, str(rootstring) + "\\" + sonroot['title']) 11 12 elif isinstance(root,str): 13 print(str(rootstring) ) 14 15 # TraversalXmind(root, "\\AA")
完整代碼是什么呢?
將上述轉換程序卸載model.py中:
1 # -*-coding:utf-8 -*- 2 # Author : zhengyong 3 # Time : 2020/11/5 23:06 4 # FileName: model.py 5 6 from xmindparser import xmind_to_dict 7 import os,csv 8 # filepath1 = os.path.abspath(os.path.dirname(__file__)) 9 # print(filepath1) 10 11 # filepath = "D:\\test.xmind" 12 # inputedXmind = xmind_to_dict(filepath) 13 # root = inputedXmind[0]['topic'] 14 15 def traversalXmind(root, rootstring,lisitcontainer): 16 """ 17 功能:遞回dictionary檔案得到容易寫入Excel形式的格式, 18 注意:rootstring都用str來處理中文字符 19 @param root: 將xmind處理后的dictionary檔案 20 @param rootstring: xmind根標題 21 """ 22 if isinstance(root, dict): 23 if len(root) == 2: 24 traversalXmind(root['topics'], str(rootstring),lisitcontainer) 25 if len(root) == 1: 26 traversalXmind(root['title'], str(rootstring),lisitcontainer) 27 28 elif isinstance(root, list): 29 for sonroot in root: 30 traversalXmind(sonroot, str(rootstring) + "\\" + sonroot['title'],lisitcontainer) 31 32 elif isinstance(root, str): 33 lisitcontainer.append(str(rootstring)) 34 35 def getCase(root): 36 rootstring = root['title'] 37 lisitcontainer = [] 38 traversalXmind(root, rootstring,lisitcontainer) 39 # print(lisitcontainer) 40 return lisitcontainer 41 42 # def getTestCase(filename,lisitcontainer,directory,group,runType,testcaseType,testType): 43 # header = [ 44 # '測驗案例路徑', 45 # '測驗案例名稱', 46 # '測驗案例描述', 47 # '計劃執行時長(分鐘)', 48 # '步驟描述', 49 # '預期結果', 50 # '優先級', 51 # '小組', 52 # '執行方式', 53 # '測驗模塊', 54 # '版本號', 55 # '用例型別', 56 # '測驗型別', 57 # '關聯故事卡片ID', 58 # ] 59 # 60 # with open(filename,'w',newline='') as f: 61 # writer = csv.DictWriter(f,fieldnames=header), 62 # writer.writeheader() 63 # for row in lisitcontainer: 64 # writer.writerow({ 65 # '測驗案例路徑': directory, 66 # '測驗案例名稱': row, 67 # '小組': group, 68 # '執行方式': runType, 69 # '用例型別':testcaseType, 70 # '測驗型別': testType, 71 # })
三,UI界面
UI界面用tkinter寫的,直接上代碼:
1 #-*-coding:utf-8 -*- 2 # Author : zhengyong 3 # Time : 2020/11/14 23:54 4 # FileName: main1.py 5 # https://github.com/ndnmonkey/transXmindToCSV.git 6 7 import model 8 from xmindparser import xmind_to_dict 9 import tkinter 10 from tkinter import filedialog 11 from tkinter import * 12 import os,csv 13 14 15 window = tkinter.Tk() 16 window.title("用例匯出工具") 17 # 設定視窗圖示 18 # window.iconbitmap("D:\Code\Python\XmindToExcel\image\icon.ico") 19 # window.iconbitmap("image\ifavicon.ico") 20 window.geometry("400x370+200+50") 21 22 # 標題 23 labeltitle = tkinter.Label(window,text = "用例匯出工具",font = ('幼圓',20)).pack() 24 25 # 創建選單欄 26 MenuBar = tkinter.Menu(window) 27 # 將選單欄放到主視窗 28 window.config(menu=MenuBar) 29 # 創建檔案選單,不顯示分窗 30 fileBar = tkinter.Menu(MenuBar, tearoff=0) 31 # 添加檔案選單項 32 fileBar.add_command(label="用法說明") 33 # fileBar.add_command(label="聯系作者") 34 # 創建分割線 35 # fileBar.add_separator() 36 fileBar.add_command(label="退出", command=window.destroy) 37 # 將檔案選單添加到菜單欄 38 MenuBar.add_cascade(label="選單", menu=fileBar) 39 40 xlabe = 20 41 xtext = 80 42 y =10 43 step = 30 44 textwidth = 250 45 46 inputtext1 = tkinter.StringVar(value=https://www.cnblogs.com/two-peanuts/archive/2020/11/15/'\常規版本用例\當前版本名\姓名\需求名') 47 labe1 = tkinter.Label(window, text='用例路徑:').place(x=xlabe,y = y + step) 48 text1 = tkinter.Entry(window, show=None, textvariable=inputtext1) 49 text1.place(width=textwidth,x=xtext+ 1*step,y=y + step) 50 51 inputtext2 = tkinter.StringVar(value=https://www.cnblogs.com/two-peanuts/archive/2020/11/15/'小組長姓名') 52 labe2 = tkinter.Label(window, text='組長姓名:').place(x=xlabe,y=y + 2*step) 53 text2 = tkinter.Entry(window, show=None, textvariable=inputtext2) 54 text2.place(width=textwidth,x=xtext+ 1*step,y=y + 2*step) 55 56 inputtext3 = tkinter.StringVar(value=https://www.cnblogs.com/two-peanuts/archive/2020/11/15/'手工測驗') 57 labe3 = tkinter.Label(window, text='執行方式:').place(x=xlabe,y=y + 3*step) 58 text3 = tkinter.Entry(window, show=None, textvariable=inputtext3) 59 text3.place(width=textwidth,x=xtext+ 1*step,y=y + 3*step) 60 61 inputtext4 = tkinter.StringVar(value=https://www.cnblogs.com/two-peanuts/archive/2020/11/15/'系統用例') 62 labe4 = tkinter.Label(window, text='用例型別:').place(x=xlabe,y=y + 4*step) 63 text4 = tkinter.Entry(window, show=None, textvariable=inputtext4) 64 text4.place(width=textwidth,x=xtext+ 1*step,y=y + 4*step) 65 66 inputtext5 = tkinter.StringVar(value=https://www.cnblogs.com/two-peanuts/archive/2020/11/15/'手工測驗') 67 labe5 = tkinter.Label(window, text='測驗型別:').place(x=xlabe,y=y + 5*step) 68 text5 = tkinter.Entry(window, show=None, textvariable=inputtext5) 69 text5.place(width=textwidth,x=xtext+ 1*step,y=y + 5*step) 70 71 def getTextValues(): 72 templist = [] 73 var1 = text1.get();templist.append(var1) 74 var2 = text2.get();templist.append(var2) 75 var3 = text3.get();templist.append(var3) 76 var4 = text4.get();templist.append(var4) 77 var5 = text5.get();templist.append(var5) 78 # print("1",templist) 79 return templist 80 81 casebutton1 = tkinter.Button(window,text = '1,提交用例資訊',width=5,height=1,command=getTextValues) 82 casebutton1.place(x=110,y=y + 6*step) 83 casebutton1.configure(width = 34, height = 2) 84 85 def open_file(): 86 templist = getTextValues() 87 # print("2",templist) 88 filename = filedialog.askopenfilename(title='打開Xmind檔案', filetypes=[('xmind', '*.xmind')]) 89 90 entry_filename.delete(0, END) 91 entry_filename.insert('insert', filename) 92 # print(entry_filename,type(entry_filename)) 93 94 # print(filename) 95 fname = entry_filename.get() #用get提取entry中的內容 96 fname = str(fname).replace('/','\\\\') 97 # print("fname",fname) 98 99 # filepath = "D:\\test.xmind" 100 # inputedXmind = xmind_to_dict(filepath) 101 # root = inputedXmind[0]['topic'] 102 # 103 inputedXmind = xmind_to_dict(fname) 104 root = inputedXmind[0]['topic'] 105 lisitcontainer = model.getCase(root) 106 # print(lisitcontainer) 107 108 # templist 109 directory = templist[0] 110 group = templist[1] 111 runType = templist[2] 112 testcaseType = templist[3] 113 testType = templist[4] 114 # filename = 'testcase.csv' 115 header = [ 116 '測驗案例路徑', 117 '測驗案例名稱', 118 '測驗案例描述', 119 '計劃執行時長(分鐘)', 120 '步驟描述', 121 '預期結果', 122 '優先級', 123 '小組', 124 '執行方式', 125 '測驗模塊', 126 '版本號', 127 '用例型別', 128 '測驗型別', 129 '關聯故事卡片ID', 130 ] 131 # print(filename) #D:\\test.xmind 132 savepath = fname.split('.')[0] + ".csv" 133 # print("savepath:",savepath) 134 135 with open(savepath, 'w', newline='') as f: 136 writer = csv.DictWriter(f, fieldnames=header) 137 writer.writeheader() 138 for row in lisitcontainer: 139 writer.writerow({ 140 '測驗案例路徑': directory, 141 '測驗案例名稱': row, 142 '小組': group, 143 '執行方式': runType, 144 '用例型別': testcaseType, 145 '測驗型別': testType, 146 }) 147 148 # 設定button按鈕接受功能 149 importbutton = tkinter.Button(window, text="2,匯入Xmind檔案", command=open_file) 150 importbutton.place(x=110,y=y + 9*step) 151 importbutton.configure(width =34,height=2) 152 153 # 設定entry 154 entry_filename = tkinter.Entry(window, width=30, font=("宋體", 10, 'bold')) 155 entry_filename.place(width=textwidth,x=xtext+ 1*step,y=y + 8*step) 156 157 # 著作權頁 158 labelright = tkinter.Label(window,text = "version 0.3 bug修復:[email protected]",font = ('宋體',8)).place(x=60,y=350) 159 # Tk().iconbitmap('D:\Code\Python\XmindToExcel\image\icon.ico') 160 window.mainloop()
那么這時候還有一個問題,運行之后的結果是什么?

用pyinstaller就可以打包代碼檔案變成可執行檔案:
方法:
1,安裝pyinstaller(不具體介紹)
2,使用:
命令列:
pyinstaller -F 主檔案名.py
運行可執行檔案有黑框怎么辦?
pyinstaller -F -w mycode.py (-w就是取消視窗)
3,生成的可執行檔案在哪?
在主檔案同級檔案夾dist中,
4,具體檔案結構:

四,宣告
所有代碼均為原創,因為實在找不到可用的教程,了解到找教程的艱辛,所以拋磚引玉,本人水平有限,如果文章和代碼有表述不當之處,還請不吝賜教,可以交流討論,
歡迎轉載,請注明以下內容:
本人在cnblogs上的ID為puppet洛洛,博客地址為https://www.cnblogs.com/two-peanuts/所有包含原創宣告的博客均為本人原創作品,博客的內容除已注明的參考文獻外均為本人獨立研究成果,除特殊注明外均采用 知識共享 署名-非商業性使用-相同方式共享 3.0 中國大陸 許可協議進行許可,
五,github地址
具體的檔案結構已傳至github,github地址:https://github.com/ndnmonkey/transXmindToCSV ,歡迎star,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/217523.html
標籤:其他
