QT之实现简陋聊天

相关知识:QT,数据库,TCP/IP,Socket;

1.登陆界面
包含登陆和注册两种功能,思路如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200608133411367.png
难点:建立服务器和数据库,数据库保存数据,服务器与数据库产生联系
解决:数据库与服务器放在同一个类中,登陆和注册时,客户端与服务端连接,传输数据给服务端,然后通过数据库来处理数据。

大致步骤:创建ui界面 => {connect(登陆按钮,客户端主动连接服务器),connect(服务器得到新连接,connect(客户端readReady,服务器读取数据))}
=> 客户端写入数据 ->服务器读取数据 => 使用数据库 => 根据数据输出信息/注册账号。

2.实现聊天
如下为基本功能和实现思路:
在这里插入图片描述

参考博文:

  1. https://blog.csdn.net/weixin_40011728/article/details/77924196
  2. https://www.cnblogs.com/yuweifeng/p/9382841.html
  3. https://www.cnblogs.com/lifexy/p/10921958.html

上述博文的内容比我的硬核多了…

新发现一个难点:一个服务器连接多个客户端时,要想清楚怎么去确定不同的客户端,之前的错误代码使得一个客户端登陆,结果包括没连接的客户端都登陆了。

效果展示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
数据库展示:
在这里插入图片描述

可以说就实现了正常软件基本功能中的一小部分,后续检查发
现注册信息为空没有设置…
如果要优化界面应该不会比做这些功能难,拿QPaint画画,搞些和文件相关的连接应该就可以。
上面两条应该就是体力活了,但是聊天软件本来应该像QQ一样,最起码能加好友,但是我目前没有想好怎么做,这类知识全是第一次接触,可以说大半部分照着参考博文抄来的,查了很多资料去了解各种函数,有时候数据库的操作失败也是莫名其妙的,再继续下去不如系统地学点知识再来做一回。

全部代码如下:
mylogin.h

#ifndef MYLOGIN_H
#define MYLOGIN_H

#include <QWidget>

#include <QTcpServer>
#include <QTcpSocket>

#include <QMessageBox>

#include "myregister.h"
#include "server_sqlite.h"

namespace Ui {
class MyLogin;
}

class MyLogin : public QWidget
{
    Q_OBJECT

public:
    explicit MyLogin(QWidget *parent = nullptr);
    ~MyLogin();
    void clientSend(QString message);

signals:
    void toSignUp();
    void sendContent(QString content);
    void createUi(QString);
    void refreshList(QString);

private slots:
    void signIn();
    void clientRead();
    void updateSign();

private:
    Ui::MyLogin *ui;
    QTcpSocket *clientTcp;
};

#endif // MYLOGIN_H


mylogin.cpp

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

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

    connect(ui->pushButton_Login,&QPushButton::clicked,this,&MyLogin::signIn);
    connect(ui->pushButton_Register,&QPushButton::clicked,this,&MyLogin::toSignUp);

}

MyLogin::~MyLogin()
{
    clientTcp->close();
    delete ui;
}

void MyLogin::signIn(){
    clientTcp = new QTcpSocket(this);

    clientTcp->connectToHost("127.0.0.1",8888);

    if(!clientTcp->waitForConnected(20000)){
        QMessageBox::warning(this,"错误","连接超时");
        return;
    }
   // qDebug()<<"连接服务器成功!";

    QString name = ui->lineEdit_Name->text();
    QString password = ui->lineEdit_Password->text();
    if(""==name||""==password){
        QMessageBox::information(this,"提示","请输入完整登陆信息!");
        return;
    }
    QString tmp = name+";"+password;
  //  qDebug()<<"tmp:"<<tmp;
    clientTcp->write(tmp.toUtf8());//将完整信息写入传递给服务器
    connect(clientTcp,&QTcpSocket::readyRead,this,&MyLogin::updateSign);

//    //登陆成功后连接聊天的信号
//    connect(clientTcp,&QTcpSocket::readyRead,[=](){
//       emit
//    });
//    //connect(clientTcp,&QTcpSocket::readyRead,this,&MyLogin::clientRead);

}

