需求:將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 # *** 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 # *** 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 # 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='\**\**\姓*') 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='姓名') 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 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 ***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 ***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修復:zhengyong731@pingan.com.cn",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 中國大陸 許可協議進行許可。