Qt 驾校考试系统项目实现

一 登录界面

  • 创建项目的时候选择Dialog
  • 图片大小和背景框不匹配时,可以勾选scaledContents

1 正则表达式 验证用户密码和邮箱

复制代码
// \\w 是为了在 C++ 字符串中正确表示正则表达式中的 \w。如果你只写
 \w,C++ 会将其解释为一个转义字符,而不是正则表达式中的 \w
//正则表达式里面表示. ,就需要使用\.,进一步的,当前正则表达式是在字符串里面,需要加一个\来进行转义
//[a-zA-Z0-9\\-]+: 匹配一个字母(a-zA-Z)、数字(0-9)或连字符(-)。连字符 - 被转义为 
\\-,以确保它被当作普通字符而不是范围符号,
//, +表示前面字符出现一次或多次, +不需要转义字符


\[\]

cpp 复制代码
void DialogLogin::on_pushButtonLogin_clicked()
{
    //要求必须以字母开头,6-18个字符,\w表示匹配字母数字或下划线
    // \\w 是为了在 C++ 字符串中正确表示正则表达式中的 \w。如果你只写 \w,C++ 会将其解释为一个转义字符,而不是正则表达式中的 \w
    //正则表达式里面表示. ,就需要使用\.,进一步的,当前正则表达式是在字符串里面,需要加一个\来进行转义
    //[a-zA-Z0-9\\-]+: 匹配一个字母(a-zA-Z)、数字(0-9)或连字符(-)。连字符 - 被转义为 \\-,以确保它被当作普通字符而不是范围符号,
    //, +表示前面字符出现一次或多次, +不需要转义字符

    QRegExp mail("^[a-zA-Z][\\w]{5,17}@([a-zA-Z0-9\\-]+\\.)+[a-zA-Z]{2,6}$");

    bool res = mail.exactMatch(ui->lineEditLogin->text());
    if(!res){  //验证邮箱的合法性
        QMessageBox::warning(this,"提示","非法邮箱,请重新输入");
        ui->lineEditLogin->clear();
        ui->lineEditLogin->setFocus();

    }else{ //验证用户和密码的一致性
        QString code = ui->lineEditCode->text();
        QString user = ui->lineEditLogin->text();

        if(code.isEmpty()){
            QMessageBox::warning(this,"提示","密码不能为空");
        }

        //QString filename = FILENAME;
        QString filename = "I:/QT_PRACTICE/myExam22/account.txt";
        if(!filename.isEmpty()){
            //把文本编辑器的内容保存到文本
            QFile qfile(filename);
            qDebug() << filename;
            if(! qfile.open(QIODevice::ReadOnly | QIODevice::Text)){
                qDebug() << "文件打开失败";
                return;
            }

            QStringList codeList;
            QStringList userList;
            QTextStream stream(&qfile);
            while (!stream.atEnd()) {
                    QString line = stream.readLine();
                    // 分割邮箱和密码
                    QStringList list = line.split(',');
                    if (list.size() == 2) {
                        QString email = list.at(0);
                        QString password = list.at(1);

                        codeList.append(password);
                        userList.append(email);
                    }
              }

            qfile.close();

            bool succeedLabel = 0;
            for(int i = 0; i< codeList.count() ; i++){
                 if(code == codeList[i] && user == userList[i]){
                    QMessageBox::information(this,"提示","成功登录");
                    succeedLabel = 1;
                    break;
                 }
            }

            if(succeedLabel == 0){
                 QMessageBox::warning(this,"提示","用户名或密码错误");
                 ui->lineEditCode->clear();
                 ui->lineEditLogin->clear();
                 ui->lineEditLogin->setFocus();
            }
        }

    }
}

2 知识点

  • 知识点1 :只有第一次调用的时候需要设置起始点,之后就不需要了,否则位置坐标不对m_layout->addWidget(m_textEdit,0,0,1,10); //起始位置 0行,0列,行宽和列宽分别是1和10,后面调用 `` m_layout->addWidget(title,1,k);,`
  • 知识点2 :自定义组件我看有两种方式,一种是在头文件定义指针之后,在函数里面使用的时候,重新new一个对象,另一种是,头文件定义指针之后,cpp文件就直接用,不再new了。啥时候要new新的对象,啥时候不需要new呢?
    (a)头文件定义类对象指针,但是并没有分配内存空间,所以后面用的时候是必须要new m_textEdit = new QTextEdit(this);来进行空间的分配的,否则就成页指针了
    (b)至于什么时候new,那取决于是整个程序只有一个组件,还是有多个组件,如果是多个组件,那么在不同地方调用的时候,都需要new一个新的内存空间,但是如果只是一个组件,那就只需要在构造函数里面进行一次空间分配,然后后面函数就可以直接使用了


  • 知识点3 :什么时候需要定义信号,当前案例里面,自定义的pushButon按钮实现考试的提交,也是属于Qt按钮

    1.只有自己写的类才需要定义自己的信号,如果调用已有的组件,是不需要自己来写信号的

    2.QT内部组件会自动发送信号,不需要手动发送信号

  • 知识点4 :什么时候需要创建c++类文件,什么时候需要创建Qt设计师界面类,.cpp类文件的情况::Graphics View组件对应的类对象是QGraphicsView,是标准类对象,无法进行修改,需要自定义一个视图,去重载该类的鼠标事件

    !在这里插入图片描述(https://i-blog.csdnimg.cn/direct/a22cd8c5986449eaaa8a8784d0e3b392.png #pic_center =500x)

  • 知识点5 :如果要控制一个界面A运行完毕,关闭当前界面A然后打开另一个界面B,在main函数里面做如下实现,同时在界面A的相应确认打开界面B的位置添加done(Accepted);语句

  • 知识点6QVector<Question> Data; //存放题库 QVector<QButtonGroup*> btnGroup;,定义指针和不定义指针的情况有什么区别,调用的时候都是通过.来调用,而不是->,以下是查到的区别,对于动态分配内存这一点的理解是:前者调用的时候不需要使用new来为对象分配内存空间,后者需要

cpp 复制代码
private:
    QVector<Question> Data;  //存放题库
    QVector<QButtonGroup*> btnGroup;
cpp 复制代码
当前案例使用
         //判断是单选还是多选
        QButtonGroup *group = new QButtonGroup;
        if(Data[k].isSigleQuestion()){  //单选
            for(int i =0; i< Data[k].get_choiceNum(); i++){
                QRadioButton *btn = new QRadioButton;
                btn->setText(Data[k].quesChoices[i].first);
                group->addButton(btn);
                m_layout->addWidget(btn,rwoIndex + i + 1,k%eachLineShow_num);  //

            }
        }else{  //多选
            group->setExclusive(false);
            for(int i =0; i< Data[k].get_choiceNum(); i++){
                QCheckBox *checkBox = new QCheckBox;
                checkBox->setText(Data[k].quesChoices[i].first);
                group->addButton(checkBox);
                m_layout->addWidget(checkBox,rwoIndex + i + 1,k%eachLineShow_num);  //
            }
        }
        btnGroup.append(group);
bash 复制代码
            case READ_ANSER:
                if(line.at(0) == '!'){
                      line.remove(0,1);
                      //qDebug() << "答案:"<< line;
                      anse.append(line);

                      Question ques(titelLable, anse);
                      //qDebug() << "choiceLabel.size():"<< choiceLabel.size();
                      for(int i =0; i< choiceLabel.size(); i++){
                          QPair<QString, int> temp;
                            ques.quesChoices.push_back(QPair<QString, int>(choiceLabel[i],0));
                      }
                      Data.append(ques);
                }


cpp 复制代码
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    DialogLogin w;

    if(w.exec() == QDialog::Accepted){
        QDialogExamUI ed;
        ed.show();
        return a.exec();
    }
    return a.exec();
}

3 代码及文件创建 add->new->cpp,选择或者填入继承QDialog类,创建.cpp文件,注意,头文件要做相应的修改

  • 效果

"dialoglogin.h"

cpp 复制代码
#ifndef DIALOGLOGIN_H
#define DIALOGLOGIN_H

#include <QDialog>

QT_BEGIN_NAMESPACE
namespace Ui { class DialogLogin; }
QT_END_NAMESPACE

class DialogLogin : public QDialog
{
    Q_OBJECT

public:
    DialogLogin(QWidget *parent = nullptr);
    ~DialogLogin();

private slots:
    void on_pushButtonLogin_clicked();

    void on_pushButtonExit_clicked();

private:
    Ui::DialogLogin *ui;
};
#endif // DIALOGLOGIN_H
  • "qdialogexamui.h"
cpp 复制代码
#ifndef QDIALOGEXAMUI_H
#define QDIALOGEXAMUI_H

#include <QDialog>
#include <QTextEdit>
#include <QGridLayout>
#include <QVector>
#include "question.h"
#include <QCheckBox>
#include <QRadioButton>
#include <QButtonGroup>
#include <QAbstractButton>  //是 QRadioButton和 QCheckBox的父类
#include <QPushButton>
enum State{
    BEGIN,
    READ_TILTE,
    READ_CHOICE,
    READ_ANSER
};

class QDialogExamUI : public QDialog
{
    Q_OBJECT
public:
    QDialogExamUI(QWidget *parent = nullptr);
    ~QDialogExamUI();


private:
    QTextEdit *m_textEdit;  //显示考试内容
    QGridLayout *m_layout;  //布局管理器

    void initUI(); //初始化组件
    void initSlot();
    void initContent();  //解析文本内容
    void initShowContent();
    double getScore();

    QVector<Question> Data;  //存放题库
    QVector<QButtonGroup*> btnGroup;
    QPushButton *m_submitBtn;
    int eachLineShow_num = 10;

private slots: //槽函数
    void on_m_submitBtn_clicked(bool clicked);
};
#endif // QDIALOGEXAMUI_H
  • "question.h"
cpp 复制代码
#ifndef QUESTION_H
#define QUESTION_H
#include <QString>
#include <QStringList>
#include <QVector>
#include <QPair>
class Question
{
public:
     Question(const QString quesIndex, const QString quesAnser);
     Question();
    ~Question();
     Question(const Question &ques);  //拷贝构造函数
//     QVector<QPair<QString,QString>> quesChoices; // <QPair <选项索引,选项内容>>  考虑到绘制题目的时候不需要选项内容
     QVector<QPair<QString,int>> quesChoices;  // <QPair <选项索引,选项是否被选中>>
     bool isSigleQuestion();  //判断是否是单选
     QString get_quesIndex();
     QStringList get_quesAnser();
     int get_choiceNum();
private:
    QString quesIndex;     //题目编号
    QString quesAnser; //题目答案
    int choiceNum;         //选项数量
    bool is_single_ques;   //1 单选,0多选
};

#endif // QUESTION_H
  • "dialoglogin.cpp"
cpp 复制代码
#include "dialoglogin.h"
#include "ui_dialoglogin.h"
#include <QDebug>
#include <QMessageBox>
#include <QFile>

#define FILENAME "account.txt"
DialogLogin::DialogLogin(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::DialogLogin)
{
    ui->setupUi(this);
    setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); //关掉默认的右上角的叉叉
    setWindowTitle("驾校考试系统");
}

DialogLogin::~DialogLogin()
{
    delete ui;
}


void DialogLogin::on_pushButtonLogin_clicked()
{
    //要求必须以字母开头,6-18个字符,\w表示匹配字母数字或下划线
    // \\w 是为了在 C++ 字符串中正确表示正则表达式中的 \w。如果你只写 \w,C++ 会将其解释为一个转义字符,而不是正则表达式中的 \w
    //正则表达式里面表示. ,就需要使用\.,进一步的,当前正则表达式是在字符串里面,需要加一个\来进行转义
    //[a-zA-Z0-9\\-]+: 匹配一个字母(a-zA-Z)、数字(0-9)或连字符(-)。连字符 - 被转义为 \\-,以确保它被当作普通字符而不是范围符号,
    //, +表示前面字符出现一次或多次, +不需要转义字符

    QRegExp mail("^[a-zA-Z][\\w]{5,17}@([a-zA-Z0-9\\-]+\\.)+[a-zA-Z]{2,6}$");

    bool res = mail.exactMatch(ui->lineEditLogin->text());
    if(!res){  //验证邮箱的合法性
        QMessageBox::warning(this,"提示","非法邮箱,请重新输入");
        ui->lineEditLogin->clear();
        ui->lineEditLogin->setFocus();

    }else{ //验证用户和密码的一致性
        QString code = ui->lineEditCode->text();
        QString user = ui->lineEditLogin->text();

        if(code.isEmpty()){
            QMessageBox::warning(this,"提示","密码不能为空");
        }

        //QString filename = FILENAME;
        QString filename = "I:/QT_PRACTICE/myExam22/account.txt";
        if(!filename.isEmpty()){
            //把文本编辑器的内容保存到文本
            QFile qfile(filename);
            qDebug() << filename;
            if(! qfile.open(QIODevice::ReadOnly | QIODevice::Text)){
                qDebug() << "文件打开失败";
                return;
            }

            QStringList codeList;
            QStringList userList;
            QTextStream stream(&qfile);
            while (!stream.atEnd()) {
                    QString line = stream.readLine();
                    // 分割邮箱和密码
                    QStringList list = line.split(',');
                    if (list.size() == 2) {
                        QString email = list.at(0);
                        QString password = list.at(1);

                        codeList.append(password);
                        userList.append(email);
                    }
              }

            qfile.close();

            bool succeedLabel = 0;
            for(int i = 0; i< codeList.count() ; i++){
                 if(code == codeList[i] && user == userList[i]){
                    QMessageBox::information(this,"提示","成功登录");
                    succeedLabel = 1;
                    done(Accepted);
                    break;
                 }
            }

            if(succeedLabel == 0){
                 QMessageBox::warning(this,"提示","用户名或密码错误");
                 ui->lineEditCode->clear();
                 ui->lineEditLogin->clear();
                 ui->lineEditLogin->setFocus();
            }
        }

    }
}

void DialogLogin::on_pushButtonExit_clicked()
{
    this->close();
}
  • "qdialogexamui.cpp"
cpp 复制代码
#include "qdialogexamui.h"
#include <QFile>
#include <QMessageBox>
#include <QTextStream>
#include <QFont>
#include <QDebug>
#include <QLabel>
#include <QAbstractButton>
QDialogExamUI::QDialogExamUI(QWidget *parent):QDialog(parent)
{
      m_textEdit = new QTextEdit(this);
      m_layout = new QGridLayout(this);
      m_submitBtn = new QPushButton(this);

      initUI();
      initContent();
      initShowContent();
      initSlot();
}

QDialogExamUI::~QDialogExamUI()
{

}

void QDialogExamUI::initUI()
{
    //-----------------主窗体初始化----------------------------
    this->setMinimumSize(800, 600); // 设置窗体的最小大小为 800x600

    //-----------------布局管理器边界设置的初始化----------------------------
    m_layout->setMargin(10);
    m_layout->setSpacing(10);

    //-----------------文本编辑器----------------------------
//    //显示文本
//    m_textEdit->setMaximumWidth(200);
//    m_textEdit->setMaximumHeight(900);

    //设置显示文本的字体和大小
    QFont font = m_textEdit->font();  //先将字体取出来
    font.setPointSize(12); //字体
    font.setFamily("微软雅黑");
    m_textEdit->setFont(font);
    //m_layout->addWidget(m_textEdit,0,1);
    m_layout->addWidget(m_textEdit,0,0,1,10); //起始位置 0行,0列,行宽和列宽分别是1和10


}

void QDialogExamUI::initSlot()
{
    //-----------------考试提交按钮----------------------------
    connect(m_submitBtn,SIGNAL(clicked(bool)),this,SLOT(on_m_submitBtn_clicked(bool)));

}

void QDialogExamUI::initShowContent()
{
    QFont font = m_textEdit->font();  //先将字体取出来
     font.setPointSize(12); //字体
     font.setFamily("微软雅黑");
     m_textEdit->setFont(font);

    //-----------------显示题目和选项按钮----------------------------

    int maxChoiceNum =0;
    int rwoIndex =1;
    if(eachLineShow_num > Data.size()){
        eachLineShow_num = Data.size();
    }

    for(int k = 0; k < Data.size(); k++){

        if(k % eachLineShow_num == 0)
        {
            rwoIndex += maxChoiceNum +1;
        }


        //获取每行显示题目数量的最大题目数
        for(int i =0; i< eachLineShow_num; i++){
            if(Data[i].get_choiceNum() > maxChoiceNum){
                maxChoiceNum = Data[i].get_choiceNum();
            }
        }

        //显示标题
         QLabel *title = new QLabel(this);
         title->setText("第" + Data[k].get_quesIndex()+ "题");
         title->setFont(font);
//         m_layout->addWidget(title,0,0,1,k);
         m_layout->addWidget(title,rwoIndex,k%eachLineShow_num);  //

        //显示选项

         //判断是单选还是多选
        QButtonGroup *group = new QButtonGroup;
        if(Data[k].isSigleQuestion()){  //单选
            for(int i =0; i< Data[k].get_choiceNum(); i++){
                QRadioButton *btn = new QRadioButton;
                btn->setText(Data[k].quesChoices[i].first);
                group->addButton(btn);
                m_layout->addWidget(btn,rwoIndex + i + 1,k%eachLineShow_num);  //

            }
        }else{  //多选
            group->setExclusive(false);
            for(int i =0; i< Data[k].get_choiceNum(); i++){
                QCheckBox *checkBox = new QCheckBox;
                checkBox->setText(Data[k].quesChoices[i].first);
                group->addButton(checkBox);
                m_layout->addWidget(checkBox,rwoIndex + i + 1,k%eachLineShow_num);  //
            }
        }
        btnGroup.append(group);

    }


    //-----------------考试提交按钮----------------------------
    m_submitBtn->setText("提交");
    m_submitBtn->setFont(font);
    m_layout->addWidget(m_submitBtn,rwoIndex+maxChoiceNum+1,eachLineShow_num-1);
}

double QDialogExamUI::getScore()
{
    double socre = 0;
    for(int k= 0; k <Data.size(); k++){
        QList<QAbstractButton*> buttons = btnGroup[k]->buttons();
        QStringList ans = Data[k].get_quesAnser();
        //判断是单选还是多选
        if(Data[k].isSigleQuestion()){
            for(int i =0; i <buttons.size();i ++){
                if(buttons[i]->isChecked() && buttons[i]->text() == ans[0]){
                    socre += 10;
                    break;
                }
            }
        }else{
            int ans_num = ans.size();
            int counter = 0;
            for(int i =0; i <buttons.size();i ++){
                //遍历答案进行对比
                if(buttons[i]->isChecked()){
                    for(int a =0; a <ans_num;a ++){
                      //  qDebug() <<"buttons[i]->text() = " << buttons[i]->text() << ",a = " << a << ",ans[a] =  " << ans[a];
                        if(buttons[i]->text() == ans[a]){
                            counter += 1;
                            break;
                        }
                   }
              }
            }

            if(counter == ans_num){
                socre += 10;
            }
        }
    }

    return socre;
}

void QDialogExamUI::on_m_submitBtn_clicked(bool clicked)
{
    bool ret = QMessageBox::information(this,"提示","是否确认提交",QMessageBox::Ok | QMessageBox::Cancel);
    qDebug() << "ret = " << ret;
    bool done = true;
    if(ret){
        //判断是否题目没有完成
        for(int k= 0; k <Data.size(); k++){
            QAbstractButton *checkedButton = btnGroup[k]->checkedButton();
            if(!checkedButton){
                QMessageBox::warning(this,"提示","还有选项为完成!",QMessageBox::Ok);
                done = false;
                break;
            }
        }

        if(done){
            double score = getScore();
            bool ret2 =QMessageBox::information(this,"提示","您的考试分数是:" + QString::number(score) + ",是否退出考试?",QMessageBox::Ok,QMessageBox::NoButton);
            if(ret2){
                this->close();
            }
        }

    }
}

void QDialogExamUI::initContent()
{
    //当前函数,读取考题内容
    QString filename = "I:/QT_PRACTICE/myExam22/exam.txt";
    QFile file(filename);
    if(! file.open( QIODevice::ReadOnly | QIODevice::Text)){
        QMessageBox::warning(this,"警告","文件打开失败");
        return;
    }

    State currentState = State::BEGIN;
    bool readNew = true;
    QString line;
    QString titelLable;
    QStringList choiceLabel;
    QString anse;

    QTextStream stream(&file);
    stream.setCodec("UTF-8");

    while(1){
        if(!readNew){
             readNew = true;
        }else{
            if(stream.atEnd()){
                break;
            }
            line = stream.readLine();
        }

        if(line.isEmpty()) continue;
        if(line.at(0) == "#") continue;

        //状态机将题目存储进题库
            switch(currentState){
            case BEGIN:
                titelLable.clear();
                choiceLabel.clear();
                anse.clear();
                //开始状态要求必须是题目
                if(line.at(0) == 'Q'){
                    currentState = State::READ_TILTE;
                    readNew = false;
                }
                else{
                    QMessageBox::warning(this,"警告","BEGIN, 题库格式错误");
                }
                break;

            case READ_TILTE:
                if(line.at(0) == 'Q'){
                    line.remove(0,1);
                    m_textEdit->append(line);

                    titelLable.append(line.split('.').first());
                   // qDebug() << "题目:" <<line.split('.').first()<< "内容" << line;

                }else if(line.at(0) == '.'){
                    currentState = State::READ_CHOICE;
                    readNew = false;
                }else{
                    QMessageBox::warning(this,"警告","READ_TILTE, 题库格式错误");
                }
                break;

            case READ_CHOICE:
                if(line.at(0) == '.'){
                    line.remove(0,1);
                    m_textEdit->append(line);
                    choiceLabel.append(line.at(0));
                    //qDebug() << "选项:" <<line.at(0) << "内容" << line;
                }else if(line.at(0) == '!'){
                    currentState = State::READ_ANSER;
                    readNew = false;
                }else{
                    QMessageBox::warning(this,"警告","READ_CHOICE, 题库格式错误");
                }
                break;
            case READ_ANSER:
                if(line.at(0) == '!'){
                      line.remove(0,1);
                      //qDebug() << "答案:"<< line;
                      anse.append(line);

                      Question ques(titelLable, anse);
                      //qDebug() << "choiceLabel.size():"<< choiceLabel.size();
                      for(int i =0; i< choiceLabel.size(); i++){
                          QPair<QString, int> temp;
                            ques.quesChoices.push_back(QPair<QString, int>(choiceLabel[i],0));
                      }
                      Data.append(ques);
                }
                else if(line.at(0) == 'Q'){
                    currentState = State::BEGIN;
                    readNew = false;
                    m_textEdit->append("\n");
                }else{
                    QMessageBox::warning(this,"警告","READ_ANSER, 题库格式错误");
                }
                break;
            }

        }

    file.close();
}
  • "question.cpp"
cpp 复制代码
#include "question.h"

Question::Question(const QString quesIndex, const QString quesAnser)
{
    this->quesIndex = quesIndex;
    this->quesAnser = quesAnser;
    this->choiceNum = 0;
    quesChoices.clear();


    QStringList list = quesAnser.split("");
    // 移除第一个空字符串
    if (!list.isEmpty() && list.first().isEmpty())  {
        list.removeFirst();
    }
    for(int i =0; i< list.size();i ++){
        if(list[i] == "" || list[i] == " "){
            list.removeAt(i);
        }
    }


    if(list.size() > 1){
        this->is_single_ques = false;
    }else if(list.size() == 1)
    {
         this->is_single_ques = true;
    }


}

Question::Question()
{

}

Question::~Question()
{

}

Question::Question(const Question &ques)
{
    this->quesIndex = ques.quesIndex;
    this->quesAnser = ques.quesAnser;
    this->is_single_ques = ques.is_single_ques;
    this->choiceNum = ques.choiceNum;

//    // 分配足够的内存空间
//    this->quesChoices.resize(ques.quesChoices.size());
//    for(int i =0;i < ques.quesChoices.size(); i++){
//        this->quesChoices[i].first = ques.quesChoices[i].first;
//        this->quesChoices[i].second = ques.quesChoices[i].second;
//    }
    this->quesChoices = ques.quesChoices;
}

bool Question::isSigleQuestion()
{
    return is_single_ques;
}

QString Question::get_quesIndex()
{
    return quesIndex;
}

QStringList Question::get_quesAnser()
{
    QStringList list = quesAnser.split("");
    // 移除第一个空字符串
    if (!list.isEmpty() && list.first().isEmpty())  {
        list.removeFirst();
    }

    for(int i =0; i< list.size();i ++){
        if(list[i] == "" || list[i] == " "){
            list.removeAt(i);
        }
    }

    return list;
}

int Question::get_choiceNum()
{
    this->choiceNum = quesChoices.size();
    return this->choiceNum;
}
相关推荐
Scott9999HH8 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社9 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海9 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖10 小时前
JDK 26 新特性详解
java·开发语言
马优晨11 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区13 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大13 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai13 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜14 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa14 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio