34 QT - 聊天案例

简介: 34 QT - 聊天案例

1. 新建项目

1.1 创建新项目

第一步打开Qt Creator,点击新建NewProject

Application –> Qt Widgets Application -> choose

创建项目名称例如: MyselfQQ,路径自己选择,注意不要有空格和中文

选择套件,点击下一步

选择基类QWidget,然后点击下一步

然后点击完成,至此项目创建完毕。

2. 创建对话列表

2.1 添加新文件,对话列表类DialogList

右击项目名,在弹出的快捷菜单中选择“添加新文件…”菜单项,在弹出的对话框中选择“Qt”选项。选择Qt设计师界面类,单击“Choose…”按钮;

界面模板选择Widget,点击下一步

类名填写 DialogList (可以起其他名称)点击下一步

在汇总中单击“完成”按钮,系统会为我们添加“dialoglist.h”头文件和“dialoglist.cpp”源文件以及dialoglist.ui设计文件

2.2 设计对话框列表UI

按照下图所示,设计对话框的UI,该窗口的大小为 250*700,其中的主要控件是QToolBox,修改该控件的currentItemText为“群成员”,QToolBox默认生成的第二页删除掉

在群成员里我们放入一个Widget做布局操作,可以先利用一些测试控件放入到其中,然后做垂直布局,然后把测试的控件删除掉,这时该Widget中就有了一个layout布局

测试,main函数中修改代码

运行项目,效果如图

2.3 资源导入

向项目中导入资源,对应九个按钮需要九张图片作为头像图标使用,搜集九张图片(可用共享的资源或者自己收藏的图片,大小在80*80左右)

添加新文件 – Qt – Qt Resource 点击choose 名称 res 下一步,点击完成,生成res.qrc文件

右击res.qrc,点击open in Editor ,添加前缀 /

添加文件 – 将准备好的文件选中,点击打开,添加成功

2.4 设置窗体标题和图标

2.4.1设置标题

在Drawer构造函数中加入如下代码

setWindowTitle(“Myself QQ 2017”);

2.4.2 设置图标

设置主窗体标题图标

setWindowIcon(QPixmap(“:/images/qq.png”));

2.5设置列表中的按钮

2.5.1 创建9个按钮存放到QVector容器中
QVector<QToolButton *>vToolBtn; //声明存放QtoolButton的容器 vToolBtn
    for(int i = 0 ;i < 9 ; i ++)
    {
        QToolButton * btn = new QToolButton;  //创建新按钮
        btn->setText("斧头帮帮主");     //设置按钮名称
        btn->setIcon(QPixmap(":/images/ftbz.png"));   //设置图片
        btn->setIconSize(QPixmap(":images/ftbz.png").size()); //设置图片大小
        btn->setAutoRaise(true);  //设置图片透明效果
        btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);//设置按钮风格,同时显示文字和图标
        ui->vLayout->addWidget(btn); //将按钮添加到布局中
        vToolBtn.push_back(btn); //将9个按钮放入到布局中
  }

运行效果如图:(有点辣眼睛。。。)

2.5.2 替换图片,改为不同的资源

再次运行,效果如图

3. 设计聊天窗口

3.1 界面

双击widget.ui文件进入设计模式,界面宽度属性分别设置为730450,向界面中拖入部件并且进行设置,最终效果如图:

3.2 控件类型和属性设置

3.2.1 各个控件设置

如上图中标注的1~8,下表中为上图的属性设置以及控件类型

3.2.2 ToolBtn详细设置

其中前三个按钮 ,选中 checkable 属性;

其中所有的ToolBtn属性中的toolTIp依次更改为 加粗、倾斜、下划线、更改字体颜色、保存聊天记录和 清空聊天记录

3.2.3字体大小下拉框设置

界面上 5号控件设置字体大小,设置区间为8~22(与腾讯QQ软件完全相同),双击该部件,点击 + 号按钮添加新项目如图:

currentIndex属性设置为4,即默认为12号字

3.2.4 TableWidget设置

显示用户列表的TableWidget控件,将selectionModel属性选择为 SingleSelection(带有选中效果),将selectBehavior选择为 SelectRows(选中整行),取消选中的showGrid(表格显示)

