問題描述:時間過得真快,一眨眼又一個月過去,2022又過去大半,7月的尾巴,終於稍微做出來點 東西,本人也不是開發,也是在不斷學習的一枚小白。這次使用tkinter制作了一個mysql的巡檢工具,使用圖形化操作,邊學邊操作,一路踩坑,寫的不好,但是能交出來一個東西,學習的過程中加深了對class的理解,學習了tkinter布局,如何連接數據庫等等。
python:Python 3.8.1
數據庫對象:MySQL
過程中遇到的問題
1.因為做的是數據庫巡檢系統,所以登錄的賬號往往是權限較大的比如root,然後如何去校驗root賬號,跟正常的管理系統不一樣,不是創建好用戶和密碼表,然後去比對。root是存放在數據庫系統中的,沒辦法去校驗root的用戶和密碼。這裡的root是作為參數傳進來的,所以如何去比對權限較大的用戶呢?
2.顯示頁面如何去加載數據庫中的查詢結果呢?按理說應該跟簡單,取得數據庫查詢的返回值,然後加載在頁面中。但最後這個顯示結果還不是很理想
3.數據庫登錄的時候如何返回mysql連接給的報錯值,如果只設置判斷是否登錄成功失敗很簡單,但是在返回mysql報錯值的時候如何返回給前台呢
登錄類
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")
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()
顯示關於部分類
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版本的就花費了兩周時間了,不過這個大體結構成型,後邊如果想做應該會簡單些。這次更加深刻了解了面向對象,上學沒學好,畢業了都得補回來
Im still studying hard Python