void MyLogin::clientSend(QString message){
    //qDebug()<<"clientSend:"<<message;
    clientTcp->write(message.toUtf8());
}

void MyLogin::clientRead(){
    QTcpSocket *client = qobject_cast<QTcpSocket *>(sender());
    QString content = client->readAll();
    QStringList analysis = content.split("|");
    if("refreshFriendList"==analysis.at(0)){
        emit refreshList(analysis.at(1));
        return;
    }
   //qDebug()<<"emit:"<<content;
    emit sendContent(content);
}

void MyLogin::updateSign(){
    disconnect(clientTcp,&QTcpSocket::readyRead,this,&MyLogin::updateSign);
    connect(clientTcp,&QTcpSocket::readyRead,this,&MyLogin::clientRead);
    QString tmp = clientTcp->readAll();
    QStringList analysis = tmp.split("|");
    emit createUi(analysis.at(1));
}

myregister.h

#ifndef MYREGISTER_H
#define MYREGISTER_H

#include <QWidget>

#include <QTcpSocket>
#include <QTcpServer>

#include <QMessageBox>
#include <QDebug>



namespace Ui {
class MyRegister;
}

class MyRegister : public QWidget
{
    Q_OBJECT

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

private slots:
    void signUp();

private:
    Ui::MyRegister *ui;

    QTcpSocket *myReg;
};

#endif // MYREGISTER_H


myregister.cpp

#include "myregister.h"
#include "ui_myregister.h"

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

    connect(ui->pushButton_Sure,&QPushButton::clicked,this,&MyRegister::signUp);
}

MyRegister::~MyRegister()
{
    myReg->close();
    delete ui;
}

void MyRegister::signUp(){
    myReg = new QTcpSocket;
    myReg->connectToHost("127.0.0.1",8888);
    if(!myReg->waitForConnected(20000)){
        QMessageBox::warning(this,"错误","连接超时");
        return;
    }
    qDebug()<<"连接服务器成功!";

    QString name = ui->lineEdit_Name->text();
    QString pas1 = ui->lineEdit_Password->text();
    QString pas2 = ui->lineEdit_PasswordAgain->text();

    if(pas1 != pas2){
       QMessageBox::warning(this,"警告","两次输入密码不一致!");
       return;
    }

    QString temp = name+";"+pas1+";0";
    myReg->write(temp.toUtf8());
}


server_sqlite.h //服务器&数据库

#ifndef SERVER_SQLITE_H
#define SERVER_SQLITE_H

#include <QSqlDatabase>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QSqlError>

#include <QTcpServer>
#include <QTcpSocket>

#include <QMessageBox>

#include <QApplication>
#include <QObject>

#include <QDebug>

class Server_Sqlite:public QObject
{
    Q_OBJECT

public:
    Server_Sqlite();
    ~Server_Sqlite();
    void dataInit();
    void getChartRecord();

    QList<QTcpSocket *>onlineClients;//在线用户表
    QList<QString>onlineName;//在线用户昵称

private slots:
    void newClient();
    void serverRead();
    void serverDisconnect();
    void sendMessage();

private:
    QSqlDatabase db;

    QTcpServer *myServer;


};

#endif // SERVER_SQLITE_H


server_sqlite.cpp

#include "server_sqlite.h"

Server_Sqlite::Server_Sqlite()
{
     myServer = new QTcpServer;

     //监听,端口号:8888
     bool isOk = myServer->listen(QHostAddress::Any,8888);

     //监听失败
     if(false == isOk){
         QMessageBox::warning(NULL,"监听","8888监听失败!");
         return;
     }else{
         qDebug()<<"监听成功!";
     }

     //当有客户端连接时候,触发信号:newConnection()
     connect(myServer,&QTcpServer::newConnection,this,&Server_Sqlite::newClient);
}

Server_Sqlite::~Server_Sqlite(){
    db.close();
    myServer->close();
}

