用tkinter+sqlite3做了一个出勤记录小工具

在这里插入图片描述
在这里插入图片描述

pack()

  1. pack()这个布局方式非常不友好,最后菜菜用了11个Frame互相嵌套 ↑

  2. 写了很多罗里吧嗦的字符串操作语句来保证这些输入框不会报错:

     string.replace('需要替换的字符','new')
     string.strip('需要删去的首尾字符')
     string.split('分隔符',分割次数)
     string.isdigit() 判断输入的是否是纯数字
     list=[str(i) for i in list0] 先把列表list0每一项都变为字符串
     ''.join(list) 再把列表拼接为字符串
    
  3. 可以实时更新显示的Listbox和Label

     x=tkinter.StringVar()
     tkinter.Listbox(self,listvariable=x).pack()
     x.set('更新的字符串')
    
  4. 获取、清除输入框Entry的内容:

     entry.get()   或者在类外调用时须加名字前缀: app.entry.get()
     entry.delete(0,END)   或者在类外调用时须加名字前缀: app.entry.delete(0,END) 
    
  5. 注意None和空字符并不等价~;

     '+'.split('+') 的结果是 ['', '']
    
  6. cursor.fetchone()和fetchall()返回的都是列表,列表中是元组,形如[(1, ‘a’, 13, 13), (2, ‘c’, 4, 4), (3, ‘a’, 2, 2)],如果没有查询到结果则会返回None;

  7. 复习了很多已经快忘光的sql语句:

     group by, having count(*)>2, update, delete, insert or ignore, inner join
    
  8. 菜菜第一次写了400行的python……第一次用了global变量…还挺好用的… https://www.cnblogs.com/z360519549/p/5172020.html
    这两篇…↑…↓…也不晓得谁是原作者了……
    https://blog.csdn.net/qw_sunny/article/details/80972357

全部代码 ↓

import tkinter as tk
from tkinter.constants import * #这一行是为了方便pack()中的特殊字符输入,比如TOP,RIGHT,LEFT
import sqlite3

class Application(tk.Frame):  # 继承自父类Frame
    def __init__(self, master=None):  # 初始化,  master: 按钮的父容器    
        super().__init__(master)# 重写父类的初始化 https://blog.csdn.net/m0_37310097/article/details/75305015
        self.master = master
        self.pack()
        self.create_widgets()

    def create_widgets(self):
        #所有带格式条件的输入,最好增加一个报错机制
        #所有带格式条件的输入,最好增加一个报错机制
        #所有带格式条件的输入,最好增加一个报错机制 try,except
        f1=tk.Frame(self)
        f1.pack()
        f2=tk.Frame(f1)
        f2.pack(side=LEFT)
        self.label1=tk.Label(f2,text='请输入课程ID(+课程内容):') #可引入时间,比如1a、1b、1c表示第一节课的上午班、下午班、晚班
                                                               # 允许不输入课程内容
        self.label1.pack(anchor=W)
        self.course = tk.Entry(f2)
        self.course.pack(anchor=W)
        tk.Message(f2,text='↑可单独录入课程信息',fg='#808080',width=250).pack(anchor=W)
        
        self.label2=tk.Label(f2,text='请输入出席学生ID:空格相隔') #在屏幕上显示学生名单(ID-名字),方便老师输入ID而非名字
                                                                            #如果有第一次来试听的学生,请先在add输入框内为他分配ID和1个课时
                                                                            #允许不输入出席学生ID,这样的话就只更新课程信息
                                                                            #如果要删除某个学生的某次出勤记录,则输入负的学生ID,但每次只能输入一个 
        self.label2.pack(anchor=W)
        self.attendee = tk.Entry(f2)
        self.attendee.pack(anchor=W)
        
        
        f3=tk.Frame(f1)
        f3.pack(side=LEFT,padx=20)
        self.record=tk.Button(f3,text='  记录  ',command=record_db)
        self.record.pack(side=LEFT)

        f4=tk.Frame(self)
        f4.pack()
        tk.Message(f4,text='*如果要删除某个学生某次出勤记录,则输入负的学生ID,但每次只能输入一个ID,eg.-1',fg='#808080',width=250).pack(anchor=W)
        
        f5=tk.Frame(self)
        f5.pack(pady=20,ipadx=50)
        f8=tk.Frame(f5)
        f8.pack(side=BOTTOM,anchor=E)
        f6=tk.Frame(f5)
        f6.pack(side=LEFT)
        self.label3=tk.Label(f6,text='请输入某个学生的ID:') #在屏幕上显示学生名单(ID-名字),方便老师输入ID而非名字                                       
        self.label3.pack()
        self.astudent=tk.Entry(f6)
        self.astudent.pack()
        
        self.consultStu = tk.Button(f6)
        self.consultStu["text"] = "  查询学生出勤记录  "
        self.consultStu["command"] = consultStu
        self.consultStu.pack(pady=5)

        f7=tk.Frame(f5)
        f7.pack(side=RIGHT)
        self.label4=tk.Label(f7,text='请输入某节课程的ID:') #在屏幕上显示课程列表(ID-内容),方便老师输入ID
        self.label4.pack()
        self.acourse=tk.Entry(f7)
        self.acourse.pack()

        self.consultCou = tk.Button(f7,text="  查询课程出勤记录  ", command=consultCou)
        self.consultCou.pack(pady=5)

        global tip
        tip=tk.StringVar()
        giveTip()
        self.label5=tk.Label(f8,textvariable=tip,fg='#808080')
        self.label5.pack(anchor=E)

        f9=tk.Frame(self)
        f9.pack(ipadx=30)
        f10=tk.Frame(f9)
        f10.pack(side=LEFT)
        tk.Message(f10,text='学生名单').pack()
        global listOFstudent2
        listOFstudent2=tk.StringVar()
        #listOFstudent2.set('') #为了让list一开始就出现 所以调用showListStu()
        showListStu()
        self.list1=tk.Listbox(f10,listvariable=listOFstudent2,height=15,width=18)   
        self.list1.pack()
        
        tk.Message(f9,text='课程列表').pack()
        global listOFcourse
        listOFcourse=tk.StringVar()
        #listOFcourse.set('') #为了让list一开始就出现 所以调用showListCou()
        showListCou()
        self.list2=tk.Listbox(f9,listvariable=listOFcourse,height=15,width=18)
        self.list2.pack()        

        f11=tk.Frame(self)
        f11.pack(anchor=W)
        self.labeladd=tk.Label(f11,text="请输入学生ID+(名字)+(增加课时数):") #允许不输名字,比如对于已经存在的同学可以直接输入“1++10”
                                                                        #如果输错名字,则再次输入“id+名字+0”
        self.labeladd.pack()
        self.add=tk.Entry(f11)
        self.add.pack() #注意:若使用bind,则布局必须写在bind后无法会报AttributeError错

        self.update=tk.Button(f11,text='  更新  ',command=addcredits) #如果command=addcredits(),则即使不点击按钮,函数addcredits()也会被执行一次
        self.update.pack(pady=5)


        self.quit = tk.Button(self, text="QUIT", fg="red",
                              command=self.master.destroy)
        self.quit.pack(pady=20)


