英语太差怎么办,快来写一个“百词斩”软件给自己背单词

前言

虽然这个项目整体看起来不是很复杂,但是也有涉及到很多小细节。本想做完后在写博客的,但是做着做着怕一下忘了,所以还是边写边做。其实博客写多了就会发现,把项目的每个细节都写在一篇博客里是很费时间,一般来说只要自己把项目的重难点记清楚就行了。

1、功能演示

前方多图预警!


1.1、注册界面
在这里插入图片描述
1.2、登陆界面
在这里插入图片描述
1.3、学习模式
在这里插入图片描述

1.4、 背单词界面
在这里插入图片描述
1.5、更换下一个单词界面(点击我要记单词,即可切换下一单词)
在这里插入图片描述
1.6、加入生词本
在这里插入图片描述
1.7、已存在生词本
在这里插入图片描述

1.8、生词本界面

在这里插入图片描述
1.9、移出生词本

在这里插入图片描述
1.10、没有你不会的
在这里插入图片描述
1.11、数据库中保存的单词量(目前有1200多条)
在这里插入图片描述

2、数据库创建

数据库结构如下,不是很复杂,就三个表。
用户表: 记录用户的账号、密码
单词表: 记录单词的英文、中文、英标、例句、发音等信息
生词表: 记录用户id和单词id,表示用户有哪些单词是不会的
在这里插入图片描述

2.1 用户数据表

2.2 单词数据表

数据库来源,由于一款单词软件的单词通常不能太少,但是自己手敲又太慢了,我的数据库是在网上找的公开的,需要的小伙伴可以点击这里
下面给出了部分代码,可以看到这个作者已经把相关的数据表信息列出来的。

