pack()这个布局方式非常不友好,最后菜菜用了11个Frame互相嵌套 ↑
写了很多罗里吧嗦的字符串操作语句来保证这些输入框不会报错:
string.replace('需要替换的字符','new')
string.strip('需要删去的首尾字符')
string.split('分隔符',分割次数)
string.isdigit() 判断输入的是否是纯数字
list=[str(i) for i in list0] 先把列表list0每一项都变为字符串
''.join(list) 再把列表拼接为字符串
可以实时更新显示的Listbox和Label
x=tkinter.StringVar()
tkinter.Listbox(self,listvariable=x).pack()
x.set('更新的字符串')
获取、清除输入框Entry的内容:
entry.get() 或者在类外调用时须加名字前缀: app.entry.get()
entry.delete(0,END) 或者在类外调用时须加名字前缀: app.entry.delete(0,END)
注意None和空字符并不等价~;
'+'.split('+') 的结果是 ['', '']
cursor.fetchone()和fetchall()返回的都是列表,列表中是元组,形如[(1, ‘a’, 13, 13), (2, ‘c’, 4, 4), (3, ‘a’, 2, 2)],如果没有查询到结果则会返回None;
复习了很多已经快忘光的sql语句:
group by, having count(*)>2, update, delete, insert or ignore, inner join
菜菜第一次写了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)) #
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()
一些不错的笔记: