美文网首页
Python | GUI小项目_数据库批量处理器1.0

Python | GUI小项目_数据库批量处理器1.0

作者: Ghibli_Someday | 来源:发表于2018-08-18 01:37 被阅读84次

    平时的 Python 学习过程比较枯燥,想要保持兴趣和提高,就得上手亲自做一些小项目
    本次小项目是利用 Python 自带的 tkinter 模块构造简单的 GUI,利用PyMySQL 对数据库进行操作 ,数据存储利用 json模块,这些都是比较基础的东西,做的过程可以逐渐熟悉上手

    此工具可应用于多个数据库存在相同的表名等情况下,进行批量操作

    开发环境如下:

    • Python 3.6.6
    • MySQL 8.0
    • PyMySQL 0.9.2 (pip即可)
    • IDE:Geany

    先来看看项目完成的界面

    主界面

    主界面.PNG

    添加键界面

    添加界面.PNG

    功能说明
    1、左上方是 SQL 语句输入栏,输入语句后点执行按键,执行按键会从本地存储的 json 文件获取数据库信息,连接数据库,然后执行语句,再关闭数据库
    2、主界面有 2 个清屏按键,分别为语句清屏和 Info 清屏;语句清屏,顾名思义是将 SQL 语句清除, Info 清屏是将下方的执行信息栏的报错清空
    3、退出程序按键,退出该程序
    4、添加按键,点击后弹出添加界面,有 6 个输入栏,其中一个带有默认值, 6 栏全部输入信息才能点击确认,否则出现提示框提示将信息填写完整,取消即退出该界面;点击确认后,新的数据库信息将保存到本地 json 文件中,同时将已保存的数据库信息清空,提示更新键更新
    5、删除按键,点击后,删除已保存的数据库框中选中的数据库,同时本地 json 文件会删除相应的信息
    6、更新按键,点击后,获取本地 json 文件数据,同时将数据更新到上方的框中
    7、执行信息栏,在程序报错时,栏中将出现相对应的报错信息

    !由于是练手项目,基本功能不影响的情况存在未知BUG,以后在实际应用中修改BUG,再重构优化

    话不多说,根据实际开发过程中,下面依开发思路进行分解

    1、先根据需求对GUI界面进行分解、排版

    • 需要3个标签对信息框介绍——3个Label
    • 需要7个按键实现不同的功能——7个Button
    • 需要3个框,分别用来输入SQL语句、展示数据库信息、展示操作中发生的必要信息——2个scrolledtext、1个Listbox

    我的布局图

    布局.png
    PS:实际可以更简单,不需要分这么多块,但也是第一开发,经验不足,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调用时,一般都会出现不整齐的现象,这些都要需要花时间去慢慢调整,下面是完成后的代码

        # 主界面
        root = Tk()
        root.title('数据库批量处理器')
        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 = '执' + 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='已   保   存   的   数   据   库', 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='执 行', 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的功能实现

        # 主窗口方法
        # 获取本地json数据
        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: 请检查是否有添加的数据库信息及文件!\n')
        
        # 退出主界面
        def quitcommand():
            root.quit()
            
        # 清空语句  
        def clear_input1():
            input_text.delete('0.0', END)
        
        # 清空信息
        def clear_input2():
            info_text.delete('0.0', END)
        
        # 删除listbox中选中的数据库信息,以及本地数据库信息文件的更改
        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) + '个数据库' + '  ------  ' + 'DBname:  ' + json_data[i]['DBname'] + '  ------  ' +'Port:  ' + json_data[i]['port'] + '\n'
                    db_list.insert(END, string)
            
        # 获取json文件数据库的信息,连接,执行语句,关闭连接
        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 = ('发生错误:' + str(e) + '\n')
                    info_text.insert(END, error_info)
                finally:
                    cursor.close()
                    conn.close()
    

    这里我们需要了解Python操作数据库的方法,以及文件操作的知识、json的基本使用,因此我们还要import相应的包
    PS:
    python 2.7 使用 MySQLdb 进行数据库操作
    python 3.x 使用 pymysql 进行数据库操作

    3、添加界面实现

        # 创建添加二级窗口
        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('已完成', '数据库信息已添加!保存的数据将清空,请点击更新键更新!')
                        db_list.delete(0,END)
                        add.destroy()
                else:
                    messagebox.showinfo('提示', '请将6个参数填写完整!')
            
            add = Toplevel() 
            add.title('添加新的数据库')
            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)
            
    

    添加界面会出现messagebox的使用,需要import,各功能都测试过使用正常,出现的BUG基本不影响使用,编码时间2天完成,以后如有时间,将对此工具进行改进

    各部分代码全都展示完整,如果对pymysql和json的基本操作不熟,可以继续往下看,对代码中细节有疑议或有更好方法的、需要源码练习的,请留言,相信我们会交谈甚欢 φ(≧ω≦*)

    1、pymysql的基本使用

    import pymysql
    
    # 创建连接,除了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:
        # 创建游标
        cursor = conn.cursor()
    
        # 执行sql语句
        cursor.execute(sql)
    
        # 对于增删改操作,需要此方法传递数据变化
        conn.commit()
    except:
        # 如果执行出错,需要对数据回滚,回复原样
        conn.rollback()
    finally:
        # 关闭数据库连接,否则占用后台资源
        cursor.close()
        conn.close()
    

    2、json的基本用法

    json虽然易用,但由于第一次使用,还是踩了不少坑

    刚开始我是这样保存数据的

    with open('x.json', 'a', encoding='utf-8') as f:
            json.dump(data, f, indent=4)
    

    利用文本形式打开,是这样

    {
        'a':'a',
        'b':'b'
    }
    

    2个数据时

    {
        'a':'a',
        'b':'b'
    }{
        'c':'c'.
        'd':'d'
    }
    

    是不是看着很整齐? 这都是indent=4的功劳

    取出数据

    with open('x.json', 'r', encoding='utf-8') as f:
        json.load(f)
    

    你会发现,x.json中只有一个数据时,运行良好,不会报错,但存在多个时,就会出现 extra data 的报错

    json进行文件操作有4个方法
    1、dump()
    2、dumps()
    3、load()
    4、loads()

    有什么区别吗?
    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文件存在多个数据时,它无法进行转换,你可以简单认为json.load()只能对只拥有一个数据的json文件进行处理,那存在多个数据json文件就无法操作了?聪明的你已经想到先对数据择行分别取出,并分别进行转换

    但这样的你要怎么分行处理呢?

    {
        'a':'a',
        'b':'b'
    }{
        'c':'c'.
        'd':'d'
    }
    

    第一行就一个

    这样格式化的数据是因为我们设置了indent=4,如果我们没有设置这样的参数,实际的保存效果是这样的:

    {"host": "localhost", "user": "root", "password": "123456", "port": "3306", "DBname": "spiders", "charset": "utf8"}
    

    因此,当我们需要存取多个json数据时,一定不要设置indent参数,并且在存入时加入\n进行分行

    所以最后的获取函数应该这样写

    data = []
    with open('x.json') as f:
        for line in f:
            data.append(json.loads(line))
    

    Thank You For Watching...

    小知识:如果想运行时想不出现cmd窗口,可以将文件名x.py改成x.pyw哦

    相关文章

      网友评论

          本文标题:Python | GUI小项目_数据库批量处理器1.0

          本文链接:https://www.haomeiwen.com/subject/pyxtiftx.html