• python:GUI图形化数据库巡检工具


    问题描述:时间过得真快,一眨眼又一个月过去,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版本的就花费了两周时间了,不过这个大体结构成型,后边如果想做应该会简单些。这次更加深刻了解了面向对象,上学没学好,毕业了都得补回来

     

  • 相关阅读:
    如何在不修改原始数组的情况下反转数组?
    二维卷积输出特征图的计算公式
    航天联志Aisino-AISINO26081R服务器通过调BIOS用U盘重新做系统(windows系统通用)
    23【状态设计模式】
    【C++笔记】C++三大特性之多态的概念、定义及使用
    C++ primer 查漏补缺九:第六章 函数
    STM8应用笔记1.基本程序与启动代码分析
    【示波器专题】示波器一些自动测量项说明
    如何同步微信通讯录?看这里,学习如何快速添加联系人!
    OC-消息转发
  • 原文地址:https://www.cnblogs.com/houzhiheng/p/16536720.html