QT程序设计多人聊天室(基于QT、sqlite3、TCP/IP)

目录

技术路线

效果展示

程序主体

sqoperator.h

mylogin.h

myenroll.h

chatinterface.h

tips.h

myapp.h

*******************

sqoperator.cpp

mylogin.cpp

myenroll.cpp

chatinterface.cpp

tips.cpp

myapp.cpp

main.cpp

widget.h

widget.cpp

main.cpp


技术路线

QT程序设计、sqlite数据库调用、TCP/IP客户端与服务端的搭建

通过次程序代码,可以学习如何使用纯代码设计界面(非UI),了解如何通过QT调用sqlite数据库,以及如何使用TCP/IP协议简单地搭建客户端与服务端。 此项目中的界面主要做的有两个,一个登录界面,一个聊天消息收发界面,登录界面有登录和注册,通过创建数据库,调用数据库中的数据进行判断,程序实现对数据库中的资源进行增删改查。在界面跳转方面,通过QT独有的信号和槽机制,进行设计,此项目主要在登录界面和消息交互界面设计了界面的跳转。至于客户端和服务端的搭建,是本项目主要需要实现的地方,通过ip地址和端口号,进行,各个客户端之间的通信,数据先通过客户端发往服务端,再由服务端发往客户端,可以实现多个客户端的并发通信,只需要将服务端开启即可。

效果展示

QT程序设计多人聊天室(基于QT、sqlite3、TCP/IP)_第1张图片

QT程序设计多人聊天室(基于QT、sqlite3、TCP/IP)_第2张图片


程序主体

sqoperator.h

这是对数据库的封装,其中有部分封装本项目没用到

#ifndef SQLITEOPERATOR_H
#define SQLITEOPERATOR_H

#include 
#include 
#include 
#include 
#include 

typedef struct
{  
    QString usrname;
    QString usrpass;
}info;

//结构体用于存放用户名和密码

class SqOperator : public QWidget
{
    Q_OBJECT
public:
    explicit SqOperator(QWidget *parent = nullptr);

    // 打开数据库
    bool openDb(void);
    // 创建数据表
    void createTable(void);
    // 判断数据表是否存在
    bool isTableExist(QString& tableName);
    // 查询全部数据
    void queryTable(QList &list);
    // 插入数据
    bool singleInsertData(info &singleData); // 插入单条数据
    void moreInsertData(QList &moreData); // 插入多条数据
    // 修改数据
    void modifyData(QString usrname,QString usrpass);
    // 删除数据
    void deleteData(QString usrname);
    //删除数据表
    void deleteTable(QString& tableName);
    // 关闭数据库
    void closeDb(void);

private:
    QSqlDatabase database;// 用于建立和数据库的连接

};

#endif // SQLITEOPERATOR_H

mylogin.h

#ifndef MYLOGIN_H
#define MYLOGIN_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 

class mylogin : public QDialog
{
    Q_OBJECT

public:
    mylogin(QWidget *parent = nullptr);
    ~mylogin();
    void init_ui();

    QLabel *lb1;
    QLabel *lb2;
    QLabel *lb3;

    QPushButton *bnt_login;
    QPushButton *bnt_register;

    QLineEdit *usr_name_le;
    QLineEdit *usr_pass_le;

    QHBoxLayout *hb1;
    QHBoxLayout *hb2;
    QHBoxLayout *hb3;
    QVBoxLayout *vb1;


signals:
    void sig_login(QString usrname, QString usrpass); //自定义的登录信号,发给myapp
    void sig_enroll();    //自定义的注册信号,发给myapp

public slots:
    void do_login();        //为关联按键和信号发射所设的槽函数
    void do_enroll();

};
#endif // MYLOGIN_H

myenroll.h

#ifndef MYENROLL_H
#define MYENROLL_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 

class myenroll : public QWidget
{
    Q_OBJECT
public:
    explicit myenroll(QWidget *parent = nullptr);   
    void init_ui();


