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

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

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

开发环境如下:

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

先来看看项目完成的界面

主界面

Python | GUI小项目_数据库批量处理器1.0_第1张图片
主界面.PNG

添加键界面

Python | GUI小项目_数据库批量处理器1.0_第2张图片
添加界面.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

我的布局图

Python | GUI小项目_数据库批量处理器1.0_第3张图片
布局.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)