一.硬件图片
二.UI界面设计
主要用到的是QPushButten、QTabWidget、QTableWidget、QLabel
UI界面仁者见仁吧。
三.效果演示及功能说明
- 后台不断读取卡号,检测到卡号可以做出相应的动作
- 对于已存在的用户可以进行打卡操作,并生成为时3秒的弹窗告知,分别是上班打卡成功,下班打卡成功以及用卡错误
- 可以对用户进行添加或者删除
- 由于开发板没有数据库,故没有开启数据库功能
- 这里我只进行了简单的存储,所以直接将插入了QTableWidget当中,有需要的可以在开发板中部署数据库或者写JSON配置文件,将信息进行存储,同样对数据的扫描也全都来自QTableWidget,所以掉电和刷新会丢失数据需要重新添加信息,我会给出我写的数据库部分,但是这里没用到。所以只有简单的打开数据库的操作,看一看了解一下就行。勿喷
- 刷新按钮模仿新一天的轮转
- 卡号是原始16进制,需要10进制可以做相应的操作,这里我就没做了
- 需要在打卡界面才能进行打卡操作
- 用户名为空也可以添加,这里开发板要输入用户名需要唤起键盘,如果觉得不合适的话,在代码中添加唤起键盘事件和检测用户名是不是为空就行了
- 串口号和波特率自行修改,我这里为了省事把串口号在初始化部分写死了!!!!!!!!!!一定要看看你的串口设备并修改!!!!!!!!!!
四.项目源码
- ReadCard类,该类用于初始化串口,并且后台不断检测是否有卡号,在QT中,串口通信已经封装好了,我们只需要加载模块就可以使用,而检测到信号的时候,会发出readyRead信号,捕获该信号就可以进行对应的数据处理了,这里我的操作是将卡号通过信号的形式发送出去,在主窗口则可以对其进行捕获
RecdCard.cpp#include "readcard.h" ReadCard::ReadCard(QObject *parent) : QObject(parent) { serial = new QSerialPort(this); connect(serial, &QSerialPort::readyRead, this, &ReadCard::handleReadyRead); //在这里自行修改串口设备和波特率 serialInit("/dev/ttySAC2",9600); } void ReadCard::serialInit(QString portName,int baudrate) { serial->setPortName(portName); serial->setBaudRate(baudrate); serial->setDataBits(QSerialPort::Data8); serial->setParity(QSerialPort::NoParity); serial->setStopBits(QSerialPort::OneStop); serial->setFlowControl(QSerialPort::NoFlowControl); if (!serial->open(QIODevice::ReadWrite)) { qDebug() << "Failed to open serial port:" << serial->errorString(); return; } qDebug() << "Serial port initialized successfully."; } void ReadCard::handleReadyRead() { QByteArray data = serial->readAll(); // 读取所有可用数据 //qDebug() << data; // 检查数据是否以0x02开头和以0x03结尾 if (data.startsWith('\x02') && data.endsWith('\x03')) { // 去除开头和结尾的特定字符 data = data.mid(1, data.size() - 2); QString buf(data); //将简单处理的卡号发送出去 emit idCardInfoReady(buf); } }
ReadCard.h:#ifndef READCARD_H #define READCARD_H #include <QObject> #include <QDebug> #include <QtSerialPort> #include <QByteArray> class ReadCard : public QObject { Q_OBJECT public: explicit ReadCard(QObject *parent = nullptr); //初始化设备!调用的时候修改你们对应的串口号 void serialInit(QString portName, int baudrate); signals: //自定义信号 void idCardInfoReady(QString idCard); public slots: //自定义槽函数 void handleReadyRead(); private: //串口对象 QSerialPort *serial; }; #endif // READCARD_H
Dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
myUiInit();
readCard = new ReadCard(this);
timer = new QTimer(this);
tabWidgetTimer = new QTimer(this);
//employDatabase = new EmployeeDatabase;
connect(readCard,&ReadCard::idCardInfoReady,this,&Dialog::isInsertIntoTable);
connect(readCard,&ReadCard::idCardInfoReady,this,&Dialog::updateLabel);
connect(timer,&QTimer::timeout,this,&Dialog::updateTimeOnLable);
connect(tabWidgetTimer,&QTimer::timeout,this,&Dialog::switchCurrentTabWidget);
connect(this,&Dialog::signInReady,this,&Dialog::signInDialog);
connect(this,&Dialog::backHomeReady,this,&Dialog::backHomeDialog);
connect(this,&Dialog::errorAction,this,&Dialog::errorUseCardDialog);
connect(this,&Dialog::getAddInfoReady,this,&Dialog::insertIntoTable);
timer->start(1000);
}
Dialog::~Dialog()
{
delete ui;
//delete employDatabase;
}
//判断是否要插入表格中
//通过按钮控制标志位
//如果不是插入,则打卡
void Dialog::isInsertIntoTable(QString idCard)
{
qDebug()<<"capture a idCard!"<<idCard;
//判断卡号存在不存在
int isExistCardrow = queryTableByCard(idCard);
if(isExistCardrow>=0){//存在
//判断是上班还是下班
if(!workFlagMap[isExistCardrow]){//false对应没有上班
signIn(isExistCardrow);
}
else{//已经上班
//是否已经下班
if(backFlagMap[isExistCardrow]){
errorUseCard(isExistCardrow);
}
else
backHome(isExistCardrow);
}
}
else{//不存在
//存储卡号
insertFlag=true;
newIDCard = idCard;
}
}
//插入表格
void Dialog::insertIntoTable(QStringList addInfoList)
{
if(insertFlag){
//获取当前行
int lineNum = ui->allTable->rowCount();
//插入一行
ui->allTable->insertRow(lineNum);
//获取名字
QString addName = addInfoList[0];
ui->allTable->setItem(lineNum, 0, new QTableWidgetItem(addName));
QString addIDCard = addInfoList[1];
ui->allTable->setItem(lineNum, 1, new QTableWidgetItem(addIDCard));
ui->allTable->setItem(lineNum, 2, new QTableWidgetItem("否"));
workFlagMap.insert(lineNum,false);
backFlagMap.insert(lineNum,false);
insertFlag = false;
}
}
//根据卡号查询表格
int Dialog::queryTableByCard(QString idCard)
{
//获取表格所有行
int lineNum = ui->allTable->rowCount();
//遍历
for(int row = 0 ; row<lineNum; ++row){
QTableWidgetItem* itemColumn = ui->allTable->item(row, 1);
QString str = itemColumn->text();
if(idCard==str){
//找到了,返回行号
qDebug()<<"row:"<<row;
return row;
}
}
return -1;
}
//打卡操作
void Dialog::signIn(int row)
{
int nowIndex = ui->tabWidget->currentIndex();
if(nowIndex==0){
QTableWidgetItem* item = ui->allTable->item(row,2);
//修改为已打卡
item->setText("是");
//插入时间
QDateTime currentTime = QDateTime::currentDateTime();
QString formattedTime = currentTime.toString("HH时mm分");
ui->allTable->setItem(row, 3, new QTableWidgetItem(formattedTime));
//修改标志位
workFlagMap[row] = true;
//发送信号,用于弹窗告知打卡成功!
//获取该行的姓名,卡号
QTableWidgetItem* itemName = ui->allTable->item(row,0);
QString employeeName = itemName->text();
QTableWidgetItem* itemIDCard = ui->allTable->item(row,1);
QString employeeIDCard = itemIDCard->text();
QStringList successSignInInfoList;
successSignInInfoList<<employeeName<<employeeIDCard;
emit signInReady(successSignInInfoList);
}
}
void Dialog::backHome(int row)
{
//插入时间
QDateTime currentTime = QDateTime::currentDateTime();
QString formattedTime = currentTime.toString("HH时mm分");
ui->allTable->setItem(row, 4, new QTableWidgetItem(formattedTime));
backFlagMap[row]=true;
//发送信号,用于弹窗告知下班成功!
//获取该行的姓名,卡号
QTableWidgetItem* itemName = ui->allTable->item(row,0);
QString employeeName = itemName->text();
QTableWidgetItem* itemIDCard = ui->allTable->item(row,1);
QString employeeIDCard = itemIDCard->text();
QStringList successBackInfoList;
successBackInfoList<<employeeName<<employeeIDCard;
emit backHomeReady(successBackInfoList);
}
//错误用卡
void Dialog::errorUseCard(int row)
{
QTableWidgetItem* itemName = ui->allTable->item(row,0);
QString employeeName = itemName->text();
QTableWidgetItem* itemIDCard = ui->allTable->item(row,1);
QString employeeIDCard = itemIDCard->text();
QStringList errorUseCardInfoList;
errorUseCardInfoList<<employeeName<<employeeIDCard;
//发送信号,用于弹窗告知错误用卡!
emit errorAction(errorUseCardInfoList);
}
//上班弹窗
void Dialog::signInDialog(QStringList successSignInInfoList)
{
QDialog *popup = new QDialog(this, Qt::Popup | Qt::FramelessWindowHint);
QString message = QString("%1,卡号%2上班打卡成功")\
.arg(successSignInInfoList[0]).arg(successSignInInfoList[1]);
QLabel *label = new QLabel(message, popup);
QVBoxLayout *layout = new QVBoxLayout(popup);
layout->addWidget(label);
popup->resize(200, 200);
popup->setStyleSheet("QDialog { background-color: white; }");
QTimer::singleShot(3000, popup, &QDialog::close);
popup->exec();
}
//下班弹窗
void Dialog::backHomeDialog(QStringList successSignInInfoList)
{
QDialog *popup = new QDialog(this, Qt::Popup | Qt::FramelessWindowHint);
QString message = QString("%1,卡号%2下班打卡成功")\
.arg(successSignInInfoList[0]).arg(successSignInInfoList[1]);
QLabel *label = new QLabel(message, popup);
QVBoxLayout *layout = new QVBoxLayout(popup);
layout->addWidget(label);
popup->resize(200, 200);
popup->setStyleSheet("QDialog { background-color: white; }");
QTimer::singleShot(3000, popup, &QDialog::close);
popup->exec();
}
//错误用卡弹窗
void Dialog::errorUseCardDialog(QStringList successSignInInfoList)
{
QDialog *popup = new QDialog(this, Qt::Popup | Qt::FramelessWindowHint);
QString message = QString("%1,卡号%2错误用卡")\
.arg(successSignInInfoList[0]).arg(successSignInInfoList[1]);
QLabel *label = new QLabel(message, popup);
QVBoxLayout *layout = new QVBoxLayout(popup);
layout->addWidget(label);
popup->resize(200, 200);
popup->setStyleSheet("QDialog { background-color: white; }");
QTimer::singleShot(3000, popup, &QDialog::close);
popup->exec();
}
//卡号label
void Dialog::updateLabel(QString idCard)
{
if(addBtnClickedFlag){
idCardInfoLabel->setText(idCard);
addBtnClickedFlag=false;
}
}
//页面切换
void Dialog::switchCurrentTabWidget()
{
int currentTabIndex = ui->tabWidget->currentIndex();
if(currentTabIndex){
ui->tabWidget->setCurrentIndex(0);
tabWidgetTimer->stop();
}
}
//ui界面初始化
void Dialog::myUiInit()
{
//对QTabWidget操作
//设置默认界面
ui->tabWidget->setCurrentIndex(0);
//对QTablWidget操作
//自适应操作
ui->allTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
//不可编辑
ui->allTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
//按行选择
ui->allTable->setSelectionBehavior(QAbstractItemView::SelectRows);
}
//更新实时时间
void Dialog::updateTimeOnLable()
{
QDateTime currentTime = QDateTime::currentDateTime();
QString formattedTime = currentTime.toString("yyyy年MM月dd日 HH时mm分ss秒");
ui->timeLable->setText(formattedTime);
}
//添加按钮
void Dialog::on_addBtn_clicked()
{
addBtnClickedFlag=true;
ui->tabWidget->setCurrentIndex(1);
QDialog *myDialog = new QDialog(this, Qt::Popup | Qt::FramelessWindowHint);
QLabel *labelName = new QLabel("请输入用户名:",myDialog);
QLabel *labelIDCard = new QLabel("请放上卡片,自动感应卡号:",myDialog);
idCardInfoLabel = new QLabel(myDialog);
QLineEdit *lineEdit = new QLineEdit(myDialog);
QPushButton *confirmButton = new QPushButton("确认", myDialog); // 确认按钮
QVBoxLayout *layout = new QVBoxLayout(myDialog);
layout->addWidget(labelName);
layout->addWidget(lineEdit);
layout->addWidget(labelIDCard);
layout->addWidget(idCardInfoLabel); // 添加第三个标签到布局中
layout->addWidget(confirmButton); // 添加确认按钮到布局中
// 连接确认按钮的clicked信号到一个槽函数,以处理用户点击事件
connect(confirmButton, &QPushButton::clicked, [=]() {
myDialog->accept();
QString addName = lineEdit->text();
QString addIDCard = idCardInfoLabel->text();
if(!addIDCard.isEmpty()){
QStringList addInfoList;
addInfoList<<addName<<addIDCard;
emit getAddInfoReady(addInfoList);
}
tabWidgetTimer->start(5000);
});
myDialog->resize(200, 200);
myDialog->setStyleSheet("QDialog { background-color: white; }");
myDialog->exec(); // 显示并执行弹窗的事件循环
}
//刷新按钮
void Dialog::on_reBtn_clicked()
{
int row = ui->allTable->rowCount();
for(;row>=0;row--){
ui->allTable->removeRow(row - 1);
}
myUiInit();
insertFlag = false;
}
//查询按钮
void Dialog::on_queryBtn_clicked()
{
ui->tabWidget->setCurrentIndex(1);
tabWidgetTimer->start(5000);
//int nowIndex = ui->tabWidget->currentIndex();
}
//删除按钮
void Dialog::on_delBtn_clicked()
{
//获取所选定的行
int selectedIndex = ui->allTable->currentRow();
if(selectedIndex!=-1){
ui->allTable->removeRow(selectedIndex);
bool removed = backFlagMap.remove(selectedIndex);
if (!removed){
qDebug()<<"fail to delete backFlag";
}
removed = workFlagMap.remove(selectedIndex);
if (!removed){
qDebug()<<"fail to delete workFlag";
}
}
}
3. 数据库部分
!!!!!!!这里没有实现!!!!!!!仅供参考!!!!!!!需要自行完善!!!!!!!注意线程操作安全!!!!!
EmployeeDataBase.h
#ifndef EMPLOYEEDATABASE_H
#define EMPLOYEEDATABASE_H
#include <QObject>
#include <QThread>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QDebug>
class EmployeeDatabase : public QObject
{
Q_OBJECT
public:
explicit EmployeeDatabase(QObject *parent = nullptr,const QString &filename = "employee.db");
signals:
//根据卡号查询数据库,找到了发出信号
void getemployeeInfoByIDcard(QStringList);
//扫描整张数据表,扫描完成发出信号
void employeeInfoReady(QStringList);
public slots:
//创建数据表
void createEmployeeTable();
//记录员工信息,将信息保存到数据库
void recordEmployeeInfo(const QStringList employeeList);
//根据卡号扫描数据库
void checkOneByIDcard(const QString IDcard);
//扫描整个数据库
void checkAllInfo();
};
#endif // EMPLOYEEDATABASE_H
EmployeeDataBase.cpp
#include "employeedatabase.h"
EmployeeDatabase::EmployeeDatabase(QObject *parent, const QString &filename) : QObject(parent)
{
QThread *thread = new QThread;
//connect(thread,&QThread::started,this,[filename,this](){
QSqlDatabase employeeDB = QSqlDatabase::addDatabase("QSQLITE","employee-database");
employeeDB.setDatabaseName(filename);
bool ok = employeeDB.open();
if(!ok){
qDebug()<<"faile to open"<<filename<<",err:"<<employeeDB.lastError().text();
return;
}
createEmployeeTable();
//});
//this->moveToThread(thread);
//thread->start();
}
//创建表
void EmployeeDatabase::createEmployeeTable()
{
QSqlDatabase employeeDB = QSqlDatabase::database("employee-database");
QSqlQuery query(employeeDB);
bool ok = query.exec("CREATE TABLE IF NOT EXISTS "
"employeeInfo("
"id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
"姓名 TEXT NOT NULL,"
"卡号 TEXT UNIQUE NOT NULL)");
if(!ok){
qDebug()<<"fail to creat table,err:"<<query.lastError().text();
return;
}
return;
}
//插入数据
void EmployeeDatabase::recordEmployeeInfo(const QStringList employeeList)
{
QSqlDatabase emplyeeDB = QSqlDatabase::database("employee-database");
QSqlQuery query(emplyeeDB);
query.prepare("INSERT INTO employeeInfo VALUES(?,?)");
query.bindValue(0,employeeList[0]);
query.bindValue(1,employeeList[1]);
bool ok = query.exec();
if(!ok){
qDebug()<<"fail to insert,err:"<<query.lastError().text();
return;
}
return;
}
//查询数据--查单条
void EmployeeDatabase::checkOneByIDcard(const QString IDcard)
{
QSqlDatabase emplyeeDB = QSqlDatabase::database("employee-database");
QSqlQuery query(emplyeeDB);
query.prepare("SELECT * FROM employeeInfo WHERE 卡号=?");
query.bindValue(0,IDcard);
bool ok = query.exec();
if(!ok){
qDebug()<<"fail to select,err:"<<query.lastError().text();
return;
}
QString employeeName = query.value(1).toString();
QString employeeIDcard = query.value(2).toString();
QStringList employeeInfoList;
employeeInfoList<<employeeName<<employeeIDcard;
emit getemployeeInfoByIDcard(employeeInfoList);
return;
}
//查询数据--查全部
void EmployeeDatabase::checkAllInfo()
{
QSqlDatabase emplyeeDB = QSqlDatabase::database("employee-database");
QSqlQuery query(emplyeeDB);
bool ok = query.exec("SELECT * FROM employeeInfo");
if(!ok){
qDebug()<<"fail to select,err:"<<query.lastError().text();
return;
}
while (query.next()) {
QString employeeName = query.value(1).toString();
QString employeeIDcard = query.value(2).toString();
QStringList employeeInfoList;
employeeInfoList<<employeeName<<employeeIDcard;
emit employeeInfoReady(employeeInfoList);
}
return;
}
五.项目源码文件以及arm编译后的文件链接
这里 我将源码和编译后的都放在百度网盘把,有需要的可以自行下载并完善,使用过程中遇到问题请回到第三条,看功能说明,同时也欢迎各位指出问题,我自己就知道很多bug了,但是这是课程设计,当时验收完我就没管了,所以轻点喷,发这个主要是了解一下QT的使用吧,将这个项目作为QT入门的小项目还不错,如果说没有串口或者开发板,就了解一下QT中的定时器呀,定时器时间到了就发出数据,就模拟了检测卡号的工作了,也就可以抛开硬件使用纯软学习QT,不多说了,直接使用一定一定要看第三条
链接:https://pan.baidu.com/s/17GvCqI3Ghi6UWv4bO11VEw?pwd=yuan
提取码:yuan
--来自百度网盘超级会员V5的分享