双击TableWidget部件,添加“用户名”列,如图:

至此,所有弹出的聊天信息窗口全部设计完毕

4 关联图片按钮与聊天窗口

4.1 添加按钮点击事件

下一步就是要关联起聊天的列表窗口和具体的聊天信息窗口了,也就是点击按钮弹出窗口。

//添加点击事件
    for(int i = 0 ; i < vToolBtn.size();i++)
    {
        connect(vToolBtn[i],&QToolButton::clicked,this,[=]()
        {
            //qDebug() <<i;
            //此时,widget的构造函数已经修改,(见4.2步骤)创建widget窗口时,参数1 :0代表以顶层方式弹出
            //参数2:代表vToolBtn[i]->text()代表告诉聊天窗口 人物的姓名
           Widget *chatWidget = new Widget(0,vToolBtn[i]->text());
           chatWidget->setWindowTitle(vToolBtn[i]->text());
           chatWidget->setWindowIcon(vToolBtn[i]->icon());
           chatWidget->show();
        });
    }

4.2 修改弹出框的构造函数

!!! Widget文件中的 构造改为explicit Widget(QWidget *parent ,QString usrname); 参数2 用于传入用户名,让具体的弹出框知道自己的用户名是什么

4.3 测试

运行代码,点击窗口,弹出响应的对话框

4.4 解决一个窗口多次弹出的bug

在dialogList.h中加入标示

QVectorisShow; 代表是否打开窗口的标识,false未打开,true打开

dialogList.cpp构造中 将9个标志位都置为false

//九个标识位设置
    for(int i = 0 ; i < 9 ;i++)
    {
        isShow.push_back(false);
    }

修改点击事件,根据打开的标志位来判断

然后在widget函数中声明关闭的信号,和重写关闭方法

signals:
    void widgetClose();
protected:
   void closeEvent(QCloseEvent *);

然后实现重写的关闭方法,发送关闭信号

signals:
    void widgetClose();
protected:
   void closeEvent(QCloseEvent *);

然后实现重写的关闭方法,发送关闭信号

void Widget::closeEvent(QCloseEvent * e)
{
    emit this->widgetClose(); //发送关闭当前窗口的自定义信号
    QWidget::closeEvent(e);
}

点击事件中再添加一行代码

测试,一个窗口不能多次弹出

5. 实现基本聊天功能

widget中 定义枚举 enum MsgType {Msg,UsrEnter,UsrLeft} 分别代表 聊天信息、新用户加入、用户退出

5.1 声明聊天的方法

widge.h中

public:
   void sndMsg(MsgType type); //广播UDP消息
   void usrEnter(QString username);//处理新用户加入
   void usrLeft(QString usrname,QString time); //处理用户离开
   QString getUsr(); //获取用户名
   QString getMsg(); //获取聊天信息
private:
   QUdpSocket * udpSocket; //udp套接字
   qint16 port; //端口
   QString uName; //用户名
  void ReceiveMessage();   //接受UDP消息

5.2 widget.cpp中实现

构造函数中:

this->uName = usrname; //获取用户名
udpSocket = new QUdpSocket(this);
port = 23333;
udpSocket->bind(port,QUdpSocket::ShareAddress |QUdpSocket::ReuseAddressHint);  //采用ShareAddress模式(即允许其它的服务连接到相同的地址和端口,特别是用在多客户端监听同一个服务器端口等时特别有效),和ReuseAddressHint模式(重新连接服务器)
connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::ReceiveMessage); 监听信号
sndMsg(UsrEnter);//有新用户加入

发送消息函数 sndMsg

void Widget::sndMsg(MsgType type)
{
    QByteArray data;
    QDataStream out(&data,QIODevice::WriteOnly);
    out << type << getUsr();   //将消息类型 和 用户名 放入到流中
    switch (type) {
    case Msg:
        if(ui->msgTxtEdit->toPlainText() == "")
        {
            QMessageBox::warning(0,"警告","发送内容不能为空",QMessageBox::Ok);
            return;
        }
        out  <<getMsg();  //发送的是聊天信息    发送格式   消息类型 + 用户名   + 发送内容
        break;
    case UsrEnter:        //发送的是新用户进入  发送格式   消息类型 + 用户名
        break;
    case UsrLeft:         // 发送的是用户离开  发送格式    消息类型 + 用户名
        break;
    default:
        break;
    }
    udpSocket->writeDatagram(data,data.length(),QHostAddress::Broadcast,port);
}

