程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

python:GUI圖形化數據庫巡檢工具

編輯:Python

問題描述:時間過得真快,一眨眼又一個月過去,2022又過去大半,7月的尾巴,終於稍微做出來點 東西,本人也不是開發,也是在不斷學習的一枚小白。這次使用tkinter制作了一個mysql的巡檢工具,使用圖形化操作,邊學邊操作,一路踩坑,寫的不好,但是能交出來一個東西,學習的過程中加深了對class的理解,學習了tkinter布局,如何連接數據庫等等。

 

python:Python 3.8.1

數據庫對象:MySQL

 

一、實現效果

 

 

二、問題解決

過程中遇到的問題

1.因為做的是數據庫巡檢系統,所以登錄的賬號往往是權限較大的比如root,然後如何去校驗root賬號,跟正常的管理系統不一樣,不是創建好用戶和密碼表,然後去比對。root是存放在數據庫系統中的,沒辦法去校驗root的用戶和密碼。這裡的root是作為參數傳進來的,所以如何去比對權限較大的用戶呢?

 

 

 

 

 

 

2.顯示頁面如何去加載數據庫中的查詢結果呢?按理說應該跟簡單,取得數據庫查詢的返回值,然後加載在頁面中。但最後這個顯示結果還不是很理想

 

 

3.數據庫登錄的時候如何返回mysql連接給的報錯值,如果只設置判斷是否登錄成功失敗很簡單,但是在返回mysql報錯值的時候如何返回給前台呢

 

 

 

 

 

 三、代碼部分

 

 

LoginMain.py

登錄類

class Application(Frame):
username = ''
password = ''
ip = ''
port = ''
def __init__(self,master=None):
super().__init__(master)
self.master = master
self.pack()
self.createWidget()
def createWidget(self): #創建組件容器
page = Frame(self)
page.pack()
self.usernameGet = StringVar()
self.usernameGet.set('root')
self.passwordGet = StringVar()
self.passwordGet.set('zabbix.9.31')
self.ipGet = StringVar()
self.ipGet.set('192.168.163.21')
self.portGet = StringVar() #這裡如果設置IntVar,GUI上會顯示0,整數可以為0,不能是空
self.portGet.set('33306')
Label(page).grid(row=0,column=0)
Label(page,text='賬戶:').grid(row=1,column=1)
Entry(page,textvariable=self.usernameGet).grid(row=1,column=2)
Label(page,text='密碼:').grid(row=2,column=1)
Entry(page,textvariable=self.passwordGet,show='*').grid(row=2,column=2)
Label(page,text='IP地址:').grid(row=3,column=1)
Entry(page,textvariable=self.ipGet).grid(row=3,column=2)
Label(page,text='端口:').grid(row=4,column=1)
Entry(page,textvariable=self.portGet).grid(row=4,column=2)
Button(page,text='登錄',command=self.login_check).grid(row=5,column=1,pady=10)
Button(page,text='退出',command=page.quit).grid(row=5,column=2)
def login_check(self):
#設置登錄退出按鈕
i = self.ipGet.get()
p = int(self.portGet.get())
u = self.usernameGet.get()
pa = self.passwordGet.get()
db_name = 'mysql'
#實例化Mysql類
mysqlLogin = Mysql(i,p,u,pa,db_name) #這裡不能用mysqllogin對象做布爾值判斷,只要輸入正確,他的布爾值一直是true
results = mysqlLogin.select('select @@version')
print(bool(mysqlLogin));print(bool(results))
if results:
messagebox.showinfo('提示', '連接成功,正在為您生成巡檢報告')
# print(type(i),type(p),type(u),type(pa))
self.destroy()
MainPage(root)
mysqlLogin.show()

數據庫連接類

class Mysql(object):
# mysql 端口號,注意:必須是int類型
def __init__(self, host, port, user, passwd, db_name):
self.host = host
self.user = user
self.passwd = passwd
self.port = port
self.db_name = db_name
def select(self, sql):
"""
 執行sql命令
:param sql: sql語句
:return: 元祖
"""
 try:
