qt简易网络聊天室 数据库的练习

qt网络聊天室

服务器:

配置文件.pro

QT       += core gui network

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    widget.cpp

HEADERS += \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

头文件

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>   //服务器类头文件
#include <QDebug>   //信息调试类
#include <QMessageBox>  //消息对话框类头文件
#include <QTcpSocket>  //套接字类头文件

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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


private slots:
    void on_startBtn_clicked();
    void newConnection_slot();   //自定义处理newConnection信号的函数
    void readyRead_slot();   //自定义处理readyRead信号的槽函数

private:
    Ui::Widget *ui;

    //定义一个服务器指针
    QTcpServer *server;

    //定义一个客户端容器
    QList<QTcpSocket *> clientList;
};
#endif // WIDGET_H

源文件

main.cpp

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

widget.cpp

//服务器
//1、实例化服务器类对象,该对象就是服务器
//2、服务器调用成员函数listen()将服务器设置为被动监听状态,监听时可以指定主机地址,也可以any监听所有的主机地址。 端口号若为0,则服务器自动分配,也可指定端口号
//3、若有客户端连接,则服务器会发送一个newconnection信号,连接到对应的槽函数中处理相关逻辑
//4、在处理connect信号的槽函数中,使用nextPendingConntiin获取最新连接的客户端套接字
//5、将获取的套接字存入客户端容器中
//6、若客户端有数据项向服务器发送,则客户端套接字会自动发射一个readyRead信号,来凝结到对应的槽函数中处理相关逻辑
//7、在处理readyRead信号的槽函数中,来读取数据,
//7-1、先循环遍历哪些套接字是无效的(断开连接的客户端),将其从客户端容器中删除,使用state函数判断状态,为0则是断开连接的,不为0不是断开的
//7-2、在循环遍历判断是哪个客户端发来的数据,使用BytesAviable来判断哪个客户端有数据,为0则代表无
//7-3、若有数据,使用read readLine readAll函数读取数据,
//11、向套接字中写数据,可以使用write函数


#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //给服务器实例化对象
    server = new QTcpServer(this);  //该对象就是服务器,服务器创建完成


}

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


//启动服务器的槽函数
void Widget::on_startBtn_clicked()
{
    if(ui->portEdit->text().isEmpty())
    {
        QMessageBox::critical(this, "失败", "端口号不可为空");
        return;
    }

    //调用成员函数listen()将服务器设置为被动监听状态
    //函数原型:bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
    //参数1:主机号,any监听所有的主机地址, 指定主机地址,监听指定的主机地址。
    //参数2:通过哪个端口号访问服务器,若为0,则服务器自由分配,若不为0,则指定要监听的端口号, quint16 是无符号的16位整型, short
    //返回值,成功返回真,失败返回假

    //获取端口号
    quint16 port = ui->portEdit->text().toUInt();  //toUInt(), 转为无符号整型

    if(!server->listen(QHostAddress::Any, port))
    {
        QMessageBox::critical(this, "失败", "服务器启动失败");
        return;
    }
    else
    {
        QMessageBox::information(this, "成功", "服务器启动成功");
    }

    //此时说明服务器启动成功,并对客户端进行监听
    //若有客户端发送连接请求,则服务器会自动发射一个newconnection信号
    //将给信号连接到对应的槽函数中
    connect(server, &QTcpServer::newConnection, this, &Widget::newConnection_slot);
}

//处理newConnection函数的槽函数的实现
void Widget::newConnection_slot()
{

    //循环遍历,从客户端套接字容器中删去已断开连接的客户端套接字

    //函数原型:SocketState state() const;
    //功能:判断套接字的状态
    //参数,无
    //返回值:返回套接字的状态(枚举, 0代表断开连接)
    for(int i = 0; i<clientList.count(); i++)
    {
        if(0 == clientList.at(i)->state())
        {
            //移除下标为i的无效的客户端
            clientList.removeAt(i);
        }
    }
     //获取新连接的客户端的套接字
     //函数原型:virtual QTcpSocket *nextPendingConnection();
    //功能:获取新连接的客户端的套接字
    //无参,返回值就是获取到的客户端的套接字的指针
    QTcpSocket *s = server->nextPendingConnection();

    //将获取到的套接字存入到客户端套接字容器中
   clientList.push_back(s);

  //此时客户端和服务器建立连接
  //如果套接字有数据项发送给服务器时,该套接字自动发射一个readyRead信号
   //将readyRead信号连接到对应的槽函数中处理相关逻辑
   connect(s, &QTcpSocket::readyRead, this, &Widget::readyRead_slot);

}