    QLineEdit * name;
    QLineEdit * pass;
    QLabel * lb1;
    QLabel * lb2;
    QPushButton * bnt1;

    QHBoxLayout * hb1;
    QHBoxLayout * hb2;
    QHBoxLayout * hb3;

    QVBoxLayout * vb1;




signals:
    void sig_enroll_info(QString usrname, QString usrpass); //自定义的注册信号,发送给myapp

public slots:
    void send_msg();    //为注册信号所设置的槽函数
};

#endif // MYENROLL_H

chatinterface.h

#ifndef CHATINTERFACE_H
#define CHATINTERFACE_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "mylogin.h"

class chatInterface : public QWidget
{
    Q_OBJECT
public:
    explicit chatInterface(QWidget *parent = nullptr);

    void init();

    QLabel * lb1;
    QLineEdit * le1;
    QTextEdit * te1;
    QPushButton * bnt1;
    QHBoxLayout * hb1;
    QVBoxLayout * vb1;


    mylogin * login;
    QTcpSocket * mysock;

signals:

public slots:
    void connect_success_msg();
    void recv_msg_slots();
    void send_msg_slots();


};

#endif // CHATINTERFACE_H

tips.h

#ifndef TIPS_H
#define TIPS_H

#include 
#include 

class tips : public QWidget
{
    Q_OBJECT
public:
    explicit tips(QWidget *parent = nullptr);

    void loginfail();
    void insertok();


signals:

public slots:


};

#endif // TIPS_H

 myapp.h

#ifndef MYAPP_H
#define MYAPP_H

#include 
#include 
#include 
#include "mylogin.h"
#include "myenroll.h"
#include "tips.h"
#include "chatinterface.h"
#include "sqoperator.h"



class myapp : public QObject
{
    Q_OBJECT
public:
    explicit myapp(QObject *parent = nullptr);


    SqOperator *mydb;   //数据库类
    mylogin * login;
    myenroll * enroll;
    tips * tip;//提示信息类
    chatInterface *face;//主界面信息类


signals:


public slots:

    bool judge(QString usrname, QString usrpass);
    void show_enroll_face();
    void insertdb(QString usrname,QString usrpass);

};

#endif // MYAPP_H

*******************

sqoperator.cpp

#include "sqoperator.h"

SqOperator::SqOperator(QWidget *parent) : QWidget(parent)
{
    if (QSqlDatabase::contains("qt_sql_default_connection"))
        {
            database = QSqlDatabase::database("qt_sql_default_connection");
        }
        else
        {
            // 建立和SQlite数据库的连接
            database = QSqlDatabase::addDatabase("QSQLITE");
            // 设置数据库文件的名字
            database.setDatabaseName("chatapp.db");
        }
}

// 打开数据库
bool SqOperator::openDb()
{
    if (!database.open())
    {
        qDebug() << "Error: Failed to connect database." << database.lastError();
        return false;
    }
    else
    {
        qDebug() <<"open database success";
    }

    return true;
}

// 创建数据表
void SqOperator::createTable()
{
    // 用于执行sql语句的对象
    QSqlQuery sqlQuery;
    // 构建创建数据库的sql语句字符串
    QString createSql = QString("CREATE TABLE if not exists idinfo(usrname TEXT PRIMARY KEY ,usrpass TEXT NOT NULL)");
    sqlQuery.prepare(createSql);

    // 执行sql语句
    if(!sqlQuery.exec())
    {
        qDebug() << "Error: Fail to create table. " << sqlQuery.lastError();
    }
    else
    {
        qDebug() << "Table created!";
    }
}

// 判断数据库中某个数据表是否存在
bool SqOperator::isTableExist(QString& tableName)
{
    QSqlDatabase database = QSqlDatabase::database();
    if(database.tables().contains(tableName))
    {
        return true;
    }

    return false;
}