void Server_Sqlite::dataInit(){
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(QApplication::applicationDirPath()+"/scooters.dat");     //在本目录下生成
    db.open();

    QSqlQuery query;
//  query.exec("DROP TABLE clients");     //先清空一下表
//  query.exec("DROP TABLE chartRecord");
    if(!query.exec("CREATE TABLE clients ("
                       "name VARCHAR(40) NOT NULL, "
                       "password VARCHAR(40) NOT NULL,"
                       "score INTEGER NOT NULL)")){
        qDebug()<<"clients已被创建或出错!";
    }
                    //创建一个clients表

    query.exec("CREATE TABLE chartRecord ("
               "message VARCHAR(200) NOT NULL)");
}

void Server_Sqlite::newClient(){
    QTcpSocket *clientPort;
    clientPort = myServer->nextPendingConnection();
    //与对应的客户端产生连接
    connect(clientPort,&QTcpSocket::readyRead,this,&Server_Sqlite::serverRead);
    connect(clientPort,&QTcpSocket::disconnected,this,&Server_Sqlite::serverDisconnect);
    onlineClients.push_back(clientPort);
}

void Server_Sqlite::serverRead(){
    QTcpSocket *clientPort =qobject_cast<QTcpSocket *>(sender());

    bool flag = false;
    QList<QTcpSocket *>::iterator it;
    for(it = onlineClients.begin();it != onlineClients.end();it++){
        if((*it) == clientPort){
            flag = true; break;
        }
    }
    if(!flag)return;

    disconnect(clientPort,&QTcpSocket::readyRead,this,&Server_Sqlite::serverRead);
    //获取数据,查询数据库
    QString str = clientPort->readAll();
    qDebug()<<"读入数据"<<str;
    QStringList analysis = str.split(";");//str:name;password
    QString tmp = QString("select * from clients where name = '%1'").arg(analysis.at(0));
    qDebug()<<tmp;
    qDebug()<<"length="<<analysis.length();
    QSqlQuery query;
    query.exec(tmp);
    if(!query.next()){//在数据库中没找到该用户名
        qDebug()<<"没找到!";
        if(analysis.length() == 3){
            query.prepare("INSERT INTO clients (name,password,score) "
                          "VALUES(:name, :password, :score)");
            QString myName = analysis.at(0);
            QString myPassword = analysis.at(1);
            qDebug()<<myName<<myPassword;
            query.prepare("INSERT INTO clients (name, password, score) "
                          "VALUES (:name, :password, :score)");
                            //为每一列标题添加绑定值
                query.bindValue(":name", myName);
                query.bindValue(":password", myPassword);
                query.bindValue(":score", 0 );
                query.exec();               //加入库中
            QMessageBox::information(NULL,"提示","注册成功!");
            return;
        }
        QMessageBox::information(NULL,"提示","该用户尚未注册!");
        return;
    }else{//找到该用户名
        qDebug()<<"找到了!";
        if(analysis.length() == 3){
            QMessageBox::warning(NULL,"警告","该用户名已被注册!");
            return;
        }
        if(analysis.at(1) != query.value(1)){//0:name 1:password 2:score
            qDebug()<<query.value(1).toString();
            QMessageBox::warning(NULL,"提示","密码输入错误!");
            return;
        }else{
              int newScore =query.value(2).toInt()+1;
              tmp = QString("update clients set score = %1 where name = '%2'").arg(newScore).arg(analysis.at(0));
              query.exec(tmp);
        }
    }
   // qDebug()<<"来新客人了";
    onlineName.push_back(analysis.at(0));
    tmp.clear();
    QList<QString>::iterator it2;
    for(it2 = onlineName.begin(); it2 != onlineName.end(); ++it2){
        tmp+=";"+(*it2);
    }
    for(it = onlineClients.begin();it != onlineClients.end();it++){
        (*it)->write("refreshFriendList|"+tmp.toUtf8());
    }
    connect(clientPort,&QTcpSocket::readyRead,this,&Server_Sqlite::sendMessage);
}

void Server_Sqlite::sendMessage(){
        //哪一个QTcpSocketc对象可读就会发出readyRead()信号,通过信号的发出者找到相应的对象
        QTcpSocket *client = qobject_cast<QTcpSocket *>(sender());
        QString str = client->readAll();
        //群发
        QList<QTcpSocket *>::iterator it;
        for(it = onlineClients.begin();it != onlineClients.end();it++){
            qDebug()<<"群发:"<<str;
            (*it)->write(str.toUtf8());
        }
        QSqlQuery query;
        QString tmp = QString("insert into chartRecord (message) values ('%1')").arg(str);
        query.exec(tmp);
}