//自定义处理readyRead信号的槽函数的实现
void Widget::readyRead_slot()
{
    //遍历判断是哪个客户端套发来的数据
    //使用BytesAvalible函数,若无数据可读则为0
    for(int i=0; i<clientList.count(); i++)
    {
        if(clientList[i]->bytesAvailable() != 0)
        {
            //读取套接字中的数据,可用read/readall/readline
            //函数原型QByteArray readAll();
            //功能:读取数据
            //无参,返回值,c风格的字符串
            QByteArray msg = clientList[i]->readAll();

            //将数据展示到ui界面上
            ui->msgList->addItem(QString::fromLocal8Bit(msg));  //将C风格的字符串转为QString类型的

            //将接收到的消息发送给所有的客户端
            for(int j=0; j<clientList.count(); j++)
            {
                clientList[j]->write(msg);
            }
        }
    }


}

ui界面

客户端

配置文件.pro

QT       += core gui network

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    widget.cpp

HEADERS += \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

头文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>     //客户端头文件
#include <QDebug>        //信息调试类头文件
#include <QMessageBox>  //消息对话框类头文件

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

private slots:
    void on_connectBtn_clicked();
    void connected_slot();  //自定义处理connected信号的槽函数
    void readyRead_slot();  //自定义处理readyRead信号的槽函数

    void on_sendBtn_clicked();

    void on_disConnectBtn_clicked();
    void disconnected_slot();  //自定义处理disconnected信号的槽函数

private:
    Ui::Widget *ui;

    //定义一个客户端指针
    QTcpSocket *client;
};
#endif // WIDGET_H

源文件

main.cpp

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

widget.cpp

//客户端
//1、实例化QTcpSocket类对象,该对象就是客户端
//2、调用客户端对象的connecTtoHost的成员函数向服务器发送连接请求,需要给定服务器的ip和端口
//3、如果与服务器连接成功,客户端自动发射一个connected信号,将信号与对应的槽函数进行连接处理相关逻辑即可
//4、如果服务器有数据项向客户端发送,客户端会自动发射一个readyRead信号,将信号连接到对应的槽函数读取数据
//5、若客户端要给服务器发送消息,使用write函数
//6、在处理readyRead信号的槽函数中,使用read/readLine/readAll函数读取数据
//7、使用该客户端对象的disconnectFromHost函数端口与服务器的连接,
//8、如果成功断开则会自动发射disconnected信号,将disconnected信号与对应的槽函数连接处理即可

#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //实例化客户端空间
    client  = new QTcpSocket(this);   //该对象就是客户端,创建客户端成功

    ui->sendBtn->setEnabled(false);
    ui->disConnectBtn->setEnabled(false);

    //若连接成功,则客户端自动发射一个connected信号
    //将connected信号连接到对应的槽函数处理相关逻辑
    //因为只需要连接一次,所以将其写到构造函数中
    connect(client, &QTcpSocket::connected, this, &Widget::connected_slot);

    //若服务器有数据项向客户端发送,客户端会自动发送readyRead信号
    //将readyRead信号与对应的槽函数连接
    //由于只需连接一次,所以写在构造函数中
    connect(client, &QTcpSocket::readyRead, this, &Widget::readyRead_slot);

    //若断开成功,则客户端会自动发射一个disconnected信号
    //连接到对应的槽函数处理即可
    //因只需要连接一次,写在构造函数即可
    connect(client, &QTcpSocket::disconnected, this, &Widget::disconnected_slot);
}

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

//连接服务器按钮的槽函数
void Widget::on_connectBtn_clicked()
{
    if(ui->userNameEdit->text().isEmpty() || ui->ipEdit->text().isEmpty() || ui->portEdit->text().isEmpty())
    {
        QMessageBox::critical(this, "Error", "信息填写不完整");
        return;
    }
    //获取服务器的主机地址
    QString ip = ui->ipEdit->text();
    //获取服务器的端口号
    quint16 port = ui->portEdit->text().toUInt();  //toUInt()转化为无符号整数
    //该客户端对象调用成员函数连接服务器
    //函数原型:virtual void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
    //功能:客户端连接服务器
    //参数1:服务器的ip地址
    //参数2:客户端的端口号 , quint16 无符号的16位的整型 short
    //返回值:无
    client->connectToHost(ip, port);

    //顺便向客户端发送一条数据
    QString msg = ui->userNameEdit->text();
    msg = msg+ ": 进入聊天室";

    //使用write函数向服务器发送数据
    client->write(msg.toLocal8Bit());  // toLocal8Bit()转为C风格字符串

    //若连接成功,则客户端自动发射一个connected信号
    //将connected信号连接到对应的槽函数处理相关逻辑
    //因为只需要连接一次,所以将其写到构造函数中
}