def record_db():
    txt=app.course.get()
    if txt=='':
        return # 确保课程信息有输入
    
    attend=app.attendee.get() #允许attendee为空(只为了更新list2)

    x=txt.strip(' ').split('+',1)
    if len(x)==1:
        courseID=x[0]
        content=None
    else:
        courseID=x[0]
        content=x[1]
    
    db=connect_db()
    cursor=db.cursor()
    #更新表Course ↓
    sql0='''select * from Course where id_course='%s' '''% courseID
    cursor.execute(sql0)
    res=cursor.fetchone()
    if res==None:
        sql1='''insert into Course values ('%s','%s')'''%(courseID,content)
    else:
        if content==None or content=='':
            sql1=''' '''
        else:
            sql1='''update Course set course='%s' where id_course='%s' '''%(content,courseID)
    cursor.execute(sql1)
    #更新表Attendance、Student ↓
    if attend.replace(' ','').isdigit():#确保输入的学生ID都是数字
        attend2=attend.strip(' ') # 确保首尾没有空格
        attendlist=attend2.split(' ')
        for i in attendlist:
            stuID=int(i)
            sql1='select * from Student where id_stu=%d'% stuID
            cursor.execute(sql1)
            res=cursor.fetchone()
            if res != None: #更新表Attendance之前一定要确保输入的学生ID有效!!!                     
                try:
                    sql2=''' insert into Attendance values ('%s',%d) '''%(courseID,stuID)
                    cursor.execute(sql2)
                except:
                    None
                else: # 更新表Student ↓
                    sql3='''update Student set rest=rest-1 where id_stu=%d'''%stuID
                    cursor.execute(sql3)
                #本来sql2=''' insert or ignore into Attendance values ('%s',%d) '''%(courseID,stuID)
                #但是考虑到表student只有在insert成功的前提下才需要update(也就是说同一节课上多次也只扣除1课时)
                #所以这里用try语句→当无法在Attendance中插入听课记录的时候,即说明不需要更新表Student  

    #删除某个学生的某次出勤记录 ↓
    if attend != '':
        if attend[0]=='-':
            a=attend.strip('-')
            if a.isdigit():
                stuID=int(a)
                sql1="select * from Attendance where id_course='%s' and id_stu=%d" % (courseID,stuID)
                cursor.execute(sql1)
                res1=cursor.fetchone()
                if res1 != None:
                    sql2="delete from Attendance where id_course='%s' and id_stu=%d" % (courseID,stuID)
                    cursor.execute(sql2)
                    sql3='update Student set rest=rest+1 where id_stu=%d' % stuID
                    cursor.execute(sql3)

    db.commit()
    db.close()

    #更新list1 ↓
    showListStu()

    #更新list2 ↓
    showListCou()

    giveTip()

