基于TCP、QT制作的小型聊天系统


实现功能:

  • 好友列表刷新功能
  • 单聊(发送消息、发送图片)
  • 接收到的消息均有系统时间记录
  • 虚拟键盘(利用事件过滤器,设置在点击编辑框时虚拟键盘才会自动弹出)
  • 实时时钟显示(利用定时器,持续获取系统时间)
  • 手写画板(利用了QT中的事件、能更改痕迹的粗细大小、更改笔的颜色、截图保存此绘画)
  • 收消息框、写消息框、好友列表框均可手动调整大小(QT中设置这些输入框布局  分列式垂直水平布局)
  • 截图 、消息记录

尚存BUG:

        保存消息记录时还不能保存图片数据。

Client  客户端登录界面代码

  • 如果密码输入三次错误、密码输入栏会自动锁死
  • 因为初学时为了理解学习、此界面没有使用UI设计师设计、而是采用纯代码编辑
  • 默认自动弹出虚拟键盘、如果点击输入栏以外的位置会隐藏虚拟键盘

 

#include "longinwindow.h"
#include "ui_longinwindow.h"
#include 

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


    //this->setStyleSheet("background-color: rgb(255, 255, 255);");
    this->setStyleSheet("LonginWindow{border-image: url(:/new/prefix1/1.jpg);}");

    //this->show();

    usrline=new QLineEdit(this);
    //指定输入框显示的位置
    usrline->setGeometry(100,20,250,30);
    usrline->show();

    //创建一个输入框框对象
    passline = new QLineEdit (this);
    //隐藏输入的密码
    passline->setEchoMode(QLineEdit::Password);
    //指定输入框显示的位置
    passline->setGeometry(100,80,250,30);
    passline->show();


    usrline->installEventFilter(this);      //给用户输入栏添加事件过滤器
    passline->installEventFilter(this);

    //创建一个标签表示用户名
    usrlb =new QLabel(this);
    //QPalette pa;
    //定义字体对象
    QFont myfont;
    myfont.setPixelSize(20);            //设置字体大小
    myfont.setFamily("KaiTi");          //将字体设置为楷体
    //myfont.setBold(75);       //也能设置字体加粗,只是会报错截断
    myfont.setWeight(75);       //设置字体加粗

    usrlb->setStyleSheet("color:red;");          //将字体设置为红色
    //color: rgb(255, 0, 0);  //设置字体颜色
    usrlb->setFont(myfont);
    usrlb->setText("用户:");
    usrlb->setGeometry(10,20,50,30);
    usrlb->show();

    //创建一个标签表示密码

    passlb = new QLabel("密码: ",this);
    myfont2.setPixelSize(20);
    passlb->setStyleSheet("color:red;");          //将字体设置为红色
    passlb->setFont(myfont2);
    passlb->setGeometry(10,80,50,30);            //设置标签位置
    passlb->show();

    //登陆按钮

    lgbt = new QPushButton(QIcon(":/new/prefix1/2.jpg"),"登陆",this);
    lgbt->setGeometry(140,150,70,30);
    lgbt->show();

    //注册按钮

    resbt = new QPushButton(QIcon(":/new/prefix1/3.jpg"),"注册",this);
    resbt->setGeometry(270,150,70,30);
    resbt->show();

    connect(lgbt, SIGNAL(clicked(bool)), this ,SLOT(LonginBtSlot()));
}

LonginWindow::~LonginWindow()
{
    delete ui;
    delete usrline;
    delete passline;
    delete usrlb;
    //QFont myfont;
    //QFont myfont2;
    delete passlb;
    delete lgbt;
    delete resbt;
}

bool LonginWindow::eventFilter(QObject *watched, QEvent *event)
{
    if (event->type() == QEvent::FocusIn)
    {
          ui->widget->show();
    }
    else if(event->type() == QEvent::KeyPress)
    {
        QKeyEvent *kE = (QKeyEvent *)event;
        if (kE->key() == Qt::Key_Enter)
        {
            ui->widget->hide();
        }

    }
    else if(event->type() ==QEvent::FocusOut)
    {
          ui->widget->hide();
    }
    return QMainWindow::eventFilter(watched,event);
}

void LonginWindow::LonginBtSlot()
{
    qDebug()<<"我已经点击了登录按钮: ";
    static int num=0;               //记住输入错误的次数
    QString usrname=usrline->text();
    QString pas=passline->text();
    if(usrname=="xgh"&&pas=="123")
    {
        qDebug()<<"我已登录 ";
        this->hide();
        clientwin *clientw =new clientwin(this);
        clientw->show();
    }
    else
    {
        num++;
    }
    if(num>3)
    {
         passline->setEnabled(false);
    }
}