//处理connected信号的槽函数的实现
void Widget::connected_slot()
{
    //说明连接成功
    ui->userNameEdit->setEnabled(false);
    ui->ipEdit->setEnabled(false);
    ui->portEdit->setEnabled(false);
    ui->connectBtn->setEnabled(false);
    ui->disConnectBtn->setEnabled(true);
    ui->sendBtn->setEnabled(true);

    //若服务器有数据项向客户端发送,客户端会自动发送readyRead信号
    //将readyRead信号与对应的槽函数连接
    //由于只需连接一次,所以写在构造函数中

}

//处理readyRead信号的槽函数
void Widget::readyRead_slot()
{
    //使用read/readLine/readAll读取数据
    QByteArray msg =client->readAll();
   
    //将读取的数据展示到ui界面上

    ui->msgList->addItem(QString::fromLocal8Bit(msg));

}

//发送按钮的槽函数
void Widget::on_sendBtn_clicked()
{
    //获取用户名
    QString userName = ui->userNameEdit->text();
    //获取文本输入框的内容
    QString msg = ui->textEdit->toPlainText();

    msg = userName + ": " + msg;

    //将数据发送给服务器
    client->write(msg.toLocal8Bit());

    //清空文本输入框
    ui->textEdit->clear();

}

//断开按钮对应的槽函数
void Widget::on_disConnectBtn_clicked()
{
    //告知服务器即将断开连接
    QString userName = ui->userNameEdit->text();
    QString msg = userName + ": 退出聊天室";
    client->write(msg.toLocal8Bit());

    //使用该类对象调用disconnectFromHost断开与服务器的连接
    //函数原型:virtual void disconnectFromHost();
    //无参 无返
    client->disconnectFromHost();

    //若断开成功,则客户端会自动发射一个disconnected信号
    //连接到对应的槽函数处理即可
    //因只需要连接一次,写在构造函数即可
}

void Widget::disconnected_slot()
{
    ui->sendBtn->setEnabled(false);
    ui->disConnectBtn->setEnabled(false);
    ui->userNameEdit->setEnabled(true);
    ui->ipEdit->setEnabled(true);
    ui->portEdit->setEnabled(true);
    QMessageBox::information(this, "退出", "断开成功");
}

ui界面

数据库:
配置文件.pro

QT       += core gui sql

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    widget.cpp

HEADERS += \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

头文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QSqlDatabase>    //数据库管理类
#include <QSqlQuery>       //执行sql语句对应的类
#include <QSqlRecord>      //记录类
#include <QMessageBox>     //消息对话框类
#include <QDebug>          //信息调试类

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

private slots:
    void on_inputBtn_clicked();

    void on_showBtn_clicked();

    void on_searchBtn_clicked();

    void on_deleteBtn_clicked();

private:
    Ui::Widget *ui;

    //定义一个数据库对象
    QSqlDatabase db;
};
#endif // WIDGET_H

源文件:

main.cpp

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //判断数据库对象是否包含了自己使用的数据库,Student.db
    if(!db.contains("Student.db"))
    {
        //添加数据库
        // static QSqlDatabase addDatabase(QSqlDriver* driver, const QString& connectionName = QLatin1String(defaultConnection));
        //参数1:数据库的版本
        //返回值:添加的数据库
        db = QSqlDatabase::addDatabase("QSQLITE");   //表明使用的是Sqlite3版本的数据库

        //给数据库命名
        db.setDatabaseName("Student.db");
    }

    //打开数据库
    if(!db.open())
    {
        QMessageBox::information(this, "提示", "数据库打开失败");
    }

    //此时说明数据库已经创建出来并打开了,就可以创建数据表了
    //创建数据表需要使用sql语句, 需要使用QSQLQuerry对象来完成
    //准备sql语句
