折腾前引:学校任务图形化界面直接无脑tkiner,
自己想长期开发的python项目才用qt框架(pyside6库),
想处理大量同类数据及保证程序独立性再加数据库。

学习完入门python和用python写一个具有图形化界面及独立数据库连接的小型程序,其实还是有一段距离的,最典型的就是那个天天听老师、实验室前端方向学长说的学生管理系统。讲真我真不觉得这是什么难的任务,然而终于到了这玩意成作业这一天才发现上图形化界面要考虑的东西不少,程序结构也要相应大改,我把过程整理了一下放在这。

作业要求: 学生管理系统应包括学生基本信息,包括学号、姓名、性别、一门课的成绩。 1.学生基本信息录入、输出; 2.按学号查询学生信息; 3.按学号修改某学生信息并输出; 4.求所有人平均成绩; 5.删除某学生信息; 6.添加某学生信息; 7.输出平均分大于80分的同学信息; 8.根据成绩排名;

看起来确实简单——如果不用图形化的话只用python本身都能解决,一个小时就可以写完,但是毕竟是要图形化界面的,那就难很多了。

一开始我想用最美观最丰富基于qt的图形库pyside6解决,但做到一半发现,写起来是很爽,界面也能用pyside6自带的pydesigner做出来,但是后期繁冗的文件(一个用rcc编译好的py文件对应一个窗口界面)、信号槽和事件让我觉得大材小用了。 要学习pyside6的建议教程:点这里

主要是学校作业犯不上这么玩命

学长也劝我没必要,于是便转tkiner这个python自带的小型库。 国内的tkiner教程的严重滞后,也没有太多人用。 python Wiki提供了国外第三方较为完善的教程: Python GUI with Tkinter TkDocs Tutorial 第二个是官方教程但第一个很不错。

对这个程序我的基本设想是数据库操作与主程序界面分离,分为main.py负责图形化界面,logincheck.py负责登录数据库的核验,functionpack.py负责学生管理系统的数据库操作。

数据库用的是sqlite,毕竟不可能每个使用者都为了这个系统安装mysql。


界面

首先一定先写好界面,界面设计好了才知道数据库表怎么设计,怎么读取显示。

先实例化tk做出第一个主窗口,为了保证独立性,这个主窗口是登录窗口,登录窗口核验账号密码正确以后我们再做出第二个真正的主窗口:

# 创建独立登录窗口
window = tk.Tk()
# 窗口标题
window.title("学生管理系统登录界面")
# 窗口左上角图标
# window.iconbitmap = "icon.ico"
# 设置窗口大小
window.geometry("320x200")
# 初始化两个tkiner变量(类型实际是对象,可以接受输入框内的值,普通变量不可以接受)
username = tk.StringVar()
password = tk.StringVar()

# 创建一个文本框(label)
text = "用户名"
# tk.label内依次接收显示窗口,文本框内文字,字体外观,横宽边距,边框粗细
label_username = tk.Label(
    window,
    text=text,
    font=("宋体", 10, "bold"),
    padx=5,
    pady=5,
    borderwidth=1,
)
# 最后将组件用place方法放在窗口指定位置
label_username.place(x=30, y=40)

# 同上
text = "密码"
label_key = tk.Label(
    window,
    text=text,
    font=("宋体", 10, "bold"),
    padx=5,
    pady=5,
    borderwidth=1,
)
label_key.place(x=35, y=90)

# 创建输入框,ttk.Entry接收要显示的窗口,要对接输入框内容的tkiner变量
entryusername = ttk.Entry(window, textvariable=username)
entryusername.place(x=115, y=40)
entrypassword = ttk.Entry(window, textvariable=password, show="*")
entrypassword.place(x=115, y=90)
# 登录验证按钮触发方法函数,command后是点击按钮要触发的函数(函数不能带括号往内传值,不然不点按钮就自动触发,除非用lambda函数)
open_child_button = tk.Button(window, text="登录", command=login)
open_child_button.place(x=260, y=140)
# 通过实例化tk创建的主窗口必须mainloop()来监听窗口,保证窗口持续存在
window.mainloop()