// 查询全部数据,这里需要改造一下,我们传入一个空容器,然后,把数据弄出去
void SqOperator::queryTable(QList &list)
{
    QSqlQuery sqlQuery;
    sqlQuery.exec("SELECT * FROM idinfo");
    if(!sqlQuery.exec())
    {
        qDebug() << "Error: Fail to query table. " << sqlQuery.lastError();
    }
    else
    {
        while(sqlQuery.next())
        {
            QString usrname = sqlQuery.value(0).toString();
            list.append(usrname);
            QString usrpass = sqlQuery.value(1).toString();
            list.append(usrpass);

            //qDebug()<& moredb)
{
    // 进行多个数据的插入时,可以利用绑定进行批处理
    QSqlQuery sqlQuery;
    sqlQuery.prepare("INSERT INTO idinfo VALUES(?,?,?)");
    QVariantList nameList,passList;
    for(int i=0; i< moredb.size(); i++)
    {
        nameList <<  moredb.at(i).usrname;
        passList << moredb.at(i).usrpass;

    }
    sqlQuery.addBindValue(nameList);
    sqlQuery.addBindValue(passList);


    if (!sqlQuery.execBatch()) // 进行批处理,如果出错就输出错误
    {
        qDebug() << sqlQuery.lastError();
    }
}

// 修改数据
void SqOperator::modifyData(QString usrname,QString usrpass)
{
    QSqlQuery sqlQuery;
    sqlQuery.prepare("UPDATE student SET usrname=?,usrpass=?");
    sqlQuery.addBindValue(usrname);
    sqlQuery.addBindValue(usrpass);
    if(!sqlQuery.exec())
    {
        qDebug() << sqlQuery.lastError();
    }
    else
    {
        qDebug() << "updated data success!";
    }
}

// 删除数据
void SqOperator::deleteData(QString usrname)
{
    QSqlQuery sqlQuery;

    sqlQuery.exec(QString("DELETE FROM student WHERE id = %1").arg(usrname));
    if(!sqlQuery.exec())
    {
        qDebug()<

mylogin.cpp

#include "mylogin.h"

mylogin::mylogin(QWidget *parent)
    : QDialog(parent)
{
    this->init_ui();

    connect(this->bnt_login, &QPushButton::clicked, this, &mylogin::do_login);
    connect(this->bnt_register, &QPushButton::clicked , this ,&mylogin::do_enroll);

}

mylogin::~mylogin()
{

}

void mylogin::init_ui()
{
    this->setFixedSize(QSize(600,350));
    this->setWindowTitle(tr("大飞秋"));
    this->setWindowIcon(QIcon(":/src/1.png"));

    this->lb1 = new QLabel();
    this->lb2 = new QLabel();
    this->lb3 = new QLabel();

    this->lb1->setFixedSize(QSize(560,200));
    QPixmap pic;
    pic.load(":/src/2.png");
    //this->lb1->setPixmap(pic.scaled(this->lb1->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
    this->lb1->setPixmap(QPixmap(":/src/2.png"));

    this->lb2->setText(tr("用户名:"));
    this->lb3->setText(tr("密  码:"));

    this->usr_name_le = new QLineEdit();    //这两个参数后面传入信号中去,然后emit发射出去
    this->usr_pass_le = new QLineEdit();

    this->usr_pass_le->setEchoMode(QLineEdit::Password);

    this->bnt_login = new QPushButton(tr("登  陆"));
    this->bnt_register = new QPushButton(tr("注  册"));

    this->hb1 = new QHBoxLayout();
    this->hb2 = new QHBoxLayout();
    this->hb3 = new QHBoxLayout();

    this->hb1->addWidget(this->lb2);
    this->hb1->addWidget(this->usr_name_le);

    this->hb2->addWidget(this->lb3);
    this->hb2->addWidget(this->usr_pass_le);

    this->hb3->addWidget(this->bnt_login);
    this->hb3->addWidget(this->bnt_register);

    this->vb1 = new QVBoxLayout();
    this->vb1->addWidget(this->lb1);
    this->vb1->addLayout(this->hb1);
    this->vb1->addLayout(this->hb2);
    this->vb1->addLayout(this->hb3);

    this->setLayout(this->vb1);

}

void mylogin::do_login()
{
    emit sig_login(usr_name_le->text(), usr_pass_le->text());
    //需要把这里输入的账号密码信息发送到myapp那去,用到的函数是emit
    //需要自定义一个信号,sig_login
    //这个槽函数能够发出信号
}

void mylogin::do_enroll()
{
    emit sig_enroll();
}

myenroll.cpp

#include "myenroll.h"

myenroll::myenroll(QWidget *parent) : QWidget(parent)
{
    init_ui();
    connect(this->bnt1,&QPushButton::clicked,this,&myenroll::send_msg);
}


void myenroll::init_ui()
{
    this->setFixedSize(QSize(600,350));
    this->setWindowTitle(tr("信息注册"));
    this->setWindowIcon(QIcon(":/src/1.png"));


    name = new QLineEdit;           //用于写入名字
    pass = new QLineEdit;           //用于写入密码
    lb1 = new QLabel;
    lb2 = new QLabel;

    bnt1 = new QPushButton;

    hb1 = new QHBoxLayout;
    hb2 = new QHBoxLayout;
    hb3 = new QHBoxLayout;

    vb1 = new QVBoxLayout;

    this->lb1->setText(tr("请输入账号:"));
    this->lb2->setText(tr("请输入密码:"));
    this->bnt1->setText(tr("确认"));

    this->hb1->addWidget(lb1);
    this->hb1->addWidget(name);
    this->hb2->addWidget(lb2);
    this->hb2->addWidget(pass);
    this->hb3->addWidget(bnt1);

    this->vb1->addLayout(hb1);
    this->vb1->addLayout(hb2);
    this->vb1->addLayout(hb3);

    this->setLayout(vb1);


}

void myenroll::send_msg()
{
    emit sig_enroll_info(name->text(),pass->text());
}


 chatinterface.cpp

#include "chatinterface.h"

chatInterface::chatInterface(QWidget *parent) : QWidget(parent)
{
    this->init();

}


void chatInterface::init()
{
    this->setFixedSize(QSize(600,900));
    this->setWindowTitle(tr("大飞秋"));
    this->setWindowIcon(QIcon(":/src/1.png"));

    lb1 = new QLabel;
    le1 = new QLineEdit;
    te1 = new QTextEdit;
    bnt1 = new QPushButton;
    hb1 = new QHBoxLayout;
    vb1 = new QVBoxLayout;

    this->lb1->setFixedSize(QSize(565,80));
    QPixmap pic;
    pic.load(":/src/3.jpg");
    this->lb1->setPixmap(pic.scaled(this->lb1->size()));

    this->te1->setFixedSize(QSize(560,700));
    this->te1->setStyleSheet(QString("background-color:") + "white");


    this->le1->setFixedSize(QSize(450,50));
    this->bnt1->setText(tr("发送"));
    this->bnt1->setFixedSize(QSize(100,50));

    this->hb1->addWidget(le1);
    this->hb1->addWidget(bnt1);
    this->vb1->addWidget(lb1);
    this->vb1->addWidget(te1);
    this->vb1->addLayout(hb1);

    this->setLayout(vb1);

    this->mysock = new QTcpSocket();
    this->mysock->connectToHost("192.168.4.32",8888);    //需查看自己的本机ip写入
    connect(this->mysock, &QTcpSocket::connected, this, &chatInterface::connect_success_msg);
    connect(this->mysock, &QTcpSocket::readyRead, this, &chatInterface::recv_msg_slots);
    connect(this->bnt1, &QPushButton::clicked, this, &chatInterface::send_msg_slots);




}



void chatInterface::connect_success_msg()
{
    qDebug() << "链接服务器成功";
}

void chatInterface::recv_msg_slots()
{
    QByteArray con = this->mysock->readAll();
    QString *str = new QString(con);
    this->te1->append(*str);
}


void chatInterface::send_msg_slots()
{

    this->te1->append(this->le1->text());
    this->mysock->write( (this->le1->text()).toUtf8());
}




tips.cpp

#include "tips.h"

tips::tips(QWidget *parent) : QWidget(parent)
{

}

void tips::loginfail()
{
    QMessageBox msg;
    msg.warning(this,tr("登录提示"),tr("账号或密码错误,请重新登录!"));
}
void tips::insertok()
{
    QMessageBox msg;
    msg.warning(this,tr("信息注册"),tr("用户注册成功!"));
}

 myapp.cpp

#include "myapp.h"

myapp::myapp(QObject *parent) : QObject(parent)
{

    //创建并打开SQLite数据库
    this->mydb = new SqOperator;
    mydb->openDb();
    //创建数据表
    mydb->createTable();

    //这里分别新建的是登录和注册两个对象
    this->login = new mylogin;
    this->login->show();
    this->enroll = new myenroll;

    this->face = new chatInterface;
    this->tip = new tips;

    connect(login,&mylogin::sig_login,this,&myapp::judge);
    connect(login,&mylogin::sig_enroll,this,&myapp::show_enroll_face);
    connect(enroll,&myenroll::sig_enroll_info,this,&myapp::insertdb);

}


bool myapp::judge(QString usrname, QString usrpass)
{
    qDebug()< list;
    mydb->queryTable(list);

    int i = 0;
    //当存在两个账号的时候,这里循环里面的if必定会进去,错误和正确都会提示,应该在正确之后直接结束判断,而错误提示则应该放在循环结束
    for(i = 0 ; i < list.size() ; i=i+2)
    {

        if(usrname == list[i] || usrpass == list[i+1])
        {

            this->face->show();
            return true;
        }
    }

    this->tip->loginfail();
    return false;

}
void myapp::show_enroll_face()
{
    this->enroll->show();
}

void myapp::insertdb(QString usrname,QString usrpass)
{
    qDebug()<singleInsertData(info1))
    {
        this->tip->insertok();
    }
}

main.cpp

#include "myapp.h"

#include 

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

 程序至此,只是写了客户端的部分,下面是服务端

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include 
#include 
#include 
#include 
#include 

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
public:
    QTcpServer *myser;
    QList  myclis;//是一个QTcpSocket *类型的容器,可实现多个客户端socket的存放

    QTextEdit *myte;
    QHBoxLayout *hb1;

private slots:
    void accept_client();
    void do_work();
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    this->myte = new QTextEdit();
    this->hb1 = new QHBoxLayout();

    this->hb1->addWidget(this->myte);
    this->setLayout(this->hb1);

    this->myser = new QTcpServer();
    this->myser->listen(QHostAddress::AnyIPv4,8888);    //IP协议和端口号
    this->myclis.clear();

    connect(this->myser,&QTcpServer::newConnection,this,&Widget::accept_client);
}

Widget::~Widget()
{

}



void Widget::accept_client()
{
    QTcpSocket * mysck = this->myser->nextPendingConnection();
    this->myclis.append(mysck); //接收客户端的连接请求
    connect(mysck,&QTcpSocket::readyRead,this,&Widget::do_work);//接收到信号后准备读,所以,我们需要一个读的槽函数去接收这个准备好的信号

}


void Widget::do_work()//实现
{
    QTcpSocket *sck = (QTcpSocket *)sender();
    QByteArray con = sck->readAll();
    //遍历
    for(int i = 0;i < this->myclis.size();i++)
    {
        if(this->myclis[i] == sck)
        {
            continue;
        }
        this->myclis[i]->write(con);
    }

}

 main.cpp

#include "widget.h"
#include 

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

    return a.exec();
}

你可能感兴趣的:(QT,qt,c++,sqlite,tcp/ip,数据库)