客户端登录功能界面设计

基于TCP、QT制作的小型聊天系统_第1张图片

 Client 客户端功能代码,区分图片和消息数据,项目过程中遇到发送大图片会出现只接收到一部分的情况;最后采用

QDataStream类解决
#include "clientwin.h"
#include "ui_clientwin.h"
#include 
#include 
#include "longinwindow.h"

clientwin::clientwin(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::clientwin)
{
    ui->setupUi(this);
    ui->widget->hide();
    ui->chatedit->installEventFilter(this);

    timer = new QTimer(this);
    timer->start(1000);

    timer->connect(timer, SIGNAL(timeout()), this, SLOT(timerUpdate()));

    qDebug()<<"定时器定时开始 ";

    mysocket = new QTcpSocket();

    mysocket->connectToHost(QHostAddress("192.168.24.151"), 10000);



    connect(mysocket,SIGNAL(connected()),this,SLOT(onconnect()));

    connect(mysocket,SIGNAL(disconnected()),this,SLOT(ondisconnect()));
    connect(mysocket,SIGNAL(readyRead()),this,SLOT(onreadyread()));
    //connect(mysocket,SIGNAL(error(QAbstractSocket::SocketError socketError)),this,SLOT(ondisconnect()));

    connect(ui->friendlist,SIGNAL(itemSelectionChanged()),this,SLOT(on_selectchat()));

    imageIndex=0;
    savepacksize=0;

}

clientwin::~clientwin()
{
    message_save_on_local();
    delete ui;
    delete timer;
    delete mysocket;
}



/*
    Html图片格式


这个是物理路径,如果是网站的话,用相对路径则为下面的:

这个为html文件和images同一目录的写法

这个为图片和html文件同一目录的写法

*/

void clientwin::onreadyread()
{

    QObject *obj=this->sender();
    QTcpSocket *socket=qobject_cast(obj);


    //当前缓冲区里面数据的大小,收到的数据大小。
    qint64 sizeNow=0;
    do
    {
        sizeNow = socket->bytesAvailable();
        QDataStream stream(socket);
        if(savepacksize==0)
        {

            if(sizeNow> savepacksize;
        }
            //包不完整
        if(sizeNow < savepacksize -4 ) //64位和32位的区别少了4个字节 处理了半包情况
        {

            return;
        }
        savepacksize=0;
        //包已经完整
        qDebug()<<"full pack";
        QByteArray dataFull;
        stream >> dataFull;

        //判断剩下的字节数,是否会有粘包的情况。
         sizeNow = socket->bytesAvailable();
         QString dataTime=QDateTime::currentDateTime().toString("yy-MM-dd hh:mm:ss:");
         QString preindex = dataFull.mid(0,4);

         if(preindex=="TXT:")
         {
            ui->textEdit->append(dataTime);
            QString txtconcent=dataFull.mid(4);
            ui->textEdit->append(txtconcent);
          }
          else if(preindex=="IMG:")
          {

                QString htmlTag=QString ("");
                QString index=QString::number(imageIndex);
                htmlTag=htmlTag.arg(index + ".png");

                QFile file(index+".png");
                file.open(QIODevice::WriteOnly);
                file.write(dataFull.mid(4));
                file.close();

                imageIndex=imageIndex+1;
                ui->textEdit->append(dataTime);
                ui->textEdit->insertHtml(htmlTag);

          }
          else if(preindex=="RIP:")                 //如果接收到服务器发来的所有已经连接上服务器的客户端Ip端口号
          {
             qDebug()<<"服务器已经发来客户端IP信息 ";
             int i;
             int n=ui->friendlist->count();//获取item的总数
             //删去所有item
             for(int i=0;ifriendlist->takeItem(0); //这里是0,不是i,因为每移除一个item都会导致每个item的row发生变化
                 delete item;
             }
             QString  tmpdata;
             tmpdata=dataFull.mid(4);

             if(!tmpdata.isEmpty())
             {

                 QStringList clientlist =tmpdata.split(":");
                 for(i=0;ifriendlist->addItem(clientlist.at(i));
                  }
             }

          }
    }while(sizeNow>0);  //如果剩余的字节数大于0的话
}

void clientwin::onconnect()
{
    qDebug()<<"我已经连接上服务器 ";
}

void clientwin::ondisconnect()
{
    qDebug()<<"我已经断开了连接 ";
    qDebug()<<"has disconnect";

    //message_save_on_local();
    qDebug()<<"111";
    QObject *obj=this->sender();
    QTcpSocket *socket=qobject_cast(obj);     //强转
    socket->deleteLater();
}

void clientwin::onerror(QAbstractSocket::SocketError socketError)
{
    qDebug()<<"error "<chatedit->toPlainText();
    if(text.isEmpty())   //判断如果输入框是空的返回不发送
        return;

    sendtextt="TXT:"+text.toLocal8Bit();

    qDebug()<<"sendtextt "<seek(0);
    stream<write(dataSend);
}

void clientwin::on_sendpicBt_clicked()  //发送表情包按钮
{
    qDebug()<<"我已经按了发送表情包按钮 ";
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
                                                     "/",
                                                     tr("Images (*.png *.xpm *.jpg)"));

    if(fileName.isEmpty())
            return;

    QFile image(fileName);
    image.open(QIODevice::ReadOnly);
    QByteArray data="IMG:"+ image.readAll();
    image.close();

    //出现了丢包情况  封装包头
    QByteArray dataSend;    //封装的数据包

    QDataStream stream(&dataSend,QIODevice::WriteOnly);
    stream<<(quint32)0<< data;
    stream.device()->seek(0);
    stream<write(dataSend);


}