def showListStu():
    db=connect_db()
    cursor=db.cursor()
    cursor.execute(''' select * from Student order by id_stu''')
    res2=cursor.fetchall()
    #print(res2)# res2和下面的listOFstudent1结果一样,形如[(1, 'a', 13, 13), (2, 'c', 4, 4), (3, 'aa', 2, 2), (4, 'ba', 0, 0)]
       # listOFstudent1=[]
       # for row in res2:
       #     listOFstudent1.append(row)
       # print (listOFstudent1) 
    listOFstudent2.set(res2) #listOFstudent2在此没有其他定义,所以会自动搜寻关联到函数外部的同名变量
        #for row in res2:
        #    string='%d'%row[0]+', '+row[1]+', '+'%d'%row[2]+', '+'%d'%row[3]
        #    tk.Message(root,text=string,width=100).pack()
        # Message不能做到实时更新
        
    db.commit()
    db.close()

def showListCou():
            db=connect_db()
            cursor=db.cursor()
            cursor.execute(''' select * from Course order by id_course''')
            res=cursor.fetchall()
            listOFcourse.set(res) #listOFcourse在此没有其他定义,所以会自动搜寻关联到函数外部的同名变量
            db.commit()
            db.close()

def addcredits():
    txt=app.add.get()
    if txt == '': 
        return    #确保空按“更新”不会报错
    
    try:
        x=txt.split("+",2) # 分割2次,返回三个参数的列表
        stuID=int(x[0])
        stuname=x[1]
        if x[2]=='':
            x[2]=0
        credit=int(x[2])
    except:
        return
    else:
        db=connect_db()
        cursor=db.cursor()
        sql1='select * from Student where id_stu=%d' % stuID
        cursor.execute(sql1)
        res=cursor.fetchone() 
        #print(type(res)) #<class 'tuple'>
        if res==None:
            sql3=''' insert into Student values (%d,'%s',%d,%d) '''%(stuID,stuname,credit,credit)
            cursor.execute(sql3)
        else:
            if stuname=='': #空字符不是None!!!
                sql2=''' update Student set total=total+%d, rest=rest+%d where id_stu=%d ''' % (credit,credit,stuID)
            else:
                sql2=''' update Student set stuname='%s',total=total+%d, rest=rest+%d where id_stu=%d ''' % (stuname,credit,credit,stuID)
            cursor.execute(sql2)
        
        db.commit()
        db.close()

        showListStu()
        giveTip()

        app.add.delete(0,END) #清空输入框

def consultStu():
    try:
        stuID=int(app.astudent.get())
    except:
        return
    sql1='select * from Student where id_stu=%d'%stuID
    sql2='select A.id_course,C.course from Attendance as A inner join Course as C\
        on A.id_course=C.id_course where A.id_stu=%d order by A.id_course'%stuID
    sql3='select C.id_course,C.course from Course as C where C.id_course not in \
        (select id_course from Attendance where id_stu=%d) order by C.id_course'%stuID
    
    db=connect_db()
    cursor=db.cursor()
    cursor.execute(sql1)
    res1=cursor.fetchone()
    if res1==None:
        return  # 确保输入的学生ID有效
    string1='学生ID:'+'%d'%res1[0]+'  学生名字:'+res1[1]+'  总课时:'+'%d'%res1[2]+'  剩余课时:'+'%d'%res1[3]

    cursor.execute(sql2)
    res2=cursor.fetchall()

    cursor.execute(sql3)
    res3=cursor.fetchall()

    window2=tk.Tk()
    window2.title('学生出勤记录')
    window2.geometry('400x400')
    tk.Message(window2,text=string1,width=300).pack()
    for i in res2:
        string2=i[0]+' '+i[1]
        tk.Message(window2,text=string2,width=400).pack()
    tk.Message(window2,text='\n未出席课程:',fg='#808080',width=300).pack()
    for i in res3:
        string3=i[0]+' '+i[1]
        tk.Message(window2,text=string3,fg='#808080',width=400).pack()

    db.commit()
    db.close()

    app.astudent.delete(0,END) #清空输入框