//    QString sql = "create table if not exists myTable("
//            "id integer Primary key autoincrement,"
//            "numb integer,"
//            "name varchar(10),"
//            "sex varchar(4))";
    QString sql = "create table if not exists myTable("              //创建表的sql语句
                  "id integer primary key autoincrement,"          //id主键,允许自增
                  "numb integer,"                                   //学号,是整形
                  "name varchar(10),"                               //姓名   字符串
                  "score integer,"
                  "sex varchar(4))";                                 //性别 字符串


    //定义语句执行者
    QSqlQuery queery;
    //使用querry调用成员函数exec执行sql语句
    if(!queery.exec(sql))
    {
        QMessageBox::information(this, "失败", "表格创建失败");
        return;
    }
    else
    {
         QMessageBox::information(this, "成功", "表格创建出成功");
    }
}

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

//录入按钮对应的槽函数
void Widget::on_inputBtn_clicked()
{
    //获取ui界面中要存入数据库中的数据
    int numb_ui = ui->numEdit->text().toUInt();   //获取ui界面的学号
    QString name_ui = ui->nameEdit_2->text();  //获取ui界面的姓名
    int score_ui = ui->scoreEdit_3->text().toUInt();  //获取ui界面上的成绩
    QString sex_ui = ui->sexEdit_4->text();  //获取ui界面上的性别

    //判断是否有漏填数据
    if(numb_ui == 0 || name_ui.isEmpty() || score_ui == 0 || sex_ui.isEmpty())
    {
        QMessageBox::information(this, "提示", "请将数据填写完整");
        return;
    }

    //准备sql语句
    QString sql = QString("insert into myTable(numb, name, score, sex)"
                          "values(%1, '%2', %3, '%4')")
            .arg(numb_ui).arg(name_ui).arg(score_ui).arg(sex_ui);
    qDebug() << sql;

    //定义语句执行者
    QSqlQuery querry;
    //调用执行者的exec()函数执行sql语句
   if(!querry.exec(sql))
   {
       QMessageBox::information(this, "失败", "数据插入失败");
   }
   else
   {
       QMessageBox::information(this, "成功", "数据录入成功");
   }

}

//展示按钮对应的槽函数
void Widget::on_showBtn_clicked()
{
    QString sql = "select * from MyTable";

    //定义语句执行者
    QSqlQuery querry;

    //调用执行者的exec()函数执行sql语句
    if(!querry.exec(sql))
    {
        QMessageBox::information(this, "失败", "查询失败");
        return;
    }
    else
    {
        QMessageBox::information(this, "成功", "查询成功");
    }

    //此时,将查找到的所有结果。全部都放在querry中了
    //可以通过next函数不断遍历查询结果
    int i = 0;   //记录行号
    while(querry.next())
    {
        //遍历的就是任意一组记录querry.record
        //qDebug() << querry.record();

        //querry.record().count();  返回当前记录对应数据项的个数
        //querry.record().count();

        //要找到每条记录中的每个数据
        //qDebug() << querry.record().value(2);
        //querry.record().value(j).toString() //将记录的某一项的转为字符串

        //将数据库中的表格展示到ui界面
        for(int j = 0; j<querry.record().count()-1; j++)
        {
            ui->tableWidget->setItem(i, j, new QTableWidgetItem(querry.record().value(j+1).toString()));
        }
        i++;   //进行下一行
    }
}


void Widget::on_searchBtn_clicked()
{
    //获取学号
    QString name_ui = ui->nameEdit_2->text();
    int numb_ui = ui->numEdit->text().toUInt();

     //sql语句
    QString sql = QString("select * from myTable where numb = %1").arg(numb_ui);

    //定义语句执行者
    QSqlQuery querry;

    //执行者使用exec函数执行sql语句
    if(!querry.exec(sql))
    {
        QMessageBox::information(this , "提示", "查询失败");
        return;
    }
    else
    {
        QMessageBox::information(this , "提示", "查询成功");
    }

    int i = 0;  //行号
    while(querry.next())
    {
        for(int j=0; j<querry.record().count()-1; j++)
        {
            ui->tableWidget->setItem(i, j, new QTableWidgetItem(querry.record().value(j+1).toString()));
        }
    }
}

void Widget::on_deleteBtn_clicked()
{
    QString name_ui = ui->nameEdit_2->text();
    //sql语句
    QString sql = QString("delete from myTable where name = '%1'").arg(name_ui);
    qDebug() << sql;
    //定义语句执行者
    QSqlQuery querry;

    //querry调用自己成员函数执行sql语句
    if(!querry.exec(sql))
    {
        QMessageBox::information(this, "提示", "删除失败");
        return;
    }
    else
    {
        QMessageBox::information(this, "提示", "删除成功");
    }


}

ui界面

猜你喜欢

转载自blog.csdn.net/qq_46766479/article/details/132679582
今日推荐