接受消息 ReceiveMessage

void Widget::ReceiveMessage()
{
    QByteArray datagram;
    datagram.resize(udpSocket->pendingDatagramSize());
    udpSocket->readDatagram(datagram.data(),datagram.size());
    QDataStream in (&datagram,QIODevice::ReadOnly);
    int msgType;
    in >> msgType; //用户类型获取
    QString usrName,msg;  //用户名、信息
    QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
    switch (msgType){
    case Msg:
        in >> usrName  >>msg;
        ui->msgBrowser->setTextColor(Qt::blue);
        ui->msgBrowser->setCurrentFont(QFont("Times New Roman",12));
        ui->msgBrowser->append("[ " + usrName + " ]" + time);
        ui->msgBrowser->append(msg);
        break;
    case UsrEnter:
        in >> usrName ;
        usrEnter(usrName);
        break;
    case UsrLeft:
        in >> usrName;
        usrLeft(usrName,time);
        break;
    default:
        break;
    }
}

获取用户名

//获取用户名
QString Widget::getUsr()
{
    return uName;
}
获取聊天信息
//获取聊天信息
QString Widget::getMsg()
{
    QString msg = ui->msgTxtEdit->toHtml();
    ui->msgTxtEdit->clear();
    ui->msgTxtEdit->setFocus();
    return msg;
}

新用户进入

//处理新用户加入
void Widget::usrEnter(QString usrname)
{
    bool isEmpty = ui->usrTblWidget->findItems(usrname,Qt::MatchExactly).isEmpty();
    if(isEmpty)
    {
        QTableWidgetItem *usr = new QTableWidgetItem(usrname);
        ui->usrTblWidget->insertRow(0);
        ui->usrTblWidget->setItem(0,0,usr);
        ui->msgBrowser->setTextColor(Qt::gray);
        ui->msgBrowser->setCurrentFont(QFont("Times New Roman",10));
        ui->msgBrowser->append(tr("%1 在线!").arg(usrname));
        ui->usrNumLbl->setText(tr("在线人数:%1").arg(ui->usrTblWidget->rowCount()));
        //已经在线的各个端点也要告知新加入的端点他们自己的信息,若不这样做,在新端点用户列表中就无法显示其他已经在线的用户
        sndMsg(UsrEnter);
    }
}

用户离开

//用户离开
void Widget::usrLeft(QString usrname,QString time)
{
    int rowNum = ui->usrTblWidget->findItems(usrname, Qt::MatchExactly).first()->row();
    ui->usrTblWidget->removeRow(rowNum);
    ui->msgBrowser->setTextColor(Qt::gray);
    ui->msgBrowser->setCurrentFont(QFont("Times New Roman", 10));
    ui->msgBrowser->append(QString("%1 于 %2 离开!").arg(usrname).arg(time));
    ui->usrNumLbl->setText(QString("在线人数:%1").arg(ui->usrTblWidget->rowCount()));
}

重写离开Event void closeEvent(QCloseEvent *)

void Widget::closeEvent(QCloseEvent *e)
{
    emit this->widgetClose();
    sndMsg(UsrLeft);
    udpSocket->close();
    udpSocket->destroyed();
    QWidget::closeEvent(e);
}

发送按钮和离开按钮信号槽连接

connect(ui->sendBtn,&QPushButton::clicked,this,[=](){
        sndMsg(Msg);
    });
    connect(ui->exitBtn,&QPushButton::clicked,this,[=]()
    {
        this->close();
    });

至此可以测试聊天功能

6. 辅助功能

6.1 字体设置

connect(ui->fontCbx,&QFontComboBox::currentFontChanged,this,[=](const QFont &f){
       ui->msgTxtEdit->setCurrentFont(f);
       ui->msgTxtEdit->setFocus();
   });

6.2 字号设置