def consultCou():
    courseID=app.acourse.get()
    sql='''select course from Course where id_course='%s' '''% courseID
    sql2='''select A.id_stu,S.stuname from Attendance as A,Student as S \
        where A.id_course='%s' and S.id_stu=A.id_stu order by A.id_stu'''% courseID
    # sql2和sql3 是一样的
    #sql3='''select A.id_stu,S.stuname from Attendance as A inner join Student as S \
    #    on S.id_stu=A.id_stu where A.id_course='%s' order by A.id_stu '''% courseID
    sql4="select id_stu,stuname from Student where id_stu not in \
        (select id_stu from Attendance where id_course='%s')"  % courseID  

    db=connect_db()
    cursor=db.cursor()
    cursor.execute(sql)
    res=cursor.fetchone()
    if res==None:
        return  # 确保输入的课程ID有效
    string1='课程ID:'+courseID+'\n'+'课程内容:'+res[0]
    
    cursor.execute(sql2)
    res2=cursor.fetchall()

    cursor.execute(sql4)
    res4=cursor.fetchall()

    window=tk.Tk()
    window.title('课程出勤记录')
    window.geometry('400x400')
    tk.Message(window,text=string1,width=300).pack()
    for i in res2:
        string2='%d'%i[0]+'  '+i[1]
        tk.Message(window,text=string2,width=300).pack()
    tk.Message(window,text='\n未出席学生:',fg='#808080',width=300).pack()
    for i in res4:
        string4='%d'%i[0]+'  '+i[1]
        tk.Message(window,text=string4,fg='#808080',width=300).pack()

    db.commit()
    db.close()

    app.acourse.delete(0,END) #清空输入框

def giveTip():
    db=connect_db()
    cursor=db.cursor()
    sql1='select count(*) from Student'
    cursor.execute(sql1)
    res=cursor.fetchall() # res=[(行数,)]
    num=res[0][0]
    num=num-1
    sql2='select id_course from Attendance group by id_course having count(id_course)<%d' % num 
    # 新录入的课程显然没有一个学生上过,所以不会出现在Attendance中,不能算有“缺席情况”
    cursor.execute(sql2)
    res2=cursor.fetchall()
    list2=[str(i) for i in res2] # 因为join()这个函数的前提是列表的每一项都是字符串
    s=''.join(list2).replace('(','').replace(')','').replace("'",'')
    if s=='':
        s='...'
    ss='Tip:缺席学生2人及以上的课程ID有'+s
    tip.set(ss)
    db.commit()
    db.close()

def connect_db():
    db=sqlite3.connect('record.db')
    # 连接到数据库,如果数据库不存在,那它就会被创建,并返回一个数据库对象
    return db

def creat_table(db):
    cursor=db.cursor()
    # 获取cursor
    # 创建数据表Course,Attendance,Student
    cursor.execute(''' 
            create table if not exists Course 
            (
                id_course char(3) primary key  not null,
                course char(20)  
            )            
    ''')
    cursor.execute(''' 
            create table if not exists Attendance 
            (
                id_course char(3),
                id_stu int,
                primary key(id_course,id_stu)  
            )            
    ''')
    #print("Table Attendance create successfully")
    cursor.execute('''
            create table if not exists Student
            (
                id_stu int primary key  not null,
                stuname char(10),
                total int,
                rest int
            )
    ''')
    #print("Table Student create successfully")

    db.commit()
    db.close() # 执行完成之后养成关闭数据库连接的好习惯



db=connect_db()
creat_table(db)

root = tk.Tk()
root.title('出勤记录')
root.geometry('600x750')
#root.resizable() # root.resizable(width=True,heigth=True) # 设置窗口是否可以变化长宽,默认可变
app = Application(master=root)
app.mainloop()

一些不错的笔记:

  1. https://www.cnblogs.com/xuaijun/p/8296872.html
    在这里插入图片描述
  2. 布局管理
    https://blog.csdn.net/yingshukun/article/details/53983812
    https://www.cnblogs.com/pertor/p/9664344.html
    https://www.cnblogs.com/bambipai/p/6770447.html
  3. Tkinter–Listbox,StringVar,bind
    https://blog.csdn.net/ahilll/article/details/81531587
    https://blog.csdn.net/m0_37264397/article/details/79079259
    https://www.cnblogs.com/hackpig/p/8206763.html ←这篇特别好
    https://www.cnblogs.com/jerryspace/p/9836142.html → bind
  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值