上述只是登录窗口的界面,你会发现open_child_button调用了一个login函数,它负责将输入框内的值传给logincheck.py来验证账号密码并根据结果继续:

def login(username1="none", password1="none"):
    # 注意调用tkiner变量时要用get方法,不然只会得到一个对象
    username1 = str(username.get())
    password1 = str(password.get())
    # 调用logincheck.py的方法传入值,看结果True还是False
    if checkUserKey.checking(username1, password1):
        txt = "正在加载"
        # 提示窗口,传入同label文本框
        messagebox.showinfo(title="登录成功", message=txt)
        # 登录成功时摧毁登录窗口
        window.destroy()
        # 生成第二个真正的主窗口
        open_child_window()
    else:
        txt = "账号密码有误"
        messagebox.showinfo(title="登录失败", message=txt)

重头戏(烂活)来了,主界面我的设计是显示学生详细信息的表格,上方显示Menu目录提供操作数据功能(插删改查等)。表格就用二维的ttk.Treeview实现:

# 打开第二个主窗口
def open_child_window():
    # global必须放在函数开头
    global child_window
    # 调用functionpack.py内函数把数据库内学生信息全部以嵌套列表形式抽出来
    Datafile = delivery.listdisplay()
    # 创建新的tk窗口对象实例
    child_window = tk.Tk()
    child_window.geometry("840x480")
    child_window.title("学生管理系统")
    # child_window.iconbitmap("icon.ico")

    # 菜单部分,mainmenu是最顶级的目录类,实际不显示在界面
    mainmenu = tk.Menu(child_window)

    # 三个大目录是直接显示在界面上的,传入挂靠的顶级目录类,是否能
    filemenu = tk.Menu(mainmenu, tearoff=False)
    editmenu = tk.Menu(mainmenu, tearoff=False)
    aboutmenu = tk.Menu(mainmenu, tearoff=False)
    # 往第一个大目录里添加子目录,传入显示文字及点击后要执行的函数
    filemenu.add_command(label="导入", command=importing)
    filemenu.add_command(label="导出", command=exporting)

    editmenu.add_command(label="插入", command=InsertNewKey)
    editmenu.add_command(label="删除", command=DeleteKey)
    editmenu.add_command(label="更新", command=UpdateKey)
    editmenu.add_command(label="查找", command=CheckKey)
    # 大目录里的分割线,方便用户区分操作
    editmenu.add_separator()
    editmenu.add_command(label="求平均", command=average)
    editmenu.add_command(label="取优", command=Excellence)

    aboutmenu.add_command(label="关于作者", command=authorraise)
    
    # 把大目录依次显示出来,传入大目录显示名,及大目录变量名
    mainmenu.add_cascade(label="文件", menu=filemenu)
    mainmenu.add_cascade(label="操作", menu=editmenu)
    mainmenu.add_cascade(label="关于", menu=aboutmenu)
    child_window.config(menu=mainmenu)

    # 窗口数据显示,columns决定表格列名
    columns = ("ID", "Name", "Mark", "Gender")
    # 实例化ttk.Treeview创建二维tree来充当表格,传入要显示的窗口,列名,每列高度,显示的元素,显示的列
    tree = ttk.Treeview(
        child_window, columns=columns, height=20, show="headings", displaycolumns="#all"
    )
    
    # 按索引显示元素
    tree.heading("#0", text="")
    tree.heading("#1", text="ID")
    tree.heading("#2", text="姓名")
    tree.heading("#3", text="成绩")
    tree.heading("#4", text="性别")
    
    # 将刚才从数据库内得到的学生信息分出来读并插入表格
    for index, data in enumerate(Datafile):
        tree.insert("", tk.END, text=index, value=data)
    # 打包显示表格
    tree.pack()

    child_window.mainloop()