void Server_Sqlite::serverDisconnect(){//断开连接删去该用户
    QTcpSocket *client = qobject_cast<QTcpSocket *>(sender());
    onlineClients.removeOne(client);
    qDebug()<<"断开连接";
}


widgt.h//聊天窗口

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

#include <QObject>

//主窗口用户列表使用
#include <QTextBrowser>
#include <QLabel>

#include "server_sqlite.h"
#include "mylogin.h"
#include "myregister.h"

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 mainCreadUi(QString name);
    void refreshChart(QString content);
    void refreshList(QString names);

private:
    Ui::Widget *ui;
    QTextBrowser *xin;
    QLabel *record;

    Server_Sqlite *serSql;
    MyLogin *log;
    MyRegister *reg;
};
#endif // WIDGET_H


widget.cpp

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


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

//    serSql = new Server_Sqlite;
//    serSql->dataInit();

    this->setWindowTitle("群聊窗口");

    log = new MyLogin;

    connect(log,SIGNAL(createUi(QString)),this,SLOT(mainCreadUi(QString)));
    connect(log,&MyLogin::toSignUp,[=](){
        reg = new MyRegister;
        reg->show();
    });

    //发送消息写入聊天框
    connect(ui->pushButton_Send,&QPushButton::clicked,[=](){
       QString fasoner = ui->label_Name->text();
       QString message = ui->textEdit_Send->toPlainText();
       qDebug()<<fasoner<<message;
       log->clientSend(fasoner+":\n"+message);
    });
    connect(log,&MyLogin::sendContent,this,&Widget::refreshChart);
    //查看历史记录
    record = new QLabel(this);
    record->setText("消息记录:");
    record->move(565,40);
    xin = new QTextBrowser(this);
    xin->move(565,60);
    xin->setFixedSize(325,330);

    connect(ui->pushButton_ChartRecord,&QPushButton::clicked,[=](){
        if(ui->pushButton_ChartRecord->text()!="关闭记录"){
        this->setFixedSize(900,405);
        record->show();
        xin->show();
        ui->pushButton_ChartRecord->setText("关闭记录");
        qDebug()<<"数据库数据:";
            QSqlQuery query;
            query.exec("select * from chartRecord");
            while(query.next()){
                QString message = query.value(0).toString();
                xin->append(message);
        }
        }else{
            ui->pushButton_ChartRecord->setText("历史记录");
            record->close();
            xin->close();
            this->setFixedSize(565,405);
        }
    });

    //更新在线列表
    connect(log,SIGNAL(refreshList(QString)),this,SLOT(refreshList(QString)));

    log->show();
}

Widget::~Widget()
{
    qDebug()<<"数据库数据:";
        QSqlQuery query;
        query.exec("select * from chartRecord");
        while(query.next()){
            QString message = query.value(0).toString();
            qDebug()<<message;
    }
    delete ui;
}

void Widget::mainCreadUi(QString names){
    ui->textEdit_Friends->setReadOnly(true);
    QStringList analysis = names.split(";");
    ui->label_Name->setText(analysis.back());
    for(int i=0;i<analysis.length();i++){
        QString tmp = QString("select * from clients where name = '%1'").arg(analysis.at(i));
        int score;
        QSqlQuery query;
        query.exec(tmp);
        while(query.next()){
            score = query.value(2).toInt();//name;passowrd;score
            tmp.sprintf("%-15s%-5d",analysis.at(i).toStdString().data(),score);
            ui->textEdit_Friends->append(QString(tmp));
        }
    }
    QString name = analysis.back();
    this->show();
    log->close();
}

void Widget::refreshChart(QString content){
    ui->textEdit_Send->clear();
    ui->textBrowser_Chart->append(content);
}

void Widget::refreshList(QString names){
    ui->textEdit_Friends->clear();
    QStringList analysis = names.split(";");
    for(int i=0;i<analysis.length();i++){
        QString tmp = QString("select * from clients where name = '%1'").arg(analysis.at(i));
        int score;
        QSqlQuery query;
        query.exec(tmp);
        while(query.next()){
            score = query.value(2).toInt();//name;passowrd;score
            tmp.sprintf("%-15s%-5d",analysis.at(i).toStdString().data(),score);
            ui->textEdit_Friends->append(QString(tmp));
        }
    }
}