//数据表创建语句
DROP TABLE IF EXISTS `word`;
CREATE TABLE `word` (
  `englishWord` varchar(512) NOT NULL,
  `pa` varchar(512) DEFAULT NULL,
  `chineseWord` varchar(512) DEFAULT NULL,
  `englishInstance1` varchar(512) DEFAULT NULL,
  `chineseInstance1` varchar(512) DEFAULT NULL,
  `englishInstance2` varchar(512) DEFAULT NULL,
  `chineseInstance2` varchar(512) DEFAULT NULL,
  `collect` int(4) DEFAULT NULL,
  `pron` varchar(512) DEFAULT NULL,
  PRIMARY KEY (`englishWord`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

//向数据表插入数据:
INSERT INTO `word` VALUES ('abnormal', 'æbˈnɔ:ml', '反常的,异常的;不规则的;变态的,畸形的;邪门儿;', 'The abnormal activity of tyrosine protein kinase was the cause with endometrium abnormal growth.', '提示rrPK活性异常是引起子宫内膜异常增生的原因.', 'The 114 cases with epileptic wave accounted for 57.9 % of the total abnormal EEG, 83 ( 42.1 % ) caseswere atypical abnormal.', '异常脑电图中?样波114,占异常的57.9%, 非特异性异常83( 42.1% ) .', '0', 'http://res.iciba.com/resource/amp3/oxford/0/e1/42/e142d564aae5adeb3eab781855dc87b2.mp3');
INSERT INTO `word` VALUES ('abolish', 'əˈbɒlɪʃ', '废除,废止;取消,革除;消灭;撤销;', 'Related departments will also abolish the ultra vires to clear out the charges.', '相关部门还将清理取消越权出台的收费.', 'The senate vote to abolish the death penalty.', '立法机关投票决定废除死刑.', '0', 'http://res.iciba.com/resource/amp3/oxford/0/aa/f6/aaf624f7f627d0c77b67324afc845753.mp3');
INSERT INTO `word` VALUES ('abrupt', 'əˈbrʌpt', '突然的,意外的;无理的,唐突的;不连贯的;陡峭的;', 'When they across a the Alps, 3 engine the plane are abrupt flameout.', '当他们飞越阿尔卑斯山时, 飞机的3个引擎突然熄火.', 'The beetles made an abrupt turn either to the left or the right.', '甲虫突然转向既不是左又不是右.', '0', 'http://res.iciba.com/resource/amp3/oxford/0/d9/a8/d9a85767b670f3bb01398d33a5dd71f3.mp3');
INSERT INTO `word` VALUES ('absurd', 'əbˈsɜ:d', '荒谬的;荒唐的;无理性的,杂乱无章的;荒诞主义的,荒诞的;', 'There is only one step from sublimity to absurd, but no way from absurd to sublimity.', '78从庄严到可笑仅一步之隔, 但想从可笑庄严却无路可行.', 'Life is absurd, and man himself is absurd.', '生活是荒诞的, 人本身也是荒诞的.', '0', 'http://res.iciba.com/resource/amp3/0/0/65/99/65997b466ba894a5f6d08718370ebb33.mp3');
INSERT INTO `word` VALUES ('abundance', 'əˈbʌndəns', '丰富,充裕;大量,极多;盈余;丰度;', 'We build abundance computers to hold abundance information, to produce abundance copies, but HAs less communication.', '咱们生产更多的计算机用于存储更多的消息和制造更多的拷贝, 而相互间的沟通与沟通却愈来愈少.', 'We HAs abundance experts, but abundance problems abundance medicine, but less wellness.', '咱们的行家愈来愈多, 疑难题目却也日渐渐增;药品越吃越多, 康健却每况愈下.', '0', 'http://res.iciba.com/resource/amp3/oxford/0/94/58/945864aa28a2115cc9e07bcd1439756c.mp3');
INSERT INTO `word` VALUES ('academy', 'əˈkædəmi', '专科学校;学会,学院;一般的高等教育;私立学校,学术团体;', 'Academy academy says babies should should sleep on their backs.', '儿科学会表示婴儿应该仰睡.', 'The Academy academy says babies should sleep on their bedsbacks.', '儿科学会指出,婴儿应该仰睡.', '0', 'http://res.iciba.com/resource/amp3/oxford/0/b4/be/b4be4abf9aa31364851ac0ba7c75b78f.mp3');
INSERT INTO `word` VALUES ('accessory', 'əkˈsesəri', '附件;(衣服的)配饰;从犯;妇女饰品;', 'For example case accessory . Expatiate application and realization of GA in accessory processing craftwork decision - making.', '并以箱体类零件为例, 阐述遗传算法在零件加工工艺决策过程中的应用和实现.', 'We are also supply all kinds of billiard accessory parts and accessory kits.', '同时我们也供应各种桌球配件及配件包.', '0', 'http://res.iciba.com/resource/amp3/oxford/0/03/23/03238b532e20a2451c3ed9118812e1bb.mp3');
INSERT INTO `word` VALUES ('accommodate', 'əˈkɒmədeɪt', '容纳;使适应;向…提供住处;帮忙;', 'The eye can accommodate itself to different distances.', '眼睛能自行调适各种不同距离.', 'This hotel can accommodate 6 0 0 guests.', '这个旅馆能接待六百位客人住宿.', '0', 'http://res.iciba.com/resource/amp3/oxford/0/46/e5/46e5a103ceefbbde2cd009562a26fc4b.mp3');
INSERT INTO `word` VALUES ('accord', 'əˈkɔ:d', '协议;条约;(色彩的)协调;和解协议;', 'Schools are growing increasingly aware that the Bologna accord will herald unprecedented changes.', '各学校越来越多地意识到,《博洛尼亚协定》将预示着空前的改变.', 'They came to an accord that profits should be shared equally.', '他们达成协议,收益由大家均分.', '0', 'http://res.iciba.com/resource/amp3/0/0/17/ee/17eec3190b081a1c237f673f39d821a9.mp3');
INSERT INTO `word` VALUES ('acknowledge', 'əkˈnɒlɪdʒ', '承认;鸣谢;对…打招呼;告知已收到;', 'Even , even though many of them acknowledge acknowledge the hundred 100 days\'mark an arbitrary number.', '他们其中很多人都承认给这100天的政绩打的分是个武断的数字.', 'The critic should acknowledge the complexity and inherent ambiguity of the policymaker\'s choices.', '评论者应该承认决策者作出抉择的复杂性及难以避免的缺乏鲜明性.', '0', 'http://res.iciba.com/resource/amp3/oxford/0/40/f1/40f11b8dce2228348f379f34ba469974.mp3');
INSERT INTO `word` VALUES ('acquaint', 'əˈkweɪnt', '使熟悉;使认识;把某事通知某人,告知;', 'They made specific investigations to acquaint themselves with the needs of the rural market.', '诺基亚手机销售量的下降是现在手机市场十分低迷的表现.', 'Why she stares at me as if she is acquaint with me.', '为什么她看着我仿佛她认识我似的.', '0', 'http://res.iciba.com/resource/amp3/oxford/0/b0/04/b004fbf4e90db26b0e831abdcf4021fd.mp3');
INSERT INTO `word` VALUES ('acquisition', 'ˌækwɪˈzɪʃn', '获得;购置物;获得物;收购;', 'One is ...

但是和我想要的不太一样,我的项目中需要单词id作为主键,因此有必要修改这些代码。首先是修改数据表生成语句。

扫描二维码关注公众号,回复: 14774408 查看本文章
//修改了数据表的名称,加入了id并将其设置为自动增长的主键
DROP TABLE IF EXISTS `words`;
CREATE TABLE `words` (
	`id` int(10) primary key auto_increment,
  `englishWord` varchar(512) NOT NULL,
  `pa` varchar(512) DEFAULT NULL,
  `chineseWord` varchar(512) DEFAULT NULL,
  `englishInstance1` varchar(512) DEFAULT NULL,
  `chineseInstance1` varchar(512) DEFAULT NULL,
  `englishInstance2` varchar(512) DEFAULT NULL,
  `chineseInstance2` varchar(512) DEFAULT NULL,
  `collect` int(4) DEFAULT NULL,
  `pron` varchar(512) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

由于插入了id作为主键,因此我还需要修改插入语句,这意味着要修改1200多条sql语句,这里我利用python进行了相关的修改操作,代码不长,代码和修改后的部分语句如下:

#批量修改sql语句
gt_path_1 = r'C:\\Users\\Lenovo\\Desktop\\sql.txt'   # 被替换的.txt
gt_path_2 = r'C:\\Users\\Lenovo\\Desktop\\sql2.txt' 	# 替换后的.txt
s = []  # 存储
f1 = open(gt_path_1,'r',encoding= 'UTF-8')   #读文件
f2 = open(gt_path_2,'w',encoding= 'UTF-8')   #写文件
count = 1;
for line in f1:
    print(line);
    print(line[27]);
    str1 = "";
    for i in range(0,17,1):    #作用,向word字段后追加s
        str1+=line[i];
    str1+="s";
    for i in range(17,27,1):   #作用,将id字段插入到该语句中
        str1+=line[i];
    str1+="'";
    str1+=str(count);          #作用,将id字段加1
    str1 += "',";
    count+=1;
    for i in range(27,len(line),1):   #作用,将后面的内容复制进来
        str1+=line[i];
    f2.writelines(str1);
    s.append(line)         #继续读取下一行
f1.close()
f2.close()
INSERT INTO `words` VALUES ('1','abnormal', 'æbˈnɔ:ml', '反常的,异常的;不规则的;变态的,畸形的;邪门儿;', 'The abnormal activity of tyrosine protein kinase was the cause with endometrium abnormal growth.', '提示rrPK活性异常是引起子宫内膜异常增生的原因.', 'The 114 cases with epileptic wave accounted for 57.9 % of the total abnormal EEG, 83 ( 42.1 % ) caseswere atypical abnormal.', '异常脑电图中?样波114例,占异常的57.9%, 非特异性异常83例 ( 42.1% ) .', '0', 'http://res.iciba.com/resource/amp3/oxford/0/e1/42/e142d564aae5adeb3eab781855dc87b2.mp3');
INSERT INTO `words` VALUES ('2','abolish', 'əˈbɒlɪʃ', '废除,废止;取消,革除;消灭;撤销;', 'Related departments will also abolish the ultra vires to clear out the charges.', '相关部门还将清理取消越权出台的收费.', 'The senate vote to abolish the death penalty.', '立法机关投票决定废除死刑.', '0', 'http://res.iciba.com/resource/amp3/oxford/0/aa/f6/aaf624f7f627d0c77b67324afc845753.mp3');

这些SQL语句都执行完毕之后就可以创建单词数据表了,效果如下所示,可以看到花了一分多钟的时间才把这1200条语句保存到数据库中。
在这里插入图片描述

2.3 生词本数据表

在生词本数据表中,由于不同用户不会的单词可能相同也可能不同,因此我们将用户id和单词id共设为主键,SQL语句如下:

 //主键设置两个是因为每个用户不会的单词都不一样
CREATE TABLE Unword (
UserID varchar(20),
id int,
primary key(UserID,id),   
foreign key (UserID) REFERENCES Usertable(UserID),
foreign key (id) REFERENCES words(id)
)

补充:

MYSQL数据库的排序规则:utf8_general_ci

如果账号密码都是对的,但是数据库连接不上,可以试着重启一下云服务器。

2.4 用户类、单词类

创建好数据库之后,我们还需要再qt文件中创建用户类和单词类。项目的名称我取的是Words,这个无所谓。
用户类的头文件user.h如下:

#ifndef USER_H
#define USER_H
#include <QString>

class user
{
    
    
public:
    user(QString My_user,QString My_password);  //用户输入账号密码
    QString GetUserID();  //获取用户id
    QString GetUserPassword();  //获取用户密码
    QString ChangeUserIno(QString My_user,QString My_password);  //修改用户信息
private:
    QString UserID;  //用户账户
    QString UserPassword;  //用户密码
};

#endif // USER_H

单词类的头文件words.h如下:

#ifndef WORDS_H
#define WORDS_H
#include <QString>

class words
{
    
    
public:
    words(int,QString,QString);
    words(QString);
    int GetID();           //获取单词ID
    QString GetEnglish();  //获取单词,返回单词
    QString GetChinese();  //获取中文意思,返回中文意思
private:
    QString English;
    QString Chinese;
    int id;
};

#endif // WORDS_H

3、注册、登陆界面搭建

关于软件图标如何设置问题可以在我之前的文章中QT成长笔记(一)找到,这里就不赘述了。
在这里插入图片描述
3.1 注册界面
注册界面我是通过创建ui文件来实现的,ui界面如下图所示:
在这里插入图片描述
register.h和register.cpp的代码如下,其中有两个槽函数和一个信号。构造函数创建的同时,会生成注册对象和数据库对象。点击注册按钮首先会获取当前控件中的数据,并将其传递给用户对象,然后数据库对象通过调用自身的成员函数,将该用户对象的账号密码发送至数据库进行校验,校验成功即注册成功,否则注册失败。点击返回登录按钮当前窗口会发送一个信号,由于登录按钮会一直等待连接,一接到注册窗口传来的信号便会实现窗口的切换。

//register.h
#ifndef SIGNUP_H
#define SIGNUP_H

#include <QWidget>
#include <QMessageBox>
#include "database.h"
#include "user.h"
#include "register.h"
#include "function.h"
#include <QPainter>
#include <QString>


namespace Ui {
    
    
class SignUp;
}

class SignUp : public QWidget
{
    
    
    Q_OBJECT

public:
    explicit SignUp(QWidget *parent = nullptr);
    ~SignUp();

private slots:
    void on_pushButton_2_clicked();   //登录按钮

    void on_btnregister_clicked();   //注册按钮
protected:
    //绘图事件函数
    void paintEvent(QPaintEvent *event);

private:
    Ui::SignUp *ui;
    Database *data;
};

#endif // SIGNUP_H

//register.cpp
#include "register.h"
#include "ui_register.h"
#include<QMessageBox>
Register::Register(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Register)
{
    
    
    ui->setupUi(this);
    data = new Database;
    this->setWindowTitle("百词斩");  //设置标题
    this->setFixedSize(600,600);     //设置窗口大小
}

Register::~Register()
{
    
    
    delete ui;
    delete data;
}

void Register::on_btnregister_clicked()  //注册按钮
{
    
    
    if(ui->lineUser->text()==""||ui->linePassword->text()==""||ui->linePassword2->text()=="")
    {
    
    
        QMessageBox::warning(this,"错误!","所填内容不能为空!");
        return;
    }

    if(ui->linePassword->text()==ui->linePassword2->text())
    {
    
    
        user user1(ui->lineUser->text(),ui->linePassword->text());
        if(data->Register(user1))  //Register返回的是用户是否成功注册
        {
    
    
            QMessageBox::information(this,"提示","注册成功");
        }
        else{
    
    
            QMessageBox::warning(this,"注册失败","该用户名已被注册");
        }
    }
    else{
    
    
        QMessageBox::warning(this,"错误","两次密码输入有误");
    }

}

void Register::on_ptnreturn_clicked()    //返回登陆按钮
{
    
    
    emit return_signup();    //发送返回
    this->close();
}

void Register::paintEvent(QPaintEvent *event)
{
    
    
    // 给子窗口画背景图
    QPainter p(this);    //创建画家对象
    QPixmap pixmap(":/image/image/blackgroud1.PNG");  //获取背景图路径
    p.drawPixmap(0,0,this->width(),this->height(),pixmap);  //绘制背景图
}

和注册功能类似,登录功能的具体代码如下:

//signup.h
#ifndef SIGNUP_H
#define SIGNUP_H

#include <QWidget>
#include <QMessageBox>
#include "database.h"
#include "user.h"
#include "register.h"
#include "function.h"
#include <QPainter>
#include <QString>


namespace Ui {
    
    
class SignUp;
}

class SignUp : public QWidget
{
    
    
    Q_OBJECT

public:
    explicit SignUp(QWidget *parent = nullptr);
    ~SignUp();

private slots:
    void on_pushButton_2_clicked();   //登录按钮

    void on_btnregister_clicked();   //注册按钮
protected:
    //绘图事件函数
    void paintEvent(QPaintEvent *event);

private:
    Ui::SignUp *ui;
    Database *data;
};

#endif // SIGNUP_H

//signup.cpp
#include "signup.h"
#include "ui_signup.h"
#include <QFile>
SignUp::SignUp(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SignUp)
{
    
    
    ui->setupUi(this);
    data = new Database;
    this->setWindowTitle("百词斩");
    this->setFixedSize(600,600);
}

SignUp::~SignUp()
{
    
    
    delete ui;
    delete data;
}

void SignUp::on_pushButton_2_clicked()
{
    
    
    user user1(ui->lineUser->text(),ui->linePassword->text());
    QString user_id = user1.GetUserID();
   if(data->Signup(user1))
   {
    
    
       //将用户id写到配置文件中,以便于生词本功能使用
       QFile file("user_id.txt");
       file.open(QIODevice::ReadWrite | QIODevice::Text);  //这两行的目的是判断文件是否存在,不存在则创建一个  不已追加的形式进行写入
       file.write(user_id.toUtf8());
       file.close();

       //显示功能界面
       function *fun = new function;
       fun->show();
       this->close();
   }
   else{
    
    
       QMessageBox::warning(this,"错误","登陆失败!用户名或密码不正确或用户名不存在!");
   }
}

void SignUp::on_btnregister_clicked()   //注册按钮
{
    
    
    Register *reg = new Register;    //创建注册对象,然后等待信号连接
    connect(reg,&Register::return_signup,[=](){
    
    
        this->show();   //显示登陆界面
    });
    reg->show();
    this->close();
}

void SignUp::paintEvent(QPaintEvent *event)
{
    
    
    // 给子窗口画背景图
    QPainter p(this);    //创建画家对象
    QPixmap pixmap(":/image/image/blackgroud1.PNG");  //获取背景图路径
    p.drawPixmap(0,0,this->width(),this->height(),pixmap);  //绘制背景图
}

4、数据库连接

要连接数据库首先得在pro文件中引入数据库模块,如下图所示:
在这里插入图片描述
然后可以试着创建一个database.h和database.cpp文件来试着连接数据库。
一般会用到以下三个头文件:

#include <QSqlError>
#include <QSqlQuery>  //对数据库进行增删改查的头文件
#include <QSqlRecord>

然后试着输入如下代码,测试能否成功连接mysql数据库:

void Database::ConnectSql()  //连接数据库函数
{
    
    
    //打印QT支持的数据库驱动
    qDebug()<<QSqlDatabase::drivers();

    //获取数据库对象
    db=QSqlDatabase::addDatabase("QMYSQL");
    //配置数据库
    db.setHostName("127.0.0.1");   //我们要链接的数据库的ip地址
    db.setUserName("root");        //MYSQL数据库的登录名
    db.setPassword("123456");      //MYSQL数据库的密码
    db.setDatabaseName("Words");        //数据库名称
    //打开数据库
    if(db.open())
    {
    
    
        qDebug()<<"连接数据库成功";
        return;
    }
    else{
    
    
        qDebug()<<"连接数据库失败";
        return;
    }
    QSqlQuery query;  //如果当前项目只有一个QSqlDatabase对象,则默认绑定,如果有两个及以上,则query()括号里要填具体的名字

    //query.exec("create table student(id int ...)");   创建表
    //query.exec("insert into student(id,name,age,score) values(1,'Tony,18,59')");  插入数据
}

如果连接不上,可以查看这篇文章,解决QT连不上SQL的问题。

完整的database.h和database.cpp代码如下,光靠我给出的注释应该就能理解了:

#ifndef DATABASE_H
#define DATABASE_H

#include <QDebug>
#include <QString>
#include <QMessageBox>
#include <QSqlError>
#include <QSqlQuery>  //对数据库进行增删改查的头文件
#include "user.h"
#include "words.h"
#include <QSqlRecord>
class Database
{
    
    
public:
    Database();
    void ConnectSql();  //连接数据库
    bool Signup(user myuser);  //登陆判断,可以登录返回true,失败返回FALSE
    bool Register(user myuser);  //注册新用户   注册成功返回true,失败返回false
    QString Chaxun(words words1);  //查询单词
private:
    QSqlDatabase db;  //数据库对象
};

#endif // DATABASE_H

#include "database.h"

Database::Database()
{
    
    
    ConnectSql();
}

void Database::ConnectSql()
{
    
    
    //打印QT支持的数据库驱动
    qDebug()<<QSqlDatabase::drivers();

    //获取数据库对象
    db=QSqlDatabase::addDatabase("QMYSQL");
    //配置数据库
    db.setHostName("127.0.0.1");   //我们要链接的数据库的ip地址
    db.setUserName("root");        //MYSQL数据库的登录名
    db.setPassword("123456");      //MYSQL数据库的密码
    db.setDatabaseName("Words");        //数据库名称
    //打开数据库
    if(db.open())
    {
    
    
        qDebug()<<"连接数据库成功";
        return;
    }
    else{
    
    
        qDebug()<<"连接数据库失败";
        return;
    }
    QSqlQuery query;  //如果当前项目只有一个QSqlDatabase对象,则默认绑定,如果有两个及以上,则query()括号里要填具体的名字

    //query.exec("create table student(id int ...)");   创建表
    //query.exec("insert into student(id,name,age,score) values(1,'Tony,18,59')");  插入数据
}

bool Database::Signup(user myuser)   // 登录功能
{
    
    
    QString sql=QString("select UserPassword from Usertable where UserID='%1'").arg(myuser.GetUserID());
    QSqlQuery query;
    if(query.exec(sql))   //如果这个密码存在,query.exec(sql)会返回true
    {
    
    
        if(query.first())
        {
    
    
            QString pass=query.value(0).toString();
            pass=pass.remove(QRegExp("\\s"));    //去除空格操作ui控件中的空格
            if(pass==myuser.GetUserPassword()){
    
        //如果返回的密码和当前输入的密码是一样的,说明成功了
                qDebug()<<"用户登陆成功";
                return true;
            }
            else{
    
    
                qDebug()<<"账号不存在";
                return false;
            }
        }else{
    
    
            return false;
        }
    }
    else{
    
    
        return false;
    }
}

bool Database::Register(user myuser)  //向数据库写入注册者的信息
{
    
    
    QString sql=QString("insert into Usertable values('%1','%2')").arg(myuser.GetUserID()).arg(myuser.GetUserPassword());
    qDebug()<<sql;
    QSqlQuery query;
    if(query.exec(sql))
    {
    
    
        return true;
    }
    else{
    
    
        qDebug()<<"当前用户名已被注册";
        return false;   //因为可能会出现userID已存在的情况
    }

}

QString Database::Chaxun(words words1)
{
    
    
    QString sql=QString("select translate from words where word='%1'").arg(words1.GetEnglish());
    QSqlQuery query;
    QString translate1;
    if(query.exec(sql))  //如果这个单词的翻译存在
    {
    
    
        while(query.next())
        {
    
    
            qDebug()<<words1.GetEnglish()<<"的汉语意思为:";
            translate1 = query.value(0).toString();
            return translate1;
        }
    }
    else{
    
    
        return translate1;
    }
}

5、单词显示

可以看到,在软件中,单词是在这两个文本窗口显示的。
在这里插入图片描述

  • 首先我们要创建这两个文本框,这里可以在背单词的类中创建两个私有成员,然后再其构造函数中创建文本框,具体代码如下:
private:
    Ui::memorize *ui;

    QTextEdit *m_TextEdit1, *m_TextEdit2;    //用于创建QTextEdit对象,好显示文字  (1显示单词和意思,2显示例句)
};
memorize::memorize(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::memorize)
{
    
    
    ui->setupUi(this);
    this->setWindowTitle("百词斩");
    this->setFixedSize(600,600);

    m_TextEdit1 = new QTextEdit(this);
    m_TextEdit1->setGeometry(80,80,220,300);
    m_TextEdit1->setAlignment(Qt::AlignCenter);

    m_TextEdit2 = new QTextEdit(this);
    m_TextEdit2->setGeometry(350,80,220,300);
    m_TextEdit2->setAlignment(Qt::AlignCenter);

}
  • 然后我们要去数据库中获取单词以及例句
  • 怎么获取呢?这里我们采用随机获取的方式,怎么随机呢?首先我们可以查出数据表中总共有多少个单词,记为sum,然后我们利用随机种子取余数的方式,随机的从1-sum中生成一个id号,再通过这个id号去查询单词。
  • 注意获取到id号之后我们要用两个sql语句去查询,一个查询单词及意思,一个查询例句及意思,然后输出到不同的文本窗口。
  • 进一步要注意的是,实际效果会有“单词:” 、 “解释:” 、 “例句:” 、“解释:”这是我另外拼接上去的,因为原本的数据库中并没有这几句话。而且在文本框中我还实现了换行的功能。

完整的代码如下,这里面会涉及到下一节要讲生词本的功能。

//memorize.h
#ifndef MEMORIZE_H
#define MEMORIZE_H

#include <QWidget>
#include <QTextEdit>
#include <QPainter>
#include "user.h"   //用于获取用户id
namespace Ui {
    
    
class memorize;
}

class memorize : public QWidget
{
    
    
    Q_OBJECT

public:
    explicit memorize(QWidget *parent = nullptr);
    ~memorize();

private slots:
    void on_return_function_clicked();

    void on_begin_clicked();

    void on_join_unknow_clicked();  //加入生词本

signals:
    void return_function();    //返回信号

protected:
    //绘图事件函数
    void paintEvent(QPaintEvent *event);

private:
    Ui::memorize *ui;

    QTextEdit *m_TextEdit1, *m_TextEdit2;    //用于创建QTextEdit对象,好显示文字  (1显示单词和意思,2显示例句)
};

#endif // MEMORIZE_H

//memorize.cpp
#include "memorize.h"
#include "ui_memorize.h"
#include <QTime>      //用于生成随机数,来控制id,返回单词
#include <QString>
#include <QDebug>
#include <QSqlQuery>  //对数据库进行增删改查的头文件
#include <QFile>
#include <QMessageBox>

memorize::memorize(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::memorize)
{
    
    
    ui->setupUi(this);
    this->setWindowTitle("百词斩");
    this->setFixedSize(600,600);

    m_TextEdit1 = new QTextEdit(this);
    m_TextEdit1->setGeometry(80,80,220,300);
    m_TextEdit1->setAlignment(Qt::AlignCenter);

    m_TextEdit2 = new QTextEdit(this);
    m_TextEdit2->setGeometry(350,80,220,300);
    m_TextEdit2->setAlignment(Qt::AlignCenter);

}

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

void memorize::on_return_function_clicked()
{
    
    
    emit return_function();    //发送返回信号
    this->close();
}

void memorize::on_begin_clicked()
{
    
    
    QTime randtime;
    randtime = QTime::currentTime();
    qsrand(randtime.msec()+randtime.second()*1000); //以当前时间ms为随机种子

    QString sql="select count(id) from words";
    QSqlQuery query;
    int sum; //用于记录数据表中共有多少个单词
    if(query.exec(sql)) //执行sql语句是否成功
    {
    
    
        query.next();
        sum =query.value(0).toInt();
    }else{
    
    
        qDebug()<<"查询数据库中单词总数失败";
    }

    int n = (qrand() % sum) + 1;    //产生10以内的随机整数,+1是为了防止id为0。  可以通过控制%后面的数来调整随机数的范围
    QString sql1=QString("select englishWord,chineseWord from words where id='%2'").arg(n);   //获取数据库中单词的英文和中文
    QString sql2=QString("select englishInstance1,chineseInstance1 from words where id='%1'").arg(n);  //获取数据库中单词的英文例句和中文解释
    QString sql3="\n \n";
    if(query.exec(sql1)) //执行sql语句是否成功
    {
    
    
        //将当前单词的id存入id.txt文件中,用于生词本功能
        QFile file("word_id.txt");
        file.open(QIODevice::ReadWrite | QIODevice::Text);
        file.write(QString("%1").arg(n).toUtf8());
        //关闭文件
        file.close();

        while(query.next())//指向下一条
        {
    
    
            //根据下标将返回结果进行分割
            QString TEXT1=query.value(0).toString();
            QString TEXT2=query.value(1).toString();
            m_TextEdit1->setText("单词: "+TEXT1+sql3+"解释: "+TEXT2);
        }

    }else
    {
    
    
        qDebug()<<"获取单词失败";
    }
    if(query.exec(sql2)) //执行sql语句是否成功
    {
    
    
        //qDebug()<<"find record num:"<<query.size();//查询结果记录条数

        while(query.next())//指向下一条
        {
    
    
            //根据下标找到对应字段值
            QString TEXT3=query.value(0).toString();
            QString TEXT4=query.value(1).toString();
            m_TextEdit2->setText("例句: "+TEXT3+sql3+"解释: "+TEXT4);
        }

    }else
    {
    
    
        qDebug()<<"获取单词失败";
    }

}

void memorize::paintEvent(QPaintEvent *event)   //绘制图片背景
{
    
    
   // 给子窗口画背景图
   QPainter p(this);    //创建画家对象
   QPixmap pixmap(":/image/image/blackgroud2.PNG");  //获取背景图路径
   p.drawPixmap(0,0,this->width(),this->height(),pixmap);  //绘制背景图
}


void memorize::on_join_unknow_clicked()  //加入生词本
{
    
    
    QString userid,wordid;
    QFile file1("user_id.txt");  //参数就是读取用户id文件的路径
    file1.open(QIODevice::ReadOnly);//只读模式
    userid = file1.readLine(); //按行读,追加
    file1.close();

    QFile file2("word_id.txt");    //读取单词id的文件路径
    file2.open(QIODevice::ReadOnly);//只读模式
    wordid = file2.readLine(); //按行读,追加
    qDebug()<<userid<<wordid;
    file1.close();

    QString sql=QString("insert into Unword values('%1','%2')").arg(userid).arg(wordid);
    qDebug()<<sql;
    QSqlQuery query;
    if(query.exec(sql))
    {
    
    
        QMessageBox::information(this,
               tr("百词斩"),
               tr("成功添加到生词本"),
               QMessageBox::Ok);

    }
    else{
    
    
        QMessageBox::warning(this,
               tr("百词斩"),
               tr("这个词已经存在于单词本中了哦!"),
               QMessageBox::Ok);
    }
}

6、生词本功能(添加、循环复习、移出)

在这里插入图片描述
在这里插入图片描述

1、怎么把当前不认识的单词加入到生词本呢?
由于生词本的数据库是只有一个用户ID单词ID,因此,如果用户不认识这个单词,我们只需把用户ID和当前的单词ID作为SQL语句插入到数据库中即可。

由于用户的ID是在登陆的时候输入的,因此必须得在登陆的时候记录下当前用户的ID,然后在用户碰到不认识的单词的时候,连同当前单词ID一起插入到数据库中。
(方法1):原先我是想着定义一个全局变量用于保存这个用户ID,但是由于用户ID是在函数体内通过成员函数返回的,不能赋给局部变量,因此就没有用这个方法了。
(方法2):通过文件读写,在获取到用户ID时,第一时间将他写到本地的txt文件中,当要用到生词本这个功能的时候,再将其读出来,这个方法其实效率不高,有机会还得改进。(这里就要包含#include < QFile >头文件)

2、怎么通过点击下一个生词按钮,然后随机产生一个生词本中的单词供用户复习呢?
这里我们可以通过查询用户当前的id,然后去生词本的数据表中查看该用户有多少个不认识的单词id,然后从这些不认识的单词中随机生成一个即可,这里同之前的随机来一个单词有点类似。具体代码如下
3、如果这个单词用户会背了,怎么实现移出生词本的功能呢?
当用户点击移出生词本的功能时,我们可以通过捕获当前屏幕的内容,知道当前用户背的是哪个单词,然后在单词数据表中查找这个单词的id,再到生词本数据表中将id为这个的当前用户的记录删除即可。

当然,我们还添加了一些小细节,比方说如果用户在背单词的时候将一个已经存在于生词本的单词又添加了一遍,这时候我们会给出提示,有比方说如果用户生词本中没有数据,但用户选择了下一个生词这个按钮,我们会给出提示。

生词本部分的代码如下:

//.h文件
#ifndef UNFAMILIAR_H
#define UNFAMILIAR_H

#include <QWidget>
#include <QTextEdit>
#include <QPainter>

namespace Ui {
    
    
class unfamiliar;
}

class unfamiliar : public QWidget
{
    
    
    Q_OBJECT

public:
    explicit unfamiliar(QWidget *parent = nullptr);
    ~unfamiliar();

private slots:
    void on_return_function2_clicked();   //返回功能选择界面

    void on_next_clicked();  //切换下一个生词

    void on_move_unknow_clicked();

signals:
    void return_function2();    //返回信号

protected:
    //绘图事件函数
    void paintEvent(QPaintEvent *event);

private:
    Ui::unfamiliar *ui;
    QTextEdit *m_TextEdit3, *m_TextEdit4;    //用于创建QTextEdit对象,好显示文字
};

#endif // UNFAMILIAR_H

//.cpp文件
#include "unfamiliar.h"
#include "ui_unfamiliar.h"
#include <QDebug>
#include <QFile>
#include <QSqlQuery>  //对数据库进行增删改查的头文件
#include <QVector>
#include <QTime>      //用于生成随机数,来控制id,返回单词
#include <QMessageBox>

unfamiliar::unfamiliar(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::unfamiliar)
{
    
    
    ui->setupUi(this);
    this->setWindowTitle("百词斩");
    this->setFixedSize(600,600);

    m_TextEdit3 = new QTextEdit(this);
    m_TextEdit3->setGeometry(80,80,220,300);
    m_TextEdit3->setAlignment(Qt::AlignCenter);

    m_TextEdit4 = new QTextEdit(this);
    m_TextEdit4->setGeometry(350,80,220,300);
    m_TextEdit4->setAlignment(Qt::AlignCenter);
}

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

void unfamiliar::on_return_function2_clicked()
{
    
    
    emit return_function2();    //发送返回信号
    this->close();
}

void unfamiliar::paintEvent(QPaintEvent *event)
{
    
    
    // 给子窗口画背景图
    QPainter p(this);    //创建画家对象
    QPixmap pixmap(":/image/image/blackgroud2.PNG");  //获取背景图路径
    p.drawPixmap(0,0,this->width(),this->height(),pixmap);  //绘制背景图
}

void unfamiliar::on_next_clicked()  //切换下一个生词
{
    
    
    //获取用户ID
    QString userid;
    QFile file1("user_id.txt");  //参数就是读取用户id文件的路径
    file1.open(QIODevice::ReadOnly);//只读模式
    userid = file1.readLine(); //按行读,追加
    file1.close();

    QString sql=QString("select id from Unword where UserID='%2'").arg(userid);
    QSqlQuery query;
    QVector<int> Arr_id;     //储存返回的id,因为一个用户可能有点多个不会的单词

    if(query.exec(sql)) //执行sql语句是否成功
    {
    
    
        if(query.size()==0){
    
        //如果返回结果为空,说明Unword表中,没有当前用户不会的单词id
            QMessageBox::information(this,
                   tr("百词斩"),
                   tr("可能是你太强了,目前生词本中还没有生词哦!"),
                   QMessageBox::Ok);
            return;
        }
        for(int i=0; i<query.size();i++){
    
       //这个for循环的作用是将用户不会的单词id存放到id数组中
            //根据下标将返回结果进行分割
            query.next();
            Arr_id.push_back(query.value(0).toInt());
            qDebug()<<query.value(0).toInt();
            qDebug()<<(Arr_id[i]);
        }

        //从Arr_id.size()中随机生成一个id,用于随机复习生词本中的单词
        QTime randtime;
        randtime = QTime::currentTime();
        qsrand(randtime.msec()+randtime.second()*1000); //以当前时间ms为随机种子
        int n = (qrand() % Arr_id.size());

        QString sql1=QString("select englishWord,chineseWord from words where id='%1'").arg(Arr_id[n]);   //获取数据库中单词的英文和中文
        QString sql2=QString("select englishInstance1,chineseInstance1 from words where id='%1'").arg(Arr_id[n]);  //获取数据库中单词的英文例句和中文解释
        QString sql3="\n \n";
        QSqlQuery query;
        if(query.exec(sql1)) //执行sql语句是否成功
        {
    
    
            //将当前单词的id存入unknow_id.txt文件中,用于生词本功能
            QFile file("unknow_id.txt");
            file.open(QIODevice::ReadWrite | QIODevice::Text);
            //向文件中写入两行字符串
            file.write(QString("%2").arg(n).toUtf8());   //因为一次只向数据库插一条数据(用户id,单词id),所以不能用追加读写的方式
            //关闭文件
            file.close();

            while(query.next())//指向下一条
            {
    
    
                //根据下标将返回结果进行分割
                QString TEXT1=query.value(0).toString();
                QString TEXT2=query.value(1).toString();
                m_TextEdit3->setText("单词: "+TEXT1+sql3+"解释: "+TEXT2);
            }

        }else
        {
    
    
            qDebug()<<"获取单词失败";
        }
        if(query.exec(sql2)) //执行sql语句是否成功
        {
    
    
            //qDebug()<<"find record num:"<<query.size();//查询结果记录条数

            while(query.next())//指向下一条
            {
    
    
                //根据下标找到对应字段值
                QString TEXT3=query.value(0).toString();
                QString TEXT4=query.value(1).toString();
                m_TextEdit4->setText("例句: "+TEXT3+sql3+"解释: "+TEXT4);
            }

        }else
        {
    
    
            qDebug()<<"获取单词失败";
        }
    }


}

void unfamiliar::on_move_unknow_clicked()  //移除生词本中的单词  通过检测当前m_TextEdit3中显示的是哪个单词,然后在words数据表中查找这个单词的ID,最后在Unword数据表中将用户ID对应的单词id删除
{
    
    
    QString texts = m_TextEdit3->toPlainText();  //检测当前m_TextEdit3中显示的是哪个单词
    qDebug()<<texts;
    QString englishWord;
    for(int i = 4; i < texts.size(); i++){
    
    
        if('a'<=texts[i-4]<='z'){
    
    
            englishWord+=texts[i];
        }
        if(englishWord.size()>0&&'\n'==texts[i+1]){
    
    
            break;
        }
    }
    QString sql=QString("select id from words where englishWord='%2'").arg(englishWord);   //获取words数据表中这个单词的ID
    QSqlQuery query;
    int id;
    if(query.exec(sql)) //执行sql语句是否成功
    {
    
    
        query.next();
        id=query.value(0).toInt();
        qDebug()<<id;
    }else{
    
    
        qDebug()<<"查询当前单词id失败";
    }

    QString userid;        //获取用户id
    QFile file1("user_id.txt");
    file1.open(QIODevice::ReadOnly);
    userid = file1.readLine();
    file1.close();

    QString sql2=QString("DELETE FROM Unword WHERE id='%1' AND UserID='%2';").arg(id).arg(userid);   //获取words数据表中这个单词的ID
    QSqlQuery query2;
    if(query2.exec(sql2)) //执行sql语句是否成功
    {
    
    
        QMessageBox::information(this,
               tr("百词斩"),
               tr("恭喜你,又学会了一个单词"),
               QMessageBox::Ok);
    }else{
    
    
        qDebug()<<"将该生词移除单词本失败";
    }
}

7、总结

项目其实还有很多待优化的地方,比方说界面上可以更美观一点,可以添加语音模块,可以加入一种按字典序进行背单词的模式等等。话说写代码真是一门技术,如果写的臃肿,有没有注释,那么真的很难维护,我的头文件也没有去整理。

最后
如果觉得对你有所帮助的话,希望能点赞收藏一波,您的鼓励就是对我最大的支持,谢谢!

猜你喜欢

转载自blog.csdn.net/qq_40077565/article/details/125050412