Python3:基于Opencv4.2和wxPython4可视化界面的人脸考勤系统

article/2025/10/8 5:16:16

前言

主题:基于Opencv4.2的人脸考勤系统

功能模块

架构

  • 采集人脸:收集需要录入的学生信息,开启摄像头收集截取对应的人脸图片;
  • 训练人脸数据模型:根据收集和整合好的人脸图片数据训练成人脸数据模型;
  • 人脸识别打卡:打开摄像头,截取一帧帧的人脸图片与训练好的人脸数据模型进行置信度比对,在可定范围内识别并自动打卡,打卡记录存入数据库。

交互方式

  1. 首先,点击“采集人脸”的按钮,输入对应的学生的姓名,学号,学院信息,成功录入数据库后,等待三秒后打开摄像头,面向摄像头,成功收集200张人脸图片数据后,关闭摄像头;
  2. 其次,将所有需要录入的学生的信息和人脸图片收集好后,点击“训练数据”按钮,系统自动读取学生信息和其对应的人脸图片数据,训练人脸模型;
  3. 最后,点击“识别打卡”按钮,开启摄像头,面对摄像头,系统根据人脸数据模型与当前人脸进行置信度对比,在可定范围内识别,显示打卡成功,并将学生信息显示在界面上,并将当前时间打卡的数据写入数据库,同时更新到右侧的打卡记录列表内。
    附加,点击“关闭摄像头”按钮,即可直接关闭调用摄像头的相关线程。

开发工具及环境

  • 数据存储用的为Sqlite3(可以更换为别的)
  • 开发平台为PyCharm,
  • 编程语言为Python3.6
    使用的库主要有:
    wxPython4.0.7opencv-python4.2.0.32opencv-contrib-python4.2.0.32numpypysqlite3threadingPIL

主要逻辑

人脸采集

  1. 首先是录入学生数据,点击采集人脸数据的按钮后,会弹出输入框,系统会查询判断该信息是否重复录入。
    信息采集

  2. 然后倒数3秒后开启摄像头,开启收集人脸图片/使用的是opencv自带的级联分类器来读取opencv以及训练行号的人脸模型数据haarcascade_frontalface_default.xml,然后将截取出来的人脸图片编程灰度图片存入对应的文件夹。
    主要代码如下:

    face_detector = cv.CascadeClassifier(haarcascade_frontalface_file_path)
    gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
    faces = face_detector.detectMultiScale(gray, 1.2, 5)
    

训练数据

点击“训练数据”的按钮后,系统会遍历“./resources/pictures”目录下各个学生的人脸图片,然后使用LBP直方图LBPHFace来训练人脸模型,并将生成训练好的文件存储到“./resources/Trainer”文件夹下。
在这里插入图片描述

识别打卡

点击“识别打卡”按钮后调用摄像头,将读取的每一帧图片都转为灰度图,然后依旧使用opencv自带的haarcascade_frontalface人脸模型数据来截取出对应的人脸图片,然后使用LBP直方图LBPHFace的人脸识别器,读取训练好的人脸模型数据,跟每一帧人脸图片进行对比,给出相应的置信度,当同个人的人脸图片与某个人脸数据对比生成的置信度低于55且超过24张图片,则判定该摄像头读取的人脸是人脸数据模型里面的某个人脸,然后进行打卡操作,将其对应的学生信息显示到界面上,同时记录打卡。

主要代码

以下代码为主要代码,但是需要提供相关文件资源,建议到代码仓库下载完整项目文件

import random
import cv2
import numpy
import wx
import wx.grid
import datetime
import os
import time
import cv2 as cv
import numpy as np
import threading
import sqlite3
from PIL import Image, ImageFont, ImageDrawdatabase_file_path = './resources/data.db'
picture_dir_path = "./resources/pictures"
trainer_file_path = './resources/Trainer/trainer.yml'
font_file_path = './resources/simsun.ttc'
zsc_circle_file_path = './resources/zsc.jpg'
zsc_rectangle_file_path = './resources/zsc.png'
haarcascade_frontalface_file_path = './resources/haarcascade_frontalface_default.xml'
capture_opt = 'http://admin:admin@192.168.43.1:8081/'  # 摄像头参数,0,1为本机摄像头# 继承wx库里面的Frame类来使用
class MainFrame(wx.Frame):def __init__(self):# 初始化窗体数据wx.Frame.__init__(self, None, -1, '人脸考勤系统', pos=(100, 100), size=(1337, 600))panel = wx.Panel(self, -1)sizer = wx.BoxSizer(wx.HORIZONTAL)sizer1 = wx.BoxSizer(wx.VERTICAL)sizer2 = wx.BoxSizer(wx.VERTICAL)font = wx.Font(15, wx.ROMAN, wx.NORMAL, wx.BOLD)# 设置窗口以及托盘图标icon = wx.Icon()icon.CopyFromBitmap(wx.Bitmap(wx.Image((zsc_circle_file_path), wx.BITMAP_TYPE_JPEG)))self.SetIcon(icon)# 设置左边背景学院logoimage = wx.Image(zsc_rectangle_file_path, wx.BITMAP_TYPE_PNG).ConvertToBitmap()self.background = wx.StaticBitmap(panel, -1, bitmap=image, style=wx.ALIGN_CENTER)sizer1.Add(self.background, proportion=10, flag=wx.ALIGN_CENTER, border=10)# 设置采集人脸按钮self.command1 = wx.Button(panel, -1, '采集人脸')self.command1.SetFont(font)self.command1.SetBackgroundColour('#3299CC')sizer1.Add(self.command1, proportion=5, flag=wx.ALL | wx.EXPAND, border=10)# 设置训练数据按钮self.command2 = wx.Button(panel, -1, '训练数据')self.command2.SetFont(font)self.command2.SetBackgroundColour('#DBDB70')sizer1.Add(self.command2, proportion=5, flag=wx.ALL | wx.EXPAND, border=10)# 设置人脸识别按钮self.command3 = wx.Button(panel, -1, '识别打卡')self.command3.SetFont(font)self.command3.SetBackgroundColour('#32CC32')sizer1.Add(self.command3, proportion=5, flag=wx.ALL | wx.EXPAND, border=10)# 设置退出系统按钮self.command4 = wx.Button(panel, -1, '关闭摄像头')self.command4.SetFont(font)self.command4.SetBackgroundColour((random.randint(1, 255), random.randint(0, 255), random.randint(0, 255)))sizer1.Add(self.command4, proportion=5, flag=wx.ALL | wx.EXPAND, border=10)# 设置消息提示文本self.text5 = wx.StaticText(panel, -1, '\n\n', style=wx.ALIGN_CENTER)self.text5.SetFont(font)self.text5.SetForegroundColour('Red')sizer1.Add(self.text5, proportion=15, flag=wx.ALL | wx.EXPAND, border=10)# 设置个人信息文本self.text6 = wx.StaticText(panel, -1, '姓名:')self.text7 = wx.StaticText(panel, -1, '学号:')self.text8 = wx.StaticText(panel, -1, '学院:')sizer1.Add(self.text6, proportion=3, flag=wx.LEFT, border=0)sizer1.Add(self.text7, proportion=3, flag=wx.LEFT, border=0)sizer1.Add(self.text8, proportion=3, flag=wx.LEFT, border=0)# 把分布局全部加入整体顶级布局sizer.Add(sizer1, flag=wx.EXPAND | wx.ALL, border=20)# 设置右上边消息提示文本sizer3 = wx.BoxSizer(wx.HORIZONTAL)font = wx.Font(9, wx.ROMAN, wx.NORMAL, wx.BOLD)self.text9 = wx.StaticText(panel, -1, '', style=wx.ALIGN_LEFT)self.text9.SetFont(font)self.text9.SetForegroundColour('Red')self.text9.SetLabel(u'温馨提示:' + '您好,欢迎使用人脸考勤系统!')self.text10 = wx.StaticText(panel, -1, '', style=wx.ALIGN_RIGHT)self.text10.SetFont(font)self.text10.SetForegroundColour('Blue')self.data_num = 0self.updateSumData()sizer3.Add(self.text9, proportion=1, flag= wx.ALL|wx.EXPAND, border=10)sizer3.Add(self.text10, proportion=1, flag= wx.ALL|wx.EXPAND, border=10)sizer2.Add(sizer3, proportion=1, flag=wx.EXPAND | wx.ALL, border=0)# 封面图片self.image_cover = wx.Image(zsc_circle_file_path, wx.BITMAP_TYPE_ANY).Scale(575, 460)self.bmp = wx.StaticBitmap(panel, -1, wx.Bitmap(self.image_cover))sizer2.Add(self.bmp, proportion=1, flag=wx.ALL|wx.EXPAND ,border=0)# 加入顶级布局sizer.Add(sizer2, flag=wx.EXPAND | wx.ALL, border=10)# 实例化数据库操作对象self.mySqlDao = MySQLDao(self)self.grid = MyGrid(panel, self.mySqlDao)self.grid.updateGrid()# 打卡记录表sizer.Add(self.grid, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)panel.SetSizer(sizer)# 四个按钮对应的事件self.command1.Bind(wx.EVT_BUTTON, self.command1_event)self.command4.Bind(wx.EVT_BUTTON, self.command4_event)self.command3.Bind(wx.EVT_BUTTON, self.command3_event)self.command2.Bind(wx.EVT_BUTTON, self.command2_event)# 关闭事件self.Bind(wx.EVT_CLOSE,self.onClose)# 窗口居中,显示self.Center()self.Show()# 控制摄像头的开启与关闭self.recognition = Falseself.collected = False'''主窗体关闭事件'''def onClose(self,event):self.command4_event(event)# 等待子线程完成 再关闭,防止不正常退出time.sleep(1)self.Destroy()'''采集数据按钮的响应事件'''def command1_event(self, event):self.text6.SetLabel('姓名:')self.text7.SetLabel('学号:')self.text8.SetLabel('学院:')self.text5.SetLabel(u'\n温馨提示:\n' + '⚪正在进学生信息录入...')self.text5.Update()collectFrame = CollectFrame(self,self.mySqlDao)collectFrame.Show()'''训练数据按钮的响应事件'''def command2_event(self, event):self.trainData()'''识别打卡按钮的响应事件'''def command3_event(self, event):self.text5.SetLabel(u'')self.recognition = Falset1 = threading.Thread(target=self.recognitionFace)t1.start()'''关闭摄像头按钮的响应事件'''def command4_event(self, event):if self.collected == False:self.collected = Trueif self.recognition == False:self.recognition = Truedef updateSumData(self):self.data_num = 0for list in os.listdir(picture_dir_path):if len(os.listdir(picture_dir_path + '/' + list)) >= 200:self.data_num += 1self.text10.SetLabel(u'当前已采集人脸数据的学生数:' + str(self.data_num))self.text10.Update()'''@Author:Himit_ZH@Function:处理收集人脸每一帧生成图片存入对应文件夹'''def collect(self,face_id):self.text5.SetLabel(u'\n温馨提示:\n' + '请看向摄像头\n准备采集200张人脸图片...')count = 0  # 统计照片数量path = picture_dir_path+"/Stu_" + str(face_id)  # 人脸图片数据的储存路径# 读取视频cap = cv.VideoCapture(capture_opt)if cap.isOpened() == False:self.text5.SetLabel(u'\n错误提示:\n' + '×采集人脸数据失败!\n原因:未能成功打开摄像头')return# 加载特征数据face_detector = cv.CascadeClassifier(haarcascade_frontalface_file_path)if not os.path.exists(path):  # 如果没有对应文件夹,自动生成os.makedirs(path)while self.collected == False:flag, frame = cap.read()# print('flag:',flag,'frame.shape:',frame.shape)if not flag:break# 将图片灰度gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)faces = face_detector.detectMultiScale(gray, 1.1, 3)if len(faces) > 1:  # 一帧出现两张照片丢弃,原因:有人乱入,也有可能人脸识别出现差错continue# 框选人脸,for循环保证一个能检测的实时动态视频流for x, y, w, h in faces:cv.rectangle(frame, (x, y), (x + w, y + h), color=(0, 255, 0), thickness=2)count += 1cv.imwrite(path + '/' + str(count) + '.png', gray[y:y + h, x:x + w])# # 显示图片# cv.imshow('Camera', frame)# 将一帧帧图片显示在UI中image1 = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)image2 = cv2.resize(image1, (575, 460))height, width = image2.shape[:2]pic = wx.Bitmap.FromBuffer(width, height, image2)# 显示图片在panel上self.bmp.SetBitmap(pic)self.bmp.Update()self.text9.SetLabel(u'温馨提示:\n' + '已采集'+ str(count)+'张人脸照片')if count >= 200:  #默认采集200张照片break# 关闭资源if count >=200:self.text5.SetLabel(u'\n温馨提示:\n' + '✔已成功采集到人脸数据!')self.updateSumData()else:self.text5.SetLabel(u'\n错误提示:\n' + '×采集人脸数据失败!\n未能收集到200张人脸数据!')# 删除该文件夹下的所有数据ls = os.listdir(path)for file_path in ls:f_path = os.path.join(path, file_path)os.remove(f_path)os.rmdir(path)cv.destroyAllWindows()cap.release()self.bmp.SetBitmap(wx.Bitmap(self.image_cover))'''@Author:Himit_ZH@Function:遍历指定文件夹里面的人脸数据,根据文件夹名字,训练对应数据'''def trainData(self):self.text5.SetLabel(u'\n温馨提示:\n' + '⚪正在整合训练的人脸数据\n请稍后...')# 图片路径path = picture_dir_pathfacesSamples = []imageFiles = []ids = []for root, dirs, files in os.walk(path):# root 表示当前正在访问的文件夹路径# dirs 表示该文件夹下的子目录名list# files 表示该文件夹下的文件list# 遍历文件for file in files:imageFiles.append(os.path.join(root, file))# 检测人脸的模型数据face_detector = cv2.CascadeClassifier(haarcascade_frontalface_file_path)# 遍历列表中的图片stu_map =  self.mySqlDao.getIdfromSql()for imagefile in imageFiles:  # 获得所有文件名字imagefile = imagefile.replace('\\', '/')sno = imagefile.split('/')[3].split('_')[1]if stu_map.get(sno):PIL_img = Image.open(imagefile).convert('L')  # 打开图片并且转为灰度图片# 将图像转换为数组img_numpy = np.array(PIL_img, 'uint8')faces = face_detector.detectMultiScale(img_numpy)for x, y, w, h in faces:facesSamples.append(img_numpy[y:y + h, x:x + w])ids.append(int(stu_map.get(sno)))self.text5.SetLabel(u'\n温馨提示:\n' + '⚪整合数据结束\n训练数据中...')self.text5.Update()# 获取训练对象recognizer = cv2.face.LBPHFaceRecognizer_create()recognizer.train(facesSamples, np.array(ids))# 保存文件recognizer.write(trainer_file_path)# 停顿0.5秒 防止卡顿time.sleep(0.5)self.text5.SetLabel(u'\n温馨提示:\n' + '✔训练人脸数据成功!')self.text5.Update()'''@Author:Himit_ZH@Function:读取Tranier文件夹下训练好的人脸数据,跟摄像头读取的人脸数据进行对比,进行识别操作'''def recognitionFace(self):self.text9.SetLabel(u'温馨提示:\n' + '识别过程中尽量不要多人在识别范围内,\n否则可能识别为他人!')self.text9.Update()recogizer = cv2.face.LBPHFaceRecognizer_create()# 加载训练数据集文件recogizer.read(trainer_file_path)# 准备人脸特征数据face_detector = cv2.CascadeClassifier(haarcascade_frontalface_file_path)all_stu_map = self.mySqlDao.getDataFromSql()  # 获取数据库对应数据cam = cv2.VideoCapture(capture_opt)  # 开启默认摄像头,其他外接摄像头参数可换为1,2....if cam.isOpened() == False:self.text5.SetLabel(u'\n错误提示:\n' + '×采集人脸数据失败!\n原因:未能成功打开摄像头')returnminW = 0.1 * cam.get(3)minH = 0.1 * cam.get(4)font = cv2.FONT_HERSHEY_SIMPLEX  # 字体格式# 记录识别的学生图片数,达到24张则表示可识别stu_count={}while self.recognition == False:ret, img = cam.read()  # 读取每一帧图片gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 设置成灰度图片faces = face_detector.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=5,minSize=(int(minW), int(minH)))stu_id = 0stu_name = ''for (x, y, w, h) in faces:cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)id_num, confidence = recogizer.predict(gray[y:y + h, x:x + w])idname = '正在识别...'if confidence <= 55: # 置信度55以下表示可以if stu_count.get(id_num):stu_count[id_num] += 1else:stu_count[id_num] = 1if stu_count[id_num] >= 24:idname = all_stu_map[id_num].get('name')stu_name = idnamestu_id = all_stu_map[id_num].get('id')self.text5.SetLabel(u'\n识别成功:\n' + '✔欢迎您\n亲爱的'+idname+'同学')self.text6.SetLabel('姓名:'+all_stu_map[id_num].get('name'))self.text7.SetLabel('学号:'+all_stu_map[id_num].get('num'))self.text8.SetLabel('学院:'+all_stu_map[id_num].get('college'))self.text5.Update()self.text6.Update()self.text7.Update()self.text8.Update()self.recognition = Truebreakconfidence = "置信度:%.2f"%confidence# 将名字和相似度显示在图片上 显示中文img = self.cv2ImgAddText(img, idname, x, (y - 30), (0, 255, 0), 30)# cv2.putText(img, str(idname), (x+5, y-5), font, 1, (0, 0, 255), 2)img = self.cv2ImgAddText(img, str(confidence), x + 5, y + h - 5,  (0, 0, 255), 30)# cv2.imshow('Camera', img)# 将一帧帧图片显示在UI中image1 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)image2 = cv2.resize(image1, (575, 460))height, width = image2.shape[:2]pic = wx.Bitmap.FromBuffer(width, height, image2)# 显示图片在panel上self.bmp.SetBitmap(pic)if self.recognition and stu_name != '':self.text9.SetLabel(u'温馨提示:识别成功!')self.mySqlDao.addCheckData(stu_id)self.Message = wx.MessageDialog(self, '亲爱的'+stu_name+'同学:打卡成功!', '成功',wx.ICON_MASK)self.Message.ShowModal()self.Message.Destroy()cam.release()cv2.destroyAllWindows()self.bmp.SetBitmap(wx.Bitmap(self.image_cover))self.grid.updateGrid()self.text9.SetLabel(u'温馨提示:' + '您好,欢迎使用人脸考勤系统!')def cv2ImgAddText(self,img, text, left, top, textColor=(0, 255, 0), textSize=20):if (isinstance(img, numpy.ndarray)):  # 判断是否OpenCV图片类型img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(img)fontText = ImageFont.truetype(font_file_path, textSize, encoding="utf-8")draw.text((left, top), text, textColor, font=fontText)return cv2.cvtColor(numpy.asarray(img), cv2.COLOR_RGB2BGR)class CollectFrame(wx.Frame):def __init__(self,mainFrame,mySqlDao):self.mainFrame = mainFrameself.mySqlDao = mySqlDaowx.Frame.__init__(self, None, -1, '数据采集 - 人脸识别系统', pos=(100, 100), size=(350, 350))panel = wx.Panel(self, -1)sizer = wx.BoxSizer(wx.VERTICAL)font = wx.Font(15, wx.ROMAN, wx.NORMAL, wx.BOLD)# 设置窗口以及托盘图标icon = wx.Icon()icon.CopyFromBitmap(wx.Bitmap(wx.Image((zsc_circle_file_path), wx.BITMAP_TYPE_JPEG)))self.SetIcon(icon)# 设置静态‘姓名'self.text1 = wx.StaticText(panel, -1, '姓名:', style=wx.ALIGN_LEFT)self.text1.SetFont(font)sizer.Add(self.text1, proportion=1, flag=wx.ALIGN_LEFT | wx.EXPAND, border=10)# 姓名输入框self.text2 = wx.TextCtrl(panel, -1, size=(200, -1), style=wx.TE_LEFT)self.text2.SetFont(font)sizer.Add(self.text2, proportion=0, flag=wx.ALL | wx.EXPAND, border=15)# 设置静态'学号'self.text3 = wx.StaticText(panel, -1, '学号:', style=wx.ALIGN_LEFT)self.text3.SetFont(font)sizer.Add(self.text3, proportion=1, flag=wx.ALIGN_LEFT | wx.EXPAND, border=10)# 学号输入框self.text4 = wx.TextCtrl(panel, -1, size=(200, -1), style=wx.TE_LEFT)self.text4.SetFont(font)sizer.Add(self.text4, proportion=0, flag=wx.ALL | wx.EXPAND, border=15)# 设置静态'学院'self.text5 = wx.StaticText(panel, -1, '学院:', style=wx.ALIGN_LEFT)self.text5.SetFont(font)sizer.Add(self.text5, proportion=1, flag=wx.ALIGN_LEFT | wx.EXPAND, border=10)# 学院输入框self.text6 = wx.TextCtrl(panel, -1, size=(200, -1), style=wx.TE_LEFT)self.text6.SetFont(font)sizer.Add(self.text6, proportion=0, flag=wx.ALL | wx.EXPAND, border=15)# 设置采集人脸数据按钮self.startCollect = wx.Button(panel, -1, '开始采集人脸数据')self.startCollect.SetFont(font)self.startCollect.SetBackgroundColour('#3299CC')sizer.Add(self.startCollect, proportion=2, flag=wx.ALL | wx.EXPAND, border=10)panel.SetSizer(sizer)# 按钮对应的事件self.startCollect.Bind(wx.EVT_BUTTON, self.startCollect_Event)self.Bind(wx.EVT_CLOSE,self.onClose)self.Center()self.Show()def startCollect_Event(self,event):face_name = self.text2.GetValue()face_num = self.text4.GetValue()face_college = self.text6.GetValue()result = self.mySqlDao.PutDatatoSql(face_name, face_num,face_college)flag = 0 # 是否成功if result == 1:flag = 1elif result == 2:# 可能存在数据库有记录 但是图像资源被删掉了,这种情况重新录入if not os.path.exists(picture_dir_path+'/Stu_' + str(face_num)):  # 文件夹是否存在flag = 1elif not os.listdir(picture_dir_path+'/Stu_' + str(face_num)):  # 文件夹里面是否有文件flag = 1else:self.Message = wx.MessageDialog(self, '该学生的信息已录入!请重新输入!', '错误', wx.ICON_ERROR)self.Message.ShowModal()self.Message.Destroy()else:self.Message = wx.MessageDialog(self, '数据库连接失败!', '错误', wx.ICON_ERROR)self.Message.ShowModal()self.Message.Destroy()if flag == 1 :if result ==1:self.Message = wx.MessageDialog(self, '学生信息录入数据库成功!确认后开始采集人脸!', '成功', wx.ICON_ASTERISK)else:self.Message = wx.MessageDialog(self, '该学生信息已在数据库,但是人脸数据未成功采集!\n确认后将重新采集人脸数据!', '警告', wx.ICON_WARNING)self.Message.ShowModal()self.Message.Destroy()self.Hide()second = 3while second > 0:self.mainFrame.text5.SetLabel(u'\n温馨提示:\n' + '⚪请对准摄像头\n' + str(second) + '秒后开始进行人脸录入...')self.mainFrame.text5.Update()time.sleep(1)second -= 1self.mainFrame.collected = Falset1 = threading.Thread(target=self.mainFrame.collect,args=(face_num,))t1.start()def onClose(self,event):self.mainFrame.text5.SetLabel(u'')self.mainFrame.text9.SetLabel(u'温馨提示:' + '您好,欢迎使用人脸考勤系统!')self.Destroy()class MyGrid(wx.grid.Grid):def __init__(self, parent,mySqlDao):wx.grid.Grid.__init__(self, parent=parent, id=-1)#self.CreateGrid(0, 7)self.SetDefaultCellBackgroundColour('#BFD8D8')#parent.BackgroundColourself.SetDefaultCellTextColour("#000000")self.CreateGrid(0, 4) #设置初始化时的行数和列数#为列定义初始化名字self.SetColLabelValue(0,'学号')self.SetColLabelValue(1,'姓名')self.SetColLabelValue(2,'学院')self.SetColLabelValue(3,'打卡时间')self.mySqlDao = mySqlDaodef updateGrid(self):self.ClearGrid()if self.GetNumberRows() > 0:self.DeleteRows(0, self.GetNumberRows())# 获取打卡表数据data = self.mySqlDao.getCheckData()x = len(data)y = 4self.InsertRows(0, x)for i in range(0,x):for j in range(0,y):self.SetCellValue(i,j, str(data[i][j]))#设置列的宽度self.AutoSize()self.Update()self.Refresh()'''
操作数据库的类
'''
class MySQLDao():'''数据库初始化操作'''def __init__(self,mainFrame):self.mainFrame = mainFramecon = sqlite3.connect(database_file_path)# 创建游标对象cur = con.cursor()#判断是否存在库#判断是否存在表 无则自动创建sql1 = r'''CREATE TABLE IF NOT EXISTS `t_stu`(`id` INTEGER  PRIMARY KEY AUTOINCREMENT,`sname` TEXT NOT NULL,`sno` TEXT NOT NULL,`college` TEXT NOT NULL,`created_time` datetime DEFAULT NULL);'''sql2 = r'''CREATE TABLE IF NOT EXISTS `t_check`(`id` INTEGER PRIMARY KEY AUTOINCREMENT ,`uid` INTEGER NOT NULL,`check_time` datetime NOT NULL,CONSTRAINT `check_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `t_stu` (`id`) ON DELETE CASCADE ON UPDATE CASCADE);'''try:cur.execute(sql1)cur.execute(sql2)con.commit()except Exception as e:self.mainFrame.Message = wx.MessageDialog(self.mainFrame, '数据库初始化失败!请检查数据库是否正常创建或链接!', 'ERROR', wx.ICON_ERROR)self.mainFrame.Message.ShowModal()self.mainFrame.Message.Destroy()finally:cur.close()con.close()'''@Function:将对应的学生数据存入数据库'''def PutDatatoSql(self,sname, sno,college):flag = 1con = sqlite3.connect(database_file_path)# 创建游标对象cur = con.cursor()sql2 = 'select * from t_stu where sname=? and sno=?'try:cur.execute(sql2,(sname, sno))con.commit()# 处理结果集student = cur.fetchall()if student:cur.close()con.close()flag = 2return flagexcept Exception as e:print(e)flag = 0return flag# 编写插入数据的sqlsql3= 'insert into t_stu(sname,sno,college,created_time) values(?,?,?,?)'dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")try:# 执行sqlcur.execute(sql3, (sname, sno,college, dt))# 提交事务con.commit()return flagexcept Exception as e:con.rollback()flag = 0return flagfinally:# 关闭连接cur.close()con.close()'''@Function:根据学生学号获取指定id'''def getIdfromSql(self):stu_map = {}con = sqlite3.connect(database_file_path)# 创建游标对象cur = con.cursor()# 编写查询的sqlsql = 'select sno,id from t_stu'# 执行sqltry:cur.execute(sql)# 处理结果集students = cur.fetchall()for stu in students:stu_map[str(stu[0])] = stu[1]return stu_mapexcept Exception as e:self.mainFrame.Message = wx.MessageDialog(self.mainFrame, '查询数据库错误!请检查数据库链接!', 'ERROR',wx.ICON_ERROR)self.mainFrame.Message.ShowModal()self.mainFrame.Message.Destroy()finally:cur.close()con.close()'''@Function:获取全部学生的信息'''def getDataFromSql(self):all_stu_map = {}# 创建连接con = sqlite3.connect(database_file_path)# 创建游标对象cur = con.cursor()# 编写查询的sqlsql = 'select * from t_stu'# 执行sqltry:cur.execute(sql)# 处理结果集students = cur.fetchall()for student in students:id = student[0]sname = student[1]sno = student[2]college = student[3]all_stu_map[int(id)] = {'id':id,'name': sname, 'num': sno, 'college': college}except Exception as e:self.mainFrame.Message = wx.MessageDialog(self.mainFrame, '查询数据库错误!请检查数据库链接!', 'ERROR',wx.ICON_ERROR)self.mainFrame.Message.ShowModal()self.mainFrame.Message.Destroy()finally:# 关闭连接cur.close()con.close()return all_stu_map'''@Function:获取全部打卡信息'''def getCheckData(self):# 创建连接con = sqlite3.connect(database_file_path)# 创建游标对象cur = con.cursor()# 编写查询的sqlsql = 'select s.sno,s.sname,s.college,c.check_time from t_stu s,t_check c where s.id = c.uid order by c.check_time desc'# 执行sqltry:cur.execute(sql)# 处理结果集students = cur.fetchall()return studentsexcept Exception as e:self.mainFrame.Message = wx.MessageDialog(self.mainFrame, '查询数据库错误!请检查数据库链接!', 'ERROR',wx.ICON_ERROR)self.mainFrame.Message.ShowModal()self.mainFrame.Message.Destroy()finally:# 关闭连接cur.close()con.close()''''''def addCheckData(self,uid):flag = 1con = sqlite3.connect(database_file_path)# 创建游标对象cur = con.cursor()# 编写插入数据的sqlsql = 'insert into t_check(uid,check_time) values(?,?)'dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")try:# 执行sqlcur.execute(sql, (uid, dt))# 提交事务con.commit()return flagexcept Exception as e:con.rollback()flag = 0return flagfinally:# 关闭连接cur.close()con.close()if __name__ == '__main__':app = wx.App()app.locale = wx.Locale(wx.LANGUAGE_CHINESE_SIMPLIFIED)frame = MainFrame()app.MainLoop()

项目地址

Gitee地址
Github地址


http://chatgpt.dhexx.cn/article/QOGT5h93.shtml

相关文章

QT基于百度的人脸考勤系统

目录 一、百度智能云介绍 二、代码 1、camera类 2、widget类 3、register类 4、dialog类 三、效果演示 四、改进 获取完整代码请前往&#xff1a;GitHub - zhaohigh/Qt-chatrobut 目前还在学习中&#xff0c;需要练练手&#xff0c;就写了一个基于百度人脸识别的考勤…

Python人脸识别智能考勤系统 (供源码,附报告)(可答疑,可调试)

目录 一、项目简介 二、功能展示 1.人脸识别功能测试 2.识别并录入人脸及个人信息 3.数据库保存信息 4.考勤打卡 三、环境安装实例 1.下载python对应版本的dlib 2.安装opencv-python 3.安装face_recognition 4.安装pyqt5 5.安装mysqlclient 四、需要源码私信并call邮…

基于OpenCV的人脸识别考勤系统

考勤系统设计 学生上课考勤系统最初的方式是采用的人工纸质点名,目前仍旧有一部分学校依旧采用此种方法点名,这种方法也一直是被认为最有效的签到点名方式。但由于课程繁多加上学生人数众多, 代替点名现象普遍存在, 而且传统的现场点名签到方式费时费力, 直接影响到授课质量…

手把手教你:人脸识别考勤系统

系列文章 手把手教你&#xff1a;人脸识别考勤系统 本文为系列第一篇 文章目录 系列文章项目简介一、项目展示二、环境需求环境安装实例 三、功能模块介绍1.人脸库图像2.构建人脸库3.启动人脸识别功能 完整代码地址 项目简介 本文主要介绍如何使用python搭建&#xff1a;一个…

LabVIEW入门与实战开发32例

链接&#xff1a;https://pan.baidu.com/s/1FqRL1loyDS-YT4wzIdRg2A 提取码&#xff1a;xe7y

LabView学习

一、基础篇 1、Labview是一种程序开发环境&#xff0c;由美国国家仪器&#xff08;NI&#xff09;公司研制开发。 2、NI-VISA(Virtual Instrument Software Architec-ture&#xff0c;以下简称为“VISA”)是美国国家仪器NI(NationalInstrument)公司开发的一种用来与各种仪器总…

基于实践的LabVIEW零基础入门视频教程

原文地址::http://blog.eeecontrol.com/LabVIEW1/ 《基于实践的LabVIEW零基础入门视频教程》 资料不在多,而在于精,资料太多,反而会迷失方向&#xff0c;学习最怕的就是打乱仗,选择对的方向,比努力更重要,这也是本人的亲身经历!《基于实践的LabVIEW零基础入门视频教程》方向明确…

Labview简介

公司&#xff1a;美国国家仪器&#xff08;成立于1976年&#xff09; 初始发布时间&#xff1a;1986年 底层语言&#xff1a;C、C 操作系统&#xff1a;windows、macOS、linux 运用场景&#xff1a;数据采集、仪器控制、自动化、分析与信号处理、工业控制、嵌入式和系统设计…

LabVIEW学习笔记(三)——程序结构

前言&#xff1a; 任何计算机语言都离不开程序结构&#xff0c;LabVIEW 作为一种图形化的高级程序幵发语言也不例外。LabVIEW 中的程序结构包括if-else循环&#xff0c;while循环结构&#xff0c;使能结构&#xff0c;公式节点和数学脚本节点等&#xff0c; 因此通过LabVIEW可以…

LabVIEW使用入门指导

LabVIEW是一种图像化设计工具&#xff0c;它的“代码”格式为*.vi(Virtual Instrument)&#xff0c;分为前端面板和后端面板&#xff0c;前端面板又可以称之为顶层设计&#xff0c;是用户界面&#xff0c;后端设计师代码以及元器件试图。 前端面板 前端面板中&#xff0c;用户…

labview精华资料集合光盘

最后的详细内容如下&#xff1a; 引用 |_Windows颜色拾取器 |_ActiveX举例_写Excel |_打开其它文件或者程序 |_当前文件夹查找指定文件 |_动画显示&#xff08;奔跑的马&#xff09; |_ActiveX举例_演示PPT |_获取当前程序路径及名称并判断状态是EXE等 |_简单暂停 |_前面板对象…

学习labview

公司&#xff1a;美国国家仪器&#xff08;成立于1976年&#xff09; 初始发布时间&#xff1a;1986年 底层语言&#xff1a;C、C 操作系统&#xff1a;windows、macOS、linux 运用场景&#xff1a;数据采集、仪器控制、自动化、分析与信号处理、工业控制、嵌入式和系统设计…

LabVIEW入门学习经验分享

在自动化行业&#xff0c;你如果想开发功能强大且开发周期不要太长的上位机软件。首先考虑labview&#xff0c;它可以调用c、matlab等编程语言的库文件&#xff0c;实现跨语言编程&#xff0c;从而满足你更复杂的控制&#xff0c;这是它的优势&#xff0c;国内外很多的航空航天…

labview入门到出家5(进阶篇)——程序调试以及labview函数库的运用

跟了前面几章的操作流程&#xff0c;相信大家对labview有了一定的认识。其实只要了解了labview的编程思路&#xff0c;再熟悉地运用各个变量&#xff0c;函数以及属性&#xff0c;那么我们就可以打开labview的大门了。跟其他编程语言一样&#xff0c;好的编程思维再加上一个好用…

Labview2018学习之三:LabVIEW编程

LabVIEW是图形化编辑软件&#xff0c;使用LabVIEW编程的基本流程的步骤是&#xff1a; 1&#xff09;VI的创建&#xff1b; 2&#xff09;编辑VI&#xff0c;运行和调试&#xff1b; 程序框图&#xff1a;框图是实际可执行的代码&#xff0c;通过将完成特定功能的对象连接在一…

【LabVIEW】LabVIEW练习

产生100个1-99随机整数数,求其和以及平均值 用for循环产生4行100列的二维数组,数组成员如下: 1,2,3…100; 100,99,98…1; 6,7,8…105; 105,104,103…6; 从这个数组中提取2行50列的二维数组,数组成员如下: 50,49,48…1; 56,57,58 (105) 将这2个数组用数组显示件显示在前面板. …

学习LabVIEW应看的几本书

学习LabVIEW应看的几本书 前几天&#xff0c;看了“花开不谢”的博客&#xff0c;才知道NI在中国落户已经十年了&#xff0c;推算过来大概也就是1998年前后在中国落的户&#xff0c;那时中国的改革开放已经二十多年。NI在中国推广所遇到的最大难题&#xff0c;我猜想应该语言是…

专题一:Labview表格控件 及 应用(一)

专题一&#xff1a;Labview表格控件 及 应用&#xff08;一&#xff09; 最近一段时间&#xff0c;做试验台上位机编程&#xff0c;老是用到表格控件&#xff0c;这几天抽时间专门写一个表格控件的专题。有用到的朋友可以参考一下。 总结教训&#xff1a;尽量把文章控制在5分钟…

如何系统学习LabVIEW?

目录 1、LabVIEW开发技巧 2、LabVIEW仪器控制 3、LabVIEW和单片机&#xff08;Arduino&#xff09; 4、LabVIEW工业图像处理与机器视觉实战 从2021年4月至2022年6月&#xff0c;1年零2个月的时间&#xff0c;终于把LabVIEW工控系列文章更新完了~ LabVIEW工控系列文章包含4部…

专题一:Labview表格控件 及 应用(二)

标题专题一&#xff1a;Labview表格控件 及 应用&#xff08;二&#xff09; 最近一段时间&#xff0c;做试验台上位机编程&#xff0c;老是用到表格控件&#xff0c;这几天抽时间专门写一个表格控件的专题。有用到的朋友可以参考一下。上一讲介绍了表格控件&#xff0c;认识了…