基于GEC6818的办公室打卡系统(QT开发)

一.硬件图片

二.UI界面设计


主要用到的是QPushButten、QTabWidget、QTableWidget、QLabel
UI界面仁者见仁吧。

三.效果演示及功能说明


  1. 后台不断读取卡号,检测到卡号可以做出相应的动作
  2. 对于已存在的用户可以进行打卡操作,并生成为时3秒的弹窗告知,分别是上班打卡成功,下班打卡成功以及用卡错误
  3. 可以对用户进行添加或者删除
  4. 由于开发板没有数据库,故没有开启数据库功能
  5. 这里我只进行了简单的存储,所以直接将插入了QTableWidget当中,有需要的可以在开发板中部署数据库或者写JSON配置文件,将信息进行存储,同样对数据的扫描也全都来自QTableWidget,所以掉电和刷新会丢失数据需要重新添加信息,我会给出我写的数据库部分,但是这里没用到。所以只有简单的打开数据库的操作,看一看了解一下就行。勿喷
  6. 刷新按钮模仿新一天的轮转
  7. 卡号是原始16进制,需要10进制可以做相应的操作,这里我就没做了
  8. 需要在打卡界面才能进行打卡操作
  9. 用户名为空也可以添加,这里开发板要输入用户名需要唤起键盘,如果觉得不合适的话,在代码中添加唤起键盘事件和检测用户名是不是为空就行了
  10. 串口号和波特率自行修改,我这里为了省事把串口号在初始化部分写死了!!!!!!!!!!一定要看看你的串口设备并修改!!!!!!!!!!

四.项目源码

  1. 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的分享

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值