最后是main.cpp

#include "widget.h"
#include "server_sqlite.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Server_Sqlite tmp;
    tmp.dataInit();
    Widget w,w2,w3;
  //w.show();
    return a.exec();
}

  • 2
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
用c++/qt写的项目,项目都经测试过,真实可靠,可供自己学习c++/qtQt是一个用标准C++编写的跨平台开发类库,它对标准C++进行了扩展,引入了元对象系统、信号与槽、属性等特性,使应用程序的开发变得更高效。 Qt类库中大量的类以模块形式分类组织的,包括基本模块和扩展模块等。一个模块通常就是一个编程主题,如数据库、图表、网络等。 一、Qt核心特点 1.1.概述 Qt本身并不是一种编程语言,它本质上是一个跨平台的C++开发类库,是用标准C++编写的类库,它为开发GUI应用程序和非GUI应用程序提供了各种类。 Qt对标准C++进行了扩展,引入了一些新概念和功能,例如信号和槽、对象属性等。Qt的元对象编译器(Meta-Object Compiler,MOC)是一个预处理器,在源程序被编译前先将这些Qt特性的程序转换为标准C++兼容的形式,然后再由标准C++编译器进行编译。这就是为什么在使用信号与槽机制的类里,必须添加一个Q_OBJECT宏的原因,只有添加了这个宏,moc才能对类里的信号与槽的代码进行预处理。 Qt Core模块是Qt类库的核心,所有其他模块都依赖于此模块,如果使用qmake来构建项目,Qt Core模块则是被自动加入的。 Qt为C++语言增加的特性就是在Qt Core模块里实现的,这些扩展特性由Qt的元对象系统实现,包括信号与槽机制、属性系统、动态类型转换等。 1.2.元对象系统 Qt的元对象系统(Meta-Object-System)提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。 元对象系统由以下三个基础组成: 1.QObject类是所有使用元对象系统的类的基类; 2.在一个类的private部分声明Q_OBJECT宏,使得类可以使用元对象的特性,如动态属性、信号与槽。 3.MOC(元对象编译器)为每个QObject的子类提供必要的代码来实现元对象系统的特征。 构建项目时,MOC工具读取C++源文件,当它发现类的定义里有Q_OBJECT宏时,它就会为这个类生成另外一个包含有元对象支持代码的C++源文件,这个生成的源文件连同类的实现文件一起被编译和连接。 除了信号和槽机制外,元对象还提供如下一些功能。 1.QObject::metaObject()函数返回类关联的元对象,元对象类QMetaObject包含了访问元对象的一些接口函数,例如QMetaObject::className()函数可在运行时返回类的名称字符串。 QObject obj=new QPushButton; obj->metaObject()->className(); 2.QMetaObject::newInstance()函数创建类的一个新的实例。 3.QObject::inherits(const charclassName)函数判断一个对象实例是否是名称为className的类或QObject的子类的实例。 1.3.属性系统 1.属性定义 Qt提供一个Q_PROPERTY()宏可以定义属性,它也是属于元对象系统实现的。Qt的属性系统与C++编译器无关,可以用任何标准的C++编译器编译定义了属性的Qt C++程序。 2.属性的使用 不管是否用READ和WRITE定义了接口函数,只要知道属性名称,就可以通过QObject::property()读取属性值,并通过QObject::setProperty()设置属性值。 3.动态属性 QObject::setProperty()函数可以在运行时为类定义一个新的属性,称之为动态属性。动态属性是针对类的实例定义的。 动态属性可以使用QObject::property()查询,就如在类定义里用Q_PROPERTY宏定义的属性一样。 例如,在数据表编辑界面上,一些字段是必填字段,就可以在初始化界面时为这些字段的关联显示组件定义一个新的required属性,并设置值为“true"。 4.类的附加信息 属性系统还有一个宏Q_CLASSINFO(),可以为类的元对象定义”名称——值“信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JILIN.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值