平時的 Python 學習過程比較枯燥,想要保持興趣和提高,就得上手親自做一些小項目
本次小項目是利用 Python 自帶的 tkinter 模塊構造簡單的 GUI伞辛,利用PyMySQL 對數(shù)據(jù)庫進行操作 对室,數(shù)據(jù)存儲利用 json模塊隧甚,這些都是比較基礎的東西,做的過程可以逐漸熟悉上手
此工具可應用于多個數(shù)據(jù)庫存在相同的表名等情況下峦树,進行批量操作
開發(fā)環(huán)境如下:
- Python 3.6.6
- MySQL 8.0
- PyMySQL 0.9.2 (pip即可)
- IDE:Geany
先來看看項目完成的界面
主界面
添加鍵界面
功能說明
1辣辫、左上方是 SQL 語句輸入欄,輸入語句后點執(zhí)行按鍵魁巩,執(zhí)行按鍵會從本地存儲的 json 文件獲取數(shù)據(jù)庫信息急灭,連接數(shù)據(jù)庫,然后執(zhí)行語句歪赢,再關閉數(shù)據(jù)庫
2化戳、主界面有 2 個清屏按鍵,分別為語句清屏和 Info 清屏;語句清屏点楼,顧名思義是將 SQL 語句清除扫尖, Info 清屏是將下方的執(zhí)行信息欄的報錯清空
3、退出程序按鍵掠廓,退出該程序
4换怖、添加按鍵,點擊后彈出添加界面蟀瞧,有 6 個輸入欄沉颂,其中一個帶有默認值, 6 欄全部輸入信息才能點擊確認悦污,否則出現(xiàn)提示框提示將信息填寫完整铸屉,取消即退出該界面;點擊確認后切端,新的數(shù)據(jù)庫信息將保存到本地 json 文件中彻坛,同時將已保存的數(shù)據(jù)庫信息清空,提示更新鍵更新
5踏枣、刪除按鍵昌屉,點擊后,刪除已保存的數(shù)據(jù)庫框中選中的數(shù)據(jù)庫茵瀑,同時本地 json 文件會刪除相應的信息
6间驮、更新按鍵,點擊后马昨,獲取本地 json 文件數(shù)據(jù)竞帽,同時將數(shù)據(jù)更新到上方的框中
7、執(zhí)行信息欄偏陪,在程序報錯時抢呆,欄中將出現(xiàn)相對應的報錯信息
!由于是練手項目笛谦,基本功能不影響的情況存在未知BUG抱虐,以后在實際應用中修改BUG,再重構優(yōu)化
話不多說饥脑,根據(jù)實際開發(fā)過程中恳邀,下面依開發(fā)思路進行分解
1、先根據(jù)需求對GUI界面進行分解灶轰、排版
- 需要3個標簽對信息框介紹——3個Label
- 需要7個按鍵實現(xiàn)不同的功能——7個Button
- 需要3個框谣沸,分別用來輸入SQL語句、展示數(shù)據(jù)庫信息笋颤、展示操作中發(fā)生的必要信息——2個scrolledtext乳附、1個Listbox
我的布局圖
PS:實際可以更簡單内地,不需要分這么多塊,但也是第一開發(fā)赋除,經(jīng)驗不足阱缓,Frame可有可無,方便分塊举农,看實際情況
此時應有
from tkinter import *
from tkinter import scrolledtext
def main():
root = Tk()
# Frame
frmA1 = Frame()
frmA2 = Frame()
frmA3 = Frame()
frmB1 = Frame()
frmB2 = Frame()
frmB3 = Frame()
frmC = Frame()
# Label
label_1 = Label(frmA1)
label_2 = Label(frmB1)
label_3 = Label(frmC)
# scrolledtext
scrt_1 = scrolledtext.ScrolledText(frmA2)
scrt_2 = scrolledtext.ScrolledText(frmC)
# Listbox
scrollbar = Scrollbar(frmB2)
listbox = Listbox(frmB2)
# Button
button_1 = Button(frmA3)
button_2 = Button(frmA3)
button_3 = Button(frmA3)
button_4 = Button(frmB3)
button_5 = Button(frmB3)
button_6 = Button(frmB3)
button_7 = Button(frmC)
# 窗口布局
frmA1.grid()
frmA2.grid()
frmA3.grid()
frmB1.grid()
frmB2.grid()
frmB3.grid()
frmC.grid()
label_1.grid()
label_2.grid()
label_3.grid()
scrt_1.grid()
scrt_2.grid()
scrollbar.grid()
listbox.grid()
button_1.grid()
button_2.grid()
button_3.grid()
button_4.grid()
button_5.grid()
button_6.grid()
button_7.grid()
root.mainloop()
if __name__ == '__main__':
main()
將所有需要的控件寫出來后荆针,需要對各個控件進行重命名、屬性的添加颁糟,布局的調整航背,寬度和高度沒有設置好以及grid調用時,一般都會出現(xiàn)不整齊的現(xiàn)象棱貌,這些都要需要花時間去慢慢調整玖媚,下面是完成后的代碼
# 主界面
root = Tk()
root.title('數(shù)據(jù)庫批量處理器')
root.resizable(0, 0)
# Frame
frm_width = 380
frm_height1 = 200
frm_height2 = 50
frmA1 = Frame(width=frm_width, height=frm_height2-5)
frmA2 = Frame(width=frm_width, height=frm_height1)
frmA3 = Frame(width=frm_width, height=frm_height2)
frmB1 = Frame(width=frm_width, height=frm_height2-5)
frmB2 = Frame(width=frm_width, height=frm_height1)
frmB3 = Frame(width=frm_width, height=frm_height2)
frmC = Frame(width=frm_width*2, height=frm_height1+30)
# Label
infotext = '執(zhí)' + 18*' ' + '行' + 18*' ' + '信' + 18*' ' + '息' + 18*' ' + '欄'
input_label = Label(frmA1, text='SQL 語 句 輸 入 欄', width=54, height=2, anchor='center', relief='groove', foreground='red', bd=3)
db_label = Label(frmB1, text='已 保 存 的 數(shù) 據(jù) 庫', width=54, height=2, anchor='center', relief='groove', foreground='red', bd=3)
info_label = Label(frmC, text=infotext, width=90, height=2, foreground='red', relief='groove', bd=3)
# scrolledtext
input_text = scrolledtext.ScrolledText(frmA2, width=51, height=15, wrap=WORD)
info_text = scrolledtext.ScrolledText(frmC, width=105, height=13, wrap=WORD, state='normal')
# Listbox
scr = Scrollbar(frmB2, width=16)
db_list = Listbox(frmB2, width=51, height=11, yscrollcommand=scr.set)
scr.config(command=db_list.yview)
# Button
button_width = 16
button_height = 2
exe_button = Button(frmA3, text='執(zhí) 行', width=button_width, height=button_height, foreground='blue', command=exe_input, relief='groove')
clear1_button = Button(frmA3, text='語 句 清 屏', width=button_width, height=button_height, foreground='blue', command=clear_input1, relief='groove')
quit_button = Button(frmA3, text='退 出 程 序', width=button_width, height=button_height, foreground='blue', command=quitcommand, relief='groove')
add_button = Button(frmB3, text='添 加', width=button_width, height=button_height, foreground='blue', command=create_add_frame, relief='groove')
del_button = Button(frmB3, text='刪 除', width=button_width, height=button_height, foreground='blue', relief='groove', command=del_db)
update_button = Button(frmB3, text='更 新', width=button_width, height=button_height, foreground='blue', relief='groove', command=update_db_info)
clear2_button = Button(frmC, text='Info 清 屏', width=button_width, height=button_height, foreground='blue', command=clear_input2, relief='groove')
# 窗口布局
frmA1.grid(row=0, column=0)
frmA2.grid(row=1, column=0)
frmA3.grid(row=2, column=0)
frmB1.grid(row=0, column=1)
frmB2.grid(row=1, column=1)
frmB3.grid(row=2, column=1)
frmC.grid(row=3, column=0, columnspan=3)
input_label.grid(row=0, column=0, padx=1, pady=1)
db_label.grid(row=0, column=1, padx=1, pady=1)
info_label.grid(row=0, column=0)
input_text.grid(row=0, column=0)
info_text.grid(row=1, columnspan=2)
db_list.grid(row=0, column=0)
scr.grid(row=0, column=1, ipady=80)
exe_button.grid(row=0, column=0, ipadx=1, pady=1)
clear1_button.grid(row=0, column=1, padx=1, pady=1)
quit_button.grid(row=0, column=2, padx=1, pady=1)
add_button.grid(row=0, column=0, padx=1, pady=1)
del_button.grid(row=0, column=1, padx=1, pady=1)
update_button.grid(row=0, column=2, padx=1, pady=1)
clear2_button.grid(row=0, column=1, pady=1)
2、對各Button的功能實現(xiàn)
# 主窗口方法
# 獲取本地json數(shù)據(jù)
json_data = []
def get_json_data():
try:
with open('DB_data.json') as f:
for line in f:
json_data.append(json.loads(line))
return json_data
except:
info_text.insert(END, 'Error: 請檢查是否有添加的數(shù)據(jù)庫信息及文件婚脱!\n')
# 退出主界面
def quitcommand():
root.quit()
# 清空語句
def clear_input1():
input_text.delete('0.0', END)
# 清空信息
def clear_input2():
info_text.delete('0.0', END)
# 刪除listbox中選中的數(shù)據(jù)庫信息最盅,以及本地數(shù)據(jù)庫信息文件的更改
def del_db():
index = db_list.curselection()[0]
db_list.delete(index)
with open('DB_data.json', 'r') as f:
dataList = f.readlines()
del(dataList[index])
with open('DB_data.json', 'w') as f_w:
for line in dataList:
f_w.write(line)
# 獲取json文件進行信息的更新
def update_db_info():
get_json_data()
if not db_list.size():
for i in range(len(json_data)):
string = '第' + str(i+1) + '個數(shù)據(jù)庫' + ' ------ ' + 'DBname: ' + json_data[i]['DBname'] + ' ------ ' +'Port: ' + json_data[i]['port'] + '\n'
db_list.insert(END, string)
# 獲取json文件數(shù)據(jù)庫的信息,連接起惕,執(zhí)行語句,關閉連接
def exe_input():
get_json_data()
for data in json_data:
sql = input_text.get('0.0', END)
try:
conn = pymysql.connect(host=data['host'], user=data['user'], password=data['password'], port=int(data['port']), db=data['DBname'], charset=data['charset'])
cursor = conn.cursor()
cursor.execute(sql)
conn.commit()
info_text.insert(END, '操作完成')
except Exception as e:
conn.rollback()
error_info = ('發(fā)生錯誤:' + str(e) + '\n')
info_text.insert(END, error_info)
finally:
cursor.close()
conn.close()
這里我們需要了解Python操作數(shù)據(jù)庫的方法咏删,以及文件操作的知識惹想、json的基本使用,因此我們還要import相應的包
PS:
python 2.7 使用 MySQLdb 進行數(shù)據(jù)庫操作
python 3.x 使用 pymysql 進行數(shù)據(jù)庫操作
3督函、添加界面實現(xiàn)
# 創(chuàng)建添加二級窗口
def create_add_frame():
# 二級窗口方法
def cancelCommand():
add.destroy()
def confirmCommand():
data = {'host':host_entry.get(), 'user':user_entry.get(), 'password':ps_entry.get(), 'port':port_entry.get(), 'DBname':db_entry.get(), 'charset':charset_entry.get()}
count = 0
for v in data.values():
if v != '':
count += 1
if count == 6:
with open('DB_data.json', 'a', encoding='utf-8') as f:
json.dump(data, f)
f.writelines('\n')
messagebox.showinfo('已完成', '數(shù)據(jù)庫信息已添加嘀粱!保存的數(shù)據(jù)將清空,請點擊更新鍵更新辰狡!')
db_list.delete(0,END)
add.destroy()
else:
messagebox.showinfo('提示', '請將6個參數(shù)填寫完整锋叨!')
add = Toplevel()
add.title('添加新的數(shù)據(jù)庫')
add.resizable(0, 0)
add.wm_attributes('-topmost',1) # 將add窗口置于root前
# Label
label_width = 20
label_height = 1
host_label = Label(add, text='Host:', width=label_width, height=label_height)
user_label = Label(add, text='User:', width=label_width, height=label_height)
ps_label = Label(add, text='Password:', width=label_width, height=label_height)
port_label = Label(add, text='Port:', width=label_width, height=label_height)
db_label = Label(add, text='DBname:', width=label_width, height=label_height)
charset_label = Label(add, text='Charset:', width=label_width, height=label_height)
# Entry
entry_width = 30
host_entry = Entry(add, width=entry_width, bd=3)
user_entry = Entry(add, width=entry_width, bd=3)
ps_entry = Entry(add, width=entry_width, bd=3)
port_entry = Entry(add, width=entry_width, bd=3)
db_entry = Entry(add, width=entry_width, bd=3)
charset_entry = Entry(add, width=entry_width, bd=3)
host_entry.focus()
charset_entry.insert(END, 'utf8')
# Button
bt_width = 10
bt_height = 2
confirm_button = Button(add, text='確認', width=bt_width, height=bt_height, foreground='blue', command=confirmCommand)
cancel_button = Button(add, text='取消', width=bt_width, height=bt_height, foreground='blue', command=cancelCommand)
# 窗口布局
host_label.grid(row=0, column=0)
user_label.grid(row=1, column=0)
ps_label.grid(row=2, column=0)
port_label.grid(row=3, column=0)
db_label.grid(row=4, column=0)
charset_label.grid(row=5, column=0)
host_entry.grid(row=0, column=1)
user_entry.grid(row=1, column=1)
ps_entry.grid(row=2, column=1)
port_entry.grid(row=3, column=1)
db_entry.grid(row=4, column=1)
charset_entry.grid(row=5, column=1)
confirm_button.grid(row=6, column=0)
cancel_button.grid(row=6, column=1)
添加界面會出現(xiàn)messagebox的使用,需要import宛篇,各功能都測試過使用正常娃磺,出現(xiàn)的BUG基本不影響使用,編碼時間2天完成叫倍,以后如有時間偷卧,將對此工具進行改進
各部分代碼全都展示完整,如果對pymysql和json的基本操作不熟吆倦,可以繼續(xù)往下看听诸,對代碼中細節(jié)有疑議或有更好方法的、需要源碼練習的蚕泽,請留言晌梨,相信我們會交談甚歡 φ(≧ω≦*)
1、pymysql的基本使用
import pymysql
# 創(chuàng)建連接,除了port是int類型仔蝌,其他都是str類型
conn = pymysql.connect(host=localhost, user=root, password='123456', port=3306, db=spiders, charset='utf8')
# sql語句
sql = 'SELECT acctid, money FROM bank'
try:
# 創(chuàng)建游標
cursor = conn.cursor()
# 執(zhí)行sql語句
cursor.execute(sql)
# 對于增刪改操作泛领,需要此方法傳遞數(shù)據(jù)變化
conn.commit()
except:
# 如果執(zhí)行出錯,需要對數(shù)據(jù)回滾掌逛,回復原樣
conn.rollback()
finally:
# 關閉數(shù)據(jù)庫連接师逸,否則占用后臺資源
cursor.close()
conn.close()
2、json的基本用法
json雖然易用豆混,但由于第一次使用篓像,還是踩了不少坑
剛開始我是這樣保存數(shù)據(jù)的
with open('x.json', 'a', encoding='utf-8') as f:
json.dump(data, f, indent=4)
利用文本形式打開,是這樣
{
'a':'a',
'b':'b'
}
2個數(shù)據(jù)時
{
'a':'a',
'b':'b'
}{
'c':'c'.
'd':'d'
}
是不是看著很整齊? 這都是indent=4的功勞
取出數(shù)據(jù)
with open('x.json', 'r', encoding='utf-8') as f:
json.load(f)
你會發(fā)現(xiàn)皿伺,x.json中只有一個數(shù)據(jù)時员辩,運行良好,不會報錯鸵鸥,但存在多個時奠滑,就會出現(xiàn) extra data 的報錯
json進行文件操作有4個方法
1、dump()
2妒穴、dumps()
3宋税、load()
4、loads()
有什么區(qū)別嗎讼油?
1杰赛、dump() 將dict轉為str形式,并保存到文件中
2矮台、dumps() 將dict轉為str形式
3乏屯、load() 將str轉為dict形式,并從文件中取出
4瘦赫、loads() 將str轉為dict形式
使用dumps
a = {"123":"123"}
json.dumps(a) ——> '{"123":"123"}'
使用loads
b = '{"123":"123"}'
json.loads(b) ——> {"123":"123"}
所以當json文件存在多個數(shù)據(jù)時辰晕,它無法進行轉換,你可以簡單認為json.load()只能對只擁有一個數(shù)據(jù)的json文件進行處理确虱,那存在多個數(shù)據(jù)json文件就無法操作了含友?聰明的你已經(jīng)想到先對數(shù)據(jù)擇行分別取出,并分別進行轉換
但這樣的你要怎么分行處理呢蝉娜?
{
'a':'a',
'b':'b'
}{
'c':'c'.
'd':'d'
}
第一行就一個 {唱较?
這樣格式化的數(shù)據(jù)是因為我們設置了indent=4,如果我們沒有設置這樣的參數(shù)召川,實際的保存效果是這樣的:
{"host": "localhost", "user": "root", "password": "123456", "port": "3306", "DBname": "spiders", "charset": "utf8"}
因此南缓,當我們需要存取多個json數(shù)據(jù)時,一定不要設置indent參數(shù)荧呐,并且在存入時加入\n進行分行
所以最后的獲取函數(shù)應該這樣寫
data = []
with open('x.json') as f:
for line in f:
data.append(json.loads(line))
Thank You For Watching...
小知識:如果想運行時想不出現(xiàn)cmd窗口汉形,可以將文件名x.py改成x.pyw哦