更新:
2017.07.17 補充滾動條、win批處理拉起py
2017.08.13 新增自定義圖示
--------原創 blogs: http://www.cnblogs.com/chenyuebai --------
一、背景:
1.工作中自己及同僚在查資料庫、測試接口時需要對一些字元串或json串作預處理,目前這些問題網上均有線上轉換的工具,但很繁雜,可能需要打開幾個網頁視窗;
2.之前給妹子做的文本處理工具(http://www.cnblogs.com/chenyuebai/p/6999561.html)是基于cmd指令行的,不太友好;
3.自己做的一些小工具也基本都是指令行執行的,也想接觸下GUI;
基于以上,了解到python自帶的tkinter庫可以初步滿足UI的需求,業餘時間做了個圖形化工具,涉及視窗、文本、按鈕、标簽、菜單、菜單樹、滾動條、檔案處理、界面布局等;
下文将梳理從零到完成的全過程,作為自己GUI學習的一個總結;
ps:後面版本間隙有時間的話想試試用pyQt來做。。tkinter做出的界面确實。。。23333
工具界面截圖:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL0QTN0IDMwUTOtIjM4UTN4MTOwITM3AzNxAjMtQjM1EDM58CX3AzNxAjMvwFNyUTMwkzLcd2bsJ2Lc12bj5ycn9Gbi52YuUTMwIzcldWYtl2Lc9CX6MHc0RHaiojIsJye.png)
二、實作
1.安裝所需要的庫 ps:環境是win7+python 3.6,tkinter是py的标準庫,即:如不涉及EXCEL處理,可跳過此步驟;使用python處理Excel表格,後面有時間會單獨整理
(1)et-xmlfile openpyxl安裝需要依賴,工具裡有涉及處理EXCEL(xlsx),需要用到openpyxl庫;
(2)jdcal 同上
(3)openpyxl 處理EXCEL(xlsx)
(4)xlrd 讀取EXCEL(xls)
(5)xlwt 寫入EXCEL(xls)
(6)xltils 配合上面兩個庫對已存在表格進行修改
2.建立父視窗(可了解為界面的最底層)
#################################################################
#author: 陳月白
#_blogs: http://www.cnblogs.com/chenyuebai/
#################################################################
from tkinter import *
def gui_start():
init_window = Tk() #執行個體化出一個父視窗
init_window.mainloop() #父視窗進入事件循環,可以了解為保持視窗運作,否則界面不展示
gui_start()
運作結果:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
運作會彈出上圖所示的空白視窗,這裡我們還可以修改視窗預設大小、預設彈出位置、視窗名、父視窗顔色、虛化等等;
init_window.title("文本處理工具 by: 陳月白")
init_window.geometry('290x160+10+10') #290 160為視窗大小,+10 +10 定義視窗彈出時的預設展示位置
init_window["bg"] = "pink" #視窗背景色,其他背景色見:blog.csdn.net/chl0000/article/details/7657887
init_window.attributes("-alpha",0.8) #虛化 值越小虛化程度越高
注意要加在 init_window.mainloop() 之前;
運作結果:
整理下代碼:
#################################################################
#author: 陳月白
#_blogs: http://www.cnblogs.com/chenyuebai/
#################################################################
from tkinter import *
import hashlib
class MY_GUI():
def __init__(self,init_window_name):
self.init_window_name = init_window_name
#設定視窗
def set_init_window(self):
self.init_window_name.title("文本處理工具_v1.2 by: 陳月白") #視窗名
self.init_window_name.geometry('1068x680+10+10') #290 160為視窗大小,+10 +10 定義視窗彈出時的預設展示位置
self.init_window_name["bg"] = "pink" #視窗背景色,其他背景色見:blog.csdn.net/chl0000/article/details/7657887
self.init_window_name.attributes("-alpha",0.9) #虛化,值越小虛化程度越高
def gui_start():
init_window = Tk() #執行個體化出一個父視窗
ZMJ_PORTAL = MY_GUI(init_window)
# 設定根視窗預設屬性
ZMJ_PORTAL.set_init_window()
init_window.mainloop() #父視窗進入事件循環,可以了解為保持視窗運作,否則界面不展示
gui_start()
先到這,有時間再整理。。
--------------------------------------------------------------------------------
繼續。
3.父視窗完成後,即可基于父視窗建立其他元件
下面以“字元串轉MD5”為例,介紹資料讀取、按鈕、布局、事件觸發、背景轉換處理、界面展示結果;
(1)标簽
标簽可以使用tkinter裡的Label()方法,生成标簽,括号内可帶入其他屬性參數;
生成标簽後需使用grid()或者pack()方法使其展示; #注:tkinter對于同一個父視窗,隻可允許使用一種布局函數;另外還有個place()方法,不過目前我還沒用過;
self.init_data_label = Label(self.init_window_name, text="待處理資料")
self.init_data_label.grid(row=0, column=0)
self.result_data_label = Label(self.init_window_name, text="輸出結果")
self.result_data_label.grid(row=0, column=12)
這裡正好提一下grid(),說下我的了解。grid()方法是tkinter庫自帶的布局函數,他可以将父視窗分割成類似一個Excel表格,對單個元件按照參數中傳遞的row、column放置在指定位置,并展示;
下圖紅框部分,代表整個父視窗界面:
代碼中的:self.init_data_label.grid(row=0, column=0) 代表将“init_data_label”這個标簽放置在頁面的第一行,第一列的位置(即左上角),并展示;
(2)文本框
tkinter庫中的Text()方法,可以建立一個文本框,用于資料的錄入和結果展示;Text()方法可以傳入其他參數值,用于定義如背景顔色,前景色,字型,字号、字色等
self.init_data_Text = Text(self.init_window_name, width=67, height=35) #原始資料錄入框
self.init_data_Text.grid(row=1, column=0, rowspan=10, columnspan=10)
self.result_data_Text = Text(self.init_window_name, width=70, height=49) #處理結果展示
self.result_data_Text.grid(row=1, column=12, rowspan=15, columnspan=10)
self.init_data_Text.grid(row=1, column=0, rowspan=10, columnspan=10) #"rowspan=10" 是grid()方法的一個參數,用于指定該元件(init_data_Text)占用的行數,同理columnspan用于指定占用列數;
(3)按鈕
按鈕的建立可以使用Button()方法實作,可自定義按鈕大小,寬度,按鈕文本,背景色,以及需要觸發的事件;
#按鈕
self.str_trans_to_md5_button = Button(self.init_window_name, text="字元串轉MD5", bg="lightblue", width=10,command=self.str_trans_to_md5) # 調用内部方法 加()為直接調用
self.str_trans_to_md5_button.grid(row=1, column=11)
觸發事件:在定義按鈕時,使用command=functionname這個參數來指定;當按鈕被點選,就會調用command後的方法,進而完成事件的觸發;
這裡有個小坑。。。就是command後跟函數名時,不可加括号,否則在代碼執行時,就會觸發該函數;
舉個例子,上面貼的代碼中“command=self.str_trans_to_md5”若寫為“command=self.str_trans_to_md5()”,該gui_test.py在執行時便會調用類中的self.str_trans_to_md5()方法,而不是在點選按鈕時才觸發;
(4)事件代碼:背景的處理
step3中在定義按鈕時,會選擇觸發一個事件(或稱為函數),函數的實作涉及:從界面文本框中讀取待處理資料---邏輯處理---界面結果文本框展示
code:
def str_trans_to_md5(self):
src = self.init_data_Text.get(1.0,END).strip().replace("\n","").encode()
#print("src =",src)
if src:
try:
myMd5 = hashlib.md5()
myMd5.update(src)
myMd5_Digest = myMd5.hexdigest()
#print(myMd5_Digest)
#輸出到界面
self.result_data_Text.delete(1.0,END)
self.result_data_Text.insert(1.0,myMd5_Digest) #從第一行開始
except:
self.result_data_Text.delete(1.0,END)
self.result_data_Text.insert(1.0,"字元串轉MD5失敗")
else:
print("ERROR") #界面左下角會有日志框,支援動态列印,下面會提到
讀取待處理資料:對之前定義的self.init_data_Text(待處理資料文本框)可使用tkinter庫中自帶的get()方法,擷取文本框中的内容,str類型;
邏輯處理:MD5加密,不展開了,從網上找的例子照着敲;
界面結果文本框展示:對擷取的字元串處理後,即可使用Text.insert(index,strdata)方法插入結果文本框:self.result_data_Text;
插入前建議先清理下,Text.delete(1.0,END)代表将文本框從第一行删除至最後一行,即清空;
(5)日志列印
和上面沒啥差別,直接貼代碼了~
def get_current_time(self):
current_time = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
return current_time
#日志動态列印
def write_log_to_Text(self,logmsg):
global LOG_LINE_NUM
current_time = self.get_current_time()
logmsg_in = str(current_time) +" " + str(logmsg) + "\n" #換行
if LOG_LINE_NUM <= 7:
self.log_data_Text.insert(END, logmsg_in)
LOG_LINE_NUM = LOG_LINE_NUM + 1
else:
self.log_data_Text.delete(1.0,2.0)
self.log_data_Text.insert(END, logmsg_in)
#移動光标
self.log_data_Text.focus_force()
整體的例子代碼如下:
#################################################################
#author: 陳月白
#_blogs: http://www.cnblogs.com/chenyuebai/
#################################################################
from tkinter import *
import hashlib
import time
LOG_LINE_NUM = 0
class MY_GUI():
def __init__(self,init_window_name):
self.init_window_name = init_window_name
#設定視窗
def set_init_window(self):
self.init_window_name.title("文本處理工具_v1.2 by: 陳月白") #視窗名
#self.init_window_name.geometry('320x160+10+10') #290 160為視窗大小,+10 +10 定義視窗彈出時的預設展示位置
self.init_window_name.geometry('1068x681+10+10')
#self.init_window_name["bg"] = "pink" #視窗背景色,其他背景色見:blog.csdn.net/chl0000/article/details/7657887
#self.init_window_name.attributes("-alpha",0.9) #虛化,值越小虛化程度越高
#标簽
self.init_data_label = Label(self.init_window_name, text="待處理資料")
self.init_data_label.grid(row=0, column=0)
self.result_data_label = Label(self.init_window_name, text="輸出結果")
self.result_data_label.grid(row=0, column=12)
self.log_label = Label(self.init_window_name, text="日志")
self.log_label.grid(row=12, column=0)
#文本框
self.init_data_Text = Text(self.init_window_name, width=67, height=35) #原始資料錄入框
self.init_data_Text.grid(row=1, column=0, rowspan=10, columnspan=10)
self.result_data_Text = Text(self.init_window_name, width=70, height=49) #處理結果展示
self.result_data_Text.grid(row=1, column=12, rowspan=15, columnspan=10)
self.log_data_Text = Text(self.init_window_name, width=66, height=9) # 日志框
self.log_data_Text.grid(row=13, column=0, columnspan=10)
#按鈕
self.str_trans_to_md5_button = Button(self.init_window_name, text="字元串轉MD5", bg="lightblue", width=10,command=self.str_trans_to_md5) # 調用内部方法 加()為直接調用
self.str_trans_to_md5_button.grid(row=1, column=11)
#功能函數
def str_trans_to_md5(self):
src = self.init_data_Text.get(1.0,END).strip().replace("\n","").encode()
#print("src =",src)
if src:
try:
myMd5 = hashlib.md5()
myMd5.update(src)
myMd5_Digest = myMd5.hexdigest()
#print(myMd5_Digest)
#輸出到界面
self.result_data_Text.delete(1.0,END)
self.result_data_Text.insert(1.0,myMd5_Digest)
self.write_log_to_Text("INFO:str_trans_to_md5 success")
except:
self.result_data_Text.delete(1.0,END)
self.result_data_Text.insert(1.0,"字元串轉MD5失敗")
else:
self.write_log_to_Text("ERROR:str_trans_to_md5 failed")
#擷取目前時間
def get_current_time(self):
current_time = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
return current_time
#日志動态列印
def write_log_to_Text(self,logmsg):
global LOG_LINE_NUM
current_time = self.get_current_time()
logmsg_in = str(current_time) +" " + str(logmsg) + "\n" #換行
if LOG_LINE_NUM <= 7:
self.log_data_Text.insert(END, logmsg_in)
LOG_LINE_NUM = LOG_LINE_NUM + 1
else:
self.log_data_Text.delete(1.0,2.0)
self.log_data_Text.insert(END, logmsg_in)
def gui_start():
init_window = Tk() #執行個體化出一個父視窗
ZMJ_PORTAL = MY_GUI(init_window)
# 設定根視窗預設屬性
ZMJ_PORTAL.set_init_window()
init_window.mainloop() #父視窗進入事件循環,可以了解為保持視窗運作,否則界面不展示
gui_start()
上面例子代碼的執行結果:
大概就是這些,其他功能、按鈕大同小異,就不贅述了;
有問題歡迎留言交流;
後面有時間會補充用windows批處理拉起python代碼,這樣就可以像普通軟體一樣使用這個小工具了;
2017.07.14
--------------------------------------------------------------------------------
繼續。
(6)滾動條
對于展示長文本,需要在Text文本框側翼(或下方)提供滾動條;tkinter庫中提供Scrollbar()方法建立一個滾動條
# 滾動條
self.result_data_scrollbar_y = Scrollbar(self.init_window_name) #建立縱向滾動條
self.result_data_scrollbar_y.config(command=self.result_data_Text.yview) #将建立的滾動條通過command參數綁定到需要拖動的Text上
self.result_data_Text.config(yscrollcommand=self.result_data_scrollbar_y.set) #Text反向綁定滾動條
self.result_data_scrollbar_y.grid(row=1, column=23, rowspan=15, sticky='NS')
ps1:滾動條與待綁定的元件(Text或其他)時,需要二者互相綁定,進而達到拖動任意一方,對應方同步移動;
ps2:grid()中rowspan的值最好取Text文本框的值,可以使滾動條長度完美貼合文本框
效果:
三、其他
1.windows批處理拉起python
py代碼完成後,為友善使用,可以使用windows批處理腳本拉起python代碼
common_tools_gui.py所在目錄下建立start.bat,寫入 @python common_tools_gui.py %* @pause
@pause可以保持代碼一直處于運作狀态,而不是控制台一閃而過; #python需配置在環境變量中,或者使用絕對路徑。。
但也會帶來一個問題,就是在輕按兩下這個批處理時,軟體界面出來的同時,會附帶一個黑色的cmd控制台視窗;
百度了一下,可以使用windows的.vbs拉起批處理來規避:
common_tools_gui.py所在目錄下建立start_my_gui.vbs,寫入
Set ws = CreateObject("Wscript.Shell")
ws.run "cmd /c start.bat",vbhide
此時輕按兩下該vbs腳本,即可拉起軟體界面,且隐藏cmd控制台界面;
2. 暫時木有了。。
後面項目間隙有可能會研究下robotFramework~
也有可能會試着用pyQt重寫下這個界面
先到這了;
2017.07.17
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3.自定義圖示
python打開工具時,左上角預設為tkinter預設圖示
,工作列為python預設圖示;
查了下,python的tkinter庫支援自定義圖示:py檔案目前目錄下制作ico圖示檔案(https://www.ico.la/),父視窗使用iconbitmap()方法,指定該檔案即可;
self.init_window_name.iconbitmap('text_processing_tools.ico') #指定界面圖示
中途有個坑,就是僅左上角生效,但windows下方的工作列無效;
百度之,大概意思是文本工具的py檔案運作時,windows認為彈出的工具視窗是python解釋器的衍生程式(子程式?),即預設仍使用pythpn解釋器的圖示;
需要在代碼中指定彈出視窗的AppUserModelID即可:
import ctypes
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("myappid")
效果:
左上角:
底側工作列:
附目錄結構:
2017.08.13
轉載于:https://www.cnblogs.com/chenyuebai/p/7150382.html