void (QComboBox:: * cbxSingal)(const QString &text) = &QComboBox::currentIndexChanged;
  connect(ui->sizeCbx,cbxSingal,this,[=](const QString &text){
      ui->msgTxtEdit->setFontPointSize(text.toDouble());
      ui->msgTxtEdit->setFocus();
  });

6.3 加粗

connect(ui->boldTBtn,&QToolButton::clicked,this,[=](bool checked){
      if(checked)
      {
          ui->msgTxtEdit->setFontWeight(QFont::Bold);
      }
      else
      {
          ui->msgTxtEdit->setFontWeight(QFont::Normal);
      }
      ui->msgTxtEdit->setFocus();
  });

6.4 倾斜

connect(ui->italicTBtn,&QToolButton::clicked,this,[=](bool checked){
        ui->msgTxtEdit->setFontItalic(checked);
        ui->msgTxtEdit->setFocus();
    });

6.5下划线

connect(ui->underlineTBtn,&QToolButton::clicked,this,[=](bool checked){
        ui->msgTxtEdit->setFontUnderline(checked);
        ui->msgTxtEdit->setFocus();
    });

6.6 设置文本颜色

connect(ui->colorTBtn,&QToolButton::clicked,this,[=](){
       color = QColorDialog::getColor(color,this); //color对象可以在widget.h中定义私有成员
        if(color.isValid())
        {
            ui->msgTxtEdit->setTextColor(color);
            ui->msgTxtEdit->setFocus();
        }
    });

6.7 保存聊天记录

connect(ui->saveBtn,&QToolButton::clicked,this,[=](){
            if(ui->msgBrowser->document()->isEmpty())
            {
                QMessageBox::warning(this,"警告","聊天记录为空,无法保存!",QMessageBox::Ok);
            }
            else
            {
                QString fName = QFileDialog::getSaveFileName(this,"保存聊天记录","聊天记录","(*.txt)");
                if(!fName.isEmpty())
                {
                    //保存名称不为空再做保存操作
                    QFile file(fName);
                    file.open(QIODevice::WriteOnly | QFile::Text);
                    QTextStream stream(&file);
                    stream << ui->msgBrowser->toPlainText();
                    file.close();
                }
            }
        });

6.8 清空聊天记录

connect(ui->clearTBtn,&QToolButton::clicked,[=](){
        ui->msgBrowser->clear();
});

至此本案例全部完成!

目录
相关文章
|
7月前
|
数据挖掘 C++
QT基础入门——项目案例(七)
QT基础入门——项目案例(七)
405 0
QT基础入门——项目案例(七)
|
7月前
|
IDE 开发工具 C++
QT案例IDE编写 -- 创建项目
QT案例IDE编写 -- 创建项目
92 0
|
5月前
|
网络协议
Qt中的网络编程(Tcp和Udp)运用详解以及简单示范案例
Tcp和Udp是我们学习网络编程中经常接触到的两个通讯协议,在Qt也被Qt封装成了自己的库供我们调用,对于需要进行网络交互的项目中无疑是很重要的,希望这篇文章可以帮助到大家。 是关于Qt中TCP和UDP的基本使用和特点:
848 7
|
5月前
基于QT实现的QQ聊天简易版(UDP通信版)
源码已经给小伙伴们整理好了,微信搜索 嵌入式工程之家 关注公众号回复 QQ 即可获得源码和详细操作指示哦~
124 0
|
5月前
|
开发者
Qt中的事件该如何学习?(附带案例)
事件是Qt中比较重要的一部分,在初期如果理解不当学习可能会比较困难,这里提一嘴当初教我的那位老师水平是真的高,让我很轻易的就理解了事件的概念。 在平时我们见到那些界面上的某些快捷键就有可能是事件做的,例如ESC关闭窗口,Enter提交或者登录这种类似的,这也是事件的强大之处。
139 0
|
7月前
|
存储 自然语言处理
QT案例词典 -- 存储内容及遍历
QT案例词典 -- 存储内容及遍历
52 1
|
7月前
|
自然语言处理
QT案例词典 -- 释放堆区空间及查询单词
QT案例词典 -- 释放堆区空间及查询单词
51 1