void clientwin::on_selectchat()
{
    QString itemtextt;
    QStringList list;
    QByteArray dome2;
    itemtextt=ui->friendlist->currentItem()->text();

    qDebug()<<"item text "<seek(0);
    stream<write(dataSend);

}

void clientwin::timerUpdate()
{

    QDateTime time = QDateTime::currentDateTime();
    QString str = time.toString("HH:mm:ss");
    ui->lcdNumber->display(str);

}


bool clientwin::eventFilter(QObject *watched, QEvent *event)       //配合事件过滤器使用
{
    if (event->type() == QEvent::FocusIn)
    {
          ui->widget->show();
    }
    else if(event->type() == QEvent::KeyPress)
    {
        QKeyEvent *kE = (QKeyEvent *)event;
        if (kE->key() == Qt::Key_Enter)
        {
            ui->widget->hide();
        }

    }
    else if(event->type() ==QEvent::FocusOut)
    {
          ui->widget->hide();
    }
    return QMainWindow::eventFilter(watched,event);
}

void clientwin::message_save_on_local()     //保存聊天记录到本地
{

    QByteArray massage=ui->textEdit->toPlainText().toLocal8Bit();
    QString filename=mysocket->peerAddress().toString()+".txt";
    QFile history(filename);
    history.open(QIODevice::ReadWrite|QIODevice::Append);
    history.write(massage);
    history.close();
}


void clientwin::on_friendBT_clicked()
{
    //点击按钮连接上服务器

    qDebug()<<"flash friend ";
    QString scmd="wofasongleshengqingcmd";
    QByteArray cmd="FLH:"+scmd.toLocal8Bit();

    //出现了丢包情况  封装包头
    QByteArray dataSend;    //封装的数据包

    QDataStream stream(&dataSend,QIODevice::WriteOnly);
    stream<<(quint32)0<seek(0);
    stream<write(dataSend);
}

void clientwin::on_catpicBt_clicked()       //截图按钮
{
    qDebug()<<"jie tu ";
    QPixmap pix=QApplication::screens().at(0)->grabWindow(QWidget::winId());
    //保存起来
    QString savefilepath=QFileDialog::getSaveFileName(this);
    //调用QPixmap中的save函数保存截图
    pix.save(savefilepath);
}

void clientwin::on_paintBt_clicked()    //画板
{
    message_save_on_local();        //在断开连接的时候把消息记录保存下来
    this->hide();

    Painter *p=new Painter(this);
    p->show();
}

void clientwin::on_backBt_clicked()
{
    ui->textEdit->append("我已经点击了返回按钮");
    qDebug()<<"我已经点击了返回按钮";
    this->hide();
    LonginWindow *ll =new LonginWindow(this);
    ll->show();
}

void clientwin::on_historicBt_clicked()
{
    QByteArray massage;
    QString massagestr;
    QString filename=mysocket->peerAddress().toString()+".txt";
    QFile history(filename);
    if(history.open(QIODevice::ReadOnly)==false)
    {
        return;
    }
    while(1)
    {
        massage.clear();
        massage=history.read(100);
        ui->textEdit->append(massage);
        if(massage.length()<100)
            break;
    }
    history.close();
}

 下载完整代码地址

https://download.csdn.net/download/switchandcase/11983903


 

你可能感兴趣的:(QT)