conn = pymysql.connect(
host=self.host,
user=self.user,
passwd=self.passwd,
port=self.port,
database=self.db_name,
charset='utf8',
#cursorclass=pymysql.cursors.DictCursor
)
cur = conn.cursor() # 創建游標
# conn.cursor()
cur.execute(sql) # 執行sql命令
#print(type(cur.execute(sql)))
res = cur.fetchall() # 獲取執行的返回結果
#print(type(res))
cur.close()
conn.close()
# print(res)
return res
except Exception as e:
messagebox.showerror('提示',e)
return False
def show(self):
sql1 = "show global variables"
# sql2 = "show master status;"
# sql3 = "SELECT table_schema,SUM((AVG_ROW_LENGTH*TABLE_ROWS+INDEX_LENGTH))/1024 AS total_KB FROM information_schema.TABLES GROUP BY table_schema ORDER BY total_KB DESC ;"
res1 = dict(self.select(sql1))
# res2 = self.select(sql2)
# res3 = self.select(sql3)
filename = r"D:\{}".format('mysql_check.txt')
with open(filename, mode='w', encoding='utf-8') as f:
# 檢查MySQL版本
#print("\033[1;32m當前數據庫的版本是:\033[0m" + res1['version'])
f.write("當前數據庫的版本是:" + res1['version'] + "\n")
f.write("當前數據庫的version_comment是:" + res1['version_comment'] + "\n")
f.write("當前數據庫的version_compile_machine是:" + res1['version_compile_machine'] + "\n")
f.write("當前數據庫的version_compile_os是:" + res1['version_compile_os'] + "\n")
f.write("當前數據庫的version_compile_zlib是:" + res1['version_compile_zlib'] + "\n")
f.write("當前數據庫的sql_mode是:" + res1['sql_mode'] + "\n")
# 檢查MySQL端口
#print("\033[1;35m當前數據庫的端口是:\033[0m" + res1['port'])
f.write("當前數據庫的端口是:" + res1['port'] + "\n")
# 檢查server_id
#print("\033[1;32m當前數據庫的server_id是:\033[0m" + res1['server_id'])
f.write("當前數據庫的server_id是:" + res1['server_id'] + "\n")
# 檢查basedir目錄
#print("\033[1;32m當前數據庫的basedir在:\033[0m" + res1['basedir'])
f.write("當前數據庫的basedir在:" + res1['basedir'] + "\n")
# 檢查datadir目錄
#print("\033[1;35m當前數據庫的datadir在:\033[0m" + res1['datadir'])
f.write("當前數據庫的datadir在:" + res1['datadir'] + "\n")
# 檢查tmpdir目錄
#print("\033[1;32m當前數據庫的tmpdir在:\033[0m" + res1['tmpdir'])
f.write("當前數據庫的tmpdir在:" + res1['tmpdir'] + "\n")
#pid_file
f.write("當前數據庫的pid_file在:" + res1['pid_file'] + "\n")
#optimizer_switch
f.write("當前數據庫的optimizer_switch:" + res1['optimizer_switch'] + "\n")
#mysqlx_socket
f.write("mysqlx_socket:" + res1['mysqlx_socket'] + "\n")
#log_bin_basename
f.write("當前數據庫的log_bin_basename在:" + res1['log_bin_basename'] + "\n")
#log_error
f.write("當前數據庫的log_error在:" + res1['log_error'] + "\n")
#slow_query_log_file
f.write("當前數據庫的slow_query_log_file在:" + res1['slow_query_log_file'] + "\n")

MainPage.py

class MainPage:
def __init__(self,master: tk.Tk):
self.root = master
self.root.title('數據庫巡檢系統')
self.root.geometry('600x400')
self.create_page()
def create_page(self):
self.about_frame = AboutFrame(self.root) #調用views中的aboutframe類,顯示關於的信息
# tk.Label(self.about_frame,text = '關於作品:數據庫巡檢系統').pack()
# tk.Label(self.about_frame,text = '關於作者:我愛睡蓮').pack()
# tk.Label(self.about_frame,text = '版權所有:https://www.cnblogs.com/houzhiheng/').pack()
self.check_frame = CheckFrame(self.root)
menubar = tk.Menu(self.root)
menubar.add_command(label='巡檢結果',command=self.show_check)
menubar.add_command(label='關於',command=self.show_about)
self.root['menu'] = menubar
def show_check(self):
self.check_frame.pack()
self.about_frame.pack_forget() #選擇性遺忘其他加載過的頁面,要不然都會加載在頁面當中
def show_about(self):
self.about_frame.pack()
self.check_frame.pack_forget()

views.py

顯示關於部分類

class AboutFrame(tk.Frame):
def __init__(self,root):
super().__init__(root)
tk.Label(self, text='Production:數據庫巡檢系統').pack()
tk.Label(self, text='Author:我愛睡蓮').pack()
tk.Label(self, text='Version:1.0').pack()
tk.Label(self, text='@Copyright:https://www.cnblogs.com/houzhiheng/').pack()

顯示數據庫巡檢結果類

class CheckFrame(tk.Frame):
def __init__(self,root):
super().__init__(root)
# tk.Label(self, text='巡檢結果').pack()
self.table_view = tk.Frame()
self.table_view.pack()
self.create_page()
tk.Button(self,text='保存文件',command=self.save_data_frame).pack(anchor=tk.E,pady=5)
def create_page(self):
# self.tree_view = ttk.Treeview(self,show='headings')
# columns = ("check_results")
# columns_values = ("數據庫巡檢報告")
# top = Tk() # 設置窗口
# sb = Scrollbar(top) # 設置窗口滾動條
# sb.pack(side=RIGHT, fill=) # 設置窗口滾動條位置
# self.sb = Scrollbar()
# self.sb.pack(side=RIGHT,fill= Y)
# self.tree_view = ttk.Treeview(self,show='headings',columns=columns)
# self.tree_view.column('check_results',width=500,anchor='center')
# self.tree_view.heading('check_results',text=columns_values)
# self.tree_view.pack(fill=tk.BOTH,expand=True)
# self.show_data_frame()
with open(r'D:\mysql_check.txt', 'r', encoding='utf-8') as f:
lines2 = [l.split() for l in f.readlines() if l.strip()]
# 滾動條初始化(scrollBar為垂直滾動條,scrollBarx為水平滾動條)
scrollBar = Scrollbar(self)
scrollBarx = Scrollbar(self, orient=HORIZONTAL)
# 靠右,充滿Y軸
scrollBar.pack(side=RIGHT, fill=Y)
# 靠下,充滿X軸
scrollBarx.pack(side=BOTTOM, fill=X)
lb = Text(self, width=100, height=25,)
lb.pack()
# db = Mysql('192.168.163.21', 33306, 'root', 'zabbix.9.31', 'mysql')
# res = db.show()
textvar = "1:{} \n2:{}\n3:{}\n4:{}\n5:{}\n6:{}\n7:{}\n8:{}\n9:{}\n10:{}\n11:{}\n12:{}\n13:{}\n14:{}\n15:{}"\
.format(lines2[0],lines2[1],lines2[2],lines2[3],lines2[4],lines2[5],lines2[6],lines2[7],lines2[8],lines2[9],lines2[10],lines2[11],lines2[12],lines2[13],lines2[14],lines2[15],lines2[16])
lb.insert('insert', textvar + '\n')
lb.update()
# 而當用戶操縱滾動條的時候,自動調用 Treeview 組件的 yview()與xview() 方法
# 即滾動條與頁面內容的位置同步
scrollBar.config(command=lb.yview)
scrollBarx.config(command=lb.xview)
def show_data_frame(self): #把查詢的內容輸出到屏幕上來
pass
def save_data_frame(self):
messagebox.showinfo('提示','您的文件已保存在D:\mysql_check.txt中!')

 

四、結論與收獲

問題處理:

1.如何去對比root登錄是否成功呢,從上面代碼可以看到,我沒法去直接登錄數據庫去校驗用戶名和密碼,但是我可以讓用戶名登錄成功去執行命令來判斷是否登錄成功,如果root登陸並且成功執行命令,我就返回一個結果為TRUE,如果執行失敗,就返回一個結果為FALSE,這條命令是去查詢數據庫自身版本得到,任何用戶都可以執行

2.在屏幕上輸出巡檢的結果,本來取得數據庫返回值然後給屏幕上就可以了,但是我的views.py調用class MySQL類失敗,所以最後直接把查詢結果保存在了文件中,前台輸出的時候打開文件,然後調整文件格式就輸出了,敗筆啊這一步

3.將mysql連接返回的報錯做成異常處理即可,只不過當時做的時候邏輯有一點問題一直沒顯示成功

 

結論收獲:

本來是想做一個全套的數據庫巡檢GUI系統,桌面版本的已經做好了,但是沒有到光做一個GUI+mysql版本的就花費了兩周時間了,不過這個大體結構成型,後邊如果想做應該會簡單些。這次更加深刻了解了面向對象,上學沒學好,畢業了都得補回來

 


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved