• 基于QT Creator 5.14的仿QQ聊天系统【UDP通讯】


    一、使用工具

            本次整个项目的开发基于Qt Creator 5.14.2进行的开发与调试,主要通信功能采用UDPSocket完成,是我学习Qt过程中联系的小案例,过程中遇到的bug和完整代码会尽量展现出来,关于Qt以及其安装流程在CSDN里面有很多大佬写的教程,十分详细!!!

     二、软件实例效果图

     三、完整代码解读:

    dialoglist.h

    1. #ifndef DIALOGLIST_H
    2. #define DIALOGLIST_H
    3. #include
    4. #include
    5. namespace Ui {
    6. class DialogList;
    7. }
    8. class DialogList : public QWidget
    9. {
    10. Q_OBJECT
    11. public:
    12. explicit DialogList(QWidget *parent = nullptr);
    13. ~DialogList();
    14. QVector<bool> isshow; //判断当前窗口是否已被显示出
    15. private:
    16. Ui::DialogList *ui;
    17. };
    18. #endif // DIALOGLIST_H

     dialoglist.cpp

    1. #include "dialoglist.h"
    2. #include "ui_dialoglist.h"
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. DialogList::DialogList(QWidget *parent) :
    9. QWidget(parent),
    10. ui(new Ui::DialogList)
    11. {
    12. ui->setupUi(this);
    13. setWindowIcon(QPixmap(":/images/校标.png"));
    14. //容器保存10个toolbutton
    15. QVector toolvector;
    16. //记录头像图名加入泛型集合
    17. QList name;
    18. name<<"copy.Image"<<"cut.Image"<<"Hearts"<<"new.Image"<<"open.Image"<<"paste.Image"<<"print.Image"
    19. <<"printPreview.Image"<<"save.Image"<<"校标";
    20. //在toolbox容器中动态生成toolbutton按钮
    21. for (int i=0;isize();i++)
    22. {
    23. QToolButton *btn = new QToolButton;
    24. btn->setText(name[i]);
    25. QString str = QString(":/images/"+name[i]+".png");
    26. btn->setIcon(QPixmap(str));
    27. btn->setIconSize(QSize(72,72));
    28. btn->setAutoRaise(true);
    29. btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    30. ui->vLayout->addWidget(btn);
    31. //将创建的10个toolbutton全部放入容器保存
    32. toolvector.push_back (btn);
    33. isshow.push_back(false);
    34. }
    35. //对10个toolbutton添加信号和槽函数
    36. for (int i=0;isize();i++)
    37. {
    38. connect (toolvector[i],&QToolButton::clicked,[=](){
    39. if(isshow[i]==true)
    40. {
    41. QMessageBox::warning(this,"警告","当前窗口已被打开");
    42. return;
    43. }
    44. else
    45. {
    46. isshow[i]=true;
    47. Widget *widget = new Widget(0,toolvector[i]->text());
    48. widget->setWindowTitle(toolvector[i]->text());
    49. widget->setWindowIcon(toolvector[i]->icon());
    50. widget->show();
    51. connect (widget,&Widget::closeWidget,[=](){
    52. isshow[i]=false;
    53. });
    54. }
    55. });
    56. }
    57. }
    58. DialogList::~DialogList()
    59. {
    60. delete ui;
    61. }

    widget.h

    1. #ifndef WIDGET_H
    2. #define WIDGET_H
    3. #include
    4. #include
    5. #include
    6. QT_BEGIN_NAMESPACE
    7. namespace Ui { class Widget; }
    8. QT_END_NAMESPACE
    9. class Widget : public QWidget
    10. {
    11. Q_OBJECT
    12. public:
    13. Widget(QWidget *parent,QString name);
    14. ~Widget();
    15. private:
    16. Ui::Widget *ui;
    17. signals:
    18. //关闭窗口发送关闭信号
    19. void closeWidget();
    20. public:
    21. //窗口关闭事件
    22. void closeEvent(QCloseEvent *);
    23. public:
    24. enum MsgType{Msg,UsrEnter,UsrLeft};
    25. void sendMsg(MsgType type); //广播UDP消息
    26. void usrEnter(QString usrname); //新用户进入
    27. void usrLeft(QString usrname,QString time); //处理用户离开
    28. QString getUsr(); //获取用户名
    29. QString getMsg(); //获取聊天信息
    30. private:
    31. QUdpSocket *udpSocket; //UDP套接字
    32. qint16 port; //端口
    33. QString Uname; //用户名
    34. void ReceiveMsg(); //接收UDP消息
    35. };
    36. #endif // WIDGET_H

    widget.cpp

    1. #include "widget.h"
    2. #include "ui_widget.h"
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. Widget::Widget(QWidget *parent,QString name)
    12. : QWidget(parent)
    13. , ui(new Ui::Widget)
    14. {
    15. ui->setupUi(this);
    16. udpSocket = new QUdpSocket(this);
    17. //用户名获取
    18. Uname=name;
    19. //端口号获取
    20. this->port=9999;
    21. //绑定端口号 共享地址 和 断线重连
    22. udpSocket->bind(port,QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
    23. //发送新用户进入
    24. sendMsg(UsrEnter);
    25. //点击按钮发送信息
    26. connect (ui->sendbtn,&QPushButton::clicked,[=](){
    27. sendMsg(Msg);
    28. });
    29. //监听别人发送的数据
    30. connect (udpSocket,&QUdpSocket::readyRead,this,&Widget::ReceiveMsg);
    31. //退出程序
    32. connect (ui->quitbtn,&QPushButton::clicked,[=](){
    33. this->close();
    34. });
    35. /辅助功能///
    36. //设置字体
    37. connect(ui->fontComboBox,&QFontComboBox::currentFontChanged,[=](const QFont &font){
    38. ui->msgtextEdit->setFont(font);
    39. });
    40. //设置字号[应用函数指针]
    41. void(QComboBox:: *p)(const QString &text) =&QComboBox::currentIndexChanged;
    42. connect(ui->sizecomboBox, p ,[=](const QString &text){
    43. ui->msgtextEdit->setFontPointSize(text.toDouble());
    44. ui->msgtextEdit->setFocus();
    45. });
    46. //加粗
    47. connect(ui->Boldbtn,&QToolButton::clicked,[=](bool checked){
    48. if (checked)
    49. {
    50. ui->msgtextEdit->setFontWeight(QFont::Bold);
    51. }
    52. else
    53. {
    54. ui->msgtextEdit->setFontWeight(QFont::Normal);
    55. }
    56. });
    57. //倾斜
    58. connect(ui->Itabtn,&QToolButton::clicked,[=](bool checked){
    59. if (checked)
    60. {
    61. ui->msgtextEdit->setFontItalic(true);
    62. }
    63. else
    64. {
    65. ui->msgtextEdit->setFontItalic(false);
    66. }
    67. });
    68. //下划线
    69. connect(ui->underlinebtn,&QToolButton::clicked,[=](bool checked){
    70. if (checked)
    71. {
    72. ui->msgtextEdit->setFontUnderline(true);
    73. }
    74. else
    75. {
    76. ui->msgtextEdit->setFontUnderline(false);
    77. }
    78. });
    79. //字体颜色
    80. connect(ui->colorbtn,&QToolButton::clicked,[=](){
    81. QColor color = QColorDialog::getColor(Qt::red);
    82. ui->msgtextEdit->setTextColor(color);
    83. });
    84. //清空删除聊天记录
    85. connect(ui->deletebtn,&QToolButton::clicked,[=](){
    86. ui->msgBrowser->clear();
    87. });
    88. //保存聊天记录
    89. connect(ui->savebtn,&QToolButton::clicked,[=](){
    90. if (ui->msgBrowser->document ()->isEmpty())
    91. {
    92. QMessageBox::warning(this,"警告","聊天记录不能为空");
    93. return;
    94. }
    95. else
    96. {
    97. QString savepath = QFileDialog::getSaveFileName (this,"save","聊天记录","(*.txt)");
    98. if (savepath.isEmpty())
    99. {
    100. QMessageBox::warning(this,"警告","路径不能为空");
    101. return;
    102. }
    103. else
    104. {
    105. QFile file(savepath);
    106. file.open(QIODevice::WriteOnly | QIODevice::Text);
    107. QTextStream stream(&file);
    108. stream << ui->msgBrowser->toPlainText();
    109. file.close();
    110. QMessageBox::information (this,"Tip","保存成功");
    111. }
    112. }
    113. });
    114. }
    115. Widget::~Widget()
    116. {
    117. delete ui;
    118. }
    119. //窗口关闭事件
    120. void Widget::closeEvent(QCloseEvent *)
    121. {
    122. emit this->closeWidget();
    123. //发送用户离开
    124. sendMsg(UsrLeft);
    125. //关闭套接字
    126. udpSocket->close ();
    127. udpSocket->destroyed ();
    128. }
    129. //广播UDP消息
    130. void Widget::sendMsg(MsgType type)
    131. {
    132. QByteArray array=QByteArray(10000,0);
    133. QDataStream stream(&array,QIODevice::WriteOnly);
    134. //第一段 获取信息类型 第二段 用户名
    135. stream<getUsr();
    136. switch (type)
    137. {
    138. case Msg:
    139. //如果输入框为空,则提示警告
    140. if (ui->msgtextEdit->toPlainText()=="") {
    141. QMessageBox::warning(this,"Tip Message","输入框不允许为空",QMessageBox::Close);
    142. return;
    143. }
    144. //第三段 合并获取到的消息
    145. stream<<getMsg();
    146. break;
    147. case UsrEnter: //用户进入信息
    148. break;
    149. case UsrLeft: //用户离开信息
    150. break;
    151. default:
    152. break;
    153. }
    154. //书写报文 广播发送
    155. udpSocket->writeDatagram(array,QHostAddress::Broadcast,port);
    156. }
    157. //新用户进入
    158. void Widget::usrEnter(QString usrname)
    159. {
    160. bool isempty = ui->usrtableWidget->findItems (usrname,Qt::MatchExactly).isEmpty();
    161. if (isempty)
    162. {
    163. ui->msgBrowser->setTextColor (Qt::gray);
    164. ui->msgBrowser->append (usrname+" 上线了!");
    165. QTableWidgetItem *usr = new QTableWidgetItem(usrname);
    166. ui->usrtableWidget->insertRow(0);
    167. ui->usrtableWidget->setItem(0,0,usr);
    168. QString online = QString("在线人数为:%1").arg(ui->usrtableWidget->rowCount());
    169. ui->label->setText (online);
    170. //把自身信息广播出去
    171. sendMsg (UsrEnter);
    172. }
    173. }
    174. //处理用户离开
    175. void Widget::usrLeft(QString usrname,QString time)
    176. {
    177. bool isempty = ui->usrtableWidget->findItems (usrname,Qt::MatchExactly).isEmpty();
    178. if (!isempty)
    179. {
    180. int r = ui->usrtableWidget->findItems(usrname,Qt::MatchExactly).first()->row();
    181. ui->usrtableWidget->removeRow(r);
    182. //追加聊天记录
    183. ui->msgBrowser->setTextColor(Qt::gray);
    184. ui->msgBrowser->append (QString("%1 at %2 离开").arg (usrname).arg(time));
    185. //在线人数更新
    186. ui->label->setText (QString("在线人数为:%1").arg(ui->usrtableWidget->rowCount()));
    187. }
    188. }
    189. //获取用户名
    190. QString Widget::getUsr()
    191. {
    192. return this->Uname;
    193. }
    194. //获取聊天信息
    195. QString Widget::getMsg()
    196. {
    197. QString str = ui->msgtextEdit->toHtml();
    198. ui->msgtextEdit->clear();
    199. ui->msgtextEdit->setFocus();
    200. return str;
    201. }
    202. //接收UDP消息
    203. void Widget::ReceiveMsg()
    204. {
    205. //拿到数据报文
    206. qint64 size = udpSocket->pendingDatagramSize();
    207. QByteArray array=QByteArray(10000,0);
    208. udpSocket->readDatagram(array.data(),size);
    209. //解析数据
    210. QDataStream stream(&array,QIODevice::ReadOnly);
    211. int msgType;
    212. QString usrName;
    213. QString msg;
    214. QString time = QDateTime::currentDateTime().toString ("yyyy-MM-dd hh:mm:ss");
    215. stream>>msgType;
    216. switch (msgType)
    217. {
    218. case Msg: //普通聊天
    219. {
    220. stream>>usrName>>msg;
    221. //追加聊天记录
    222. ui->msgBrowser->setTextColor(Qt::blue);
    223. ui->msgBrowser->append("["+usrName+"]"+time);
    224. ui->msgBrowser->append(msg);
    225. break;
    226. }
    227. case UsrEnter:
    228. {
    229. stream>>usrName;
    230. usrEnter(usrName);
    231. break;
    232. }
    233. case UsrLeft:
    234. {
    235. stream>>usrName;
    236. QString time = QDateTime::currentDateTime().toString ("yyyy-MM-dd hh:mm:ss");
    237. usrLeft(usrName,time);
    238. break;
    239. }
    240. default:
    241. break;
    242. }
    243. }

     ★UDP通信使用流程如下:

     


    关键核心易出错部分,解析:

    1.针对有重载的信号或槽函数,使用时要注意用到函数指针 void(作用域::*p)(int a) =&...

    1. //设置字号[应用函数指针]
    2. void(QComboBox:: *p)(const QString &text) =&QComboBox::currentIndexChanged;
    3. connect(ui->sizecomboBox, p ,[=](const QString &text){
    4. ui->msgtextEdit->setFontPointSize(text.toDouble());
    5. ui->msgtextEdit->setFocus();
    6. });

     2.写文本文件到本地,将指定文本保存到本地:

    1. QString savepath = QFileDialog::getSaveFileName (this,"save","聊天记录","(*.txt)");
    2. if (savepath.isEmpty())
    3. {
    4. QMessageBox::warning(this,"警告","路径不能为空");
    5. return;
    6. }
    7. else
    8. {
    9. QFile file(savepath);
    10. file.open(QIODevice::WriteOnly | QIODevice::Text);
    11. QTextStream stream(&file);
    12. stream << ui->msgBrowser->toPlainText();
    13. file.close();
    14. QMessageBox::information (this,"Tip","保存成功");
    15. }

    3. 广播UDP信息:

    1. //广播UDP消息
    2. void Widget::sendMsg(MsgType type)
    3. {
    4. QByteArray array=QByteArray(10000,0);
    5. QDataStream stream(&array,QIODevice::WriteOnly);
    6. //第一段 获取信息类型 第二段 用户名
    7. stream<getUsr();
    8. switch (type)
    9. {
    10. case Msg:
    11. //如果输入框为空,则提示警告
    12. if (ui->msgtextEdit->toPlainText()=="") {
    13. QMessageBox::warning(this,"Tip Message","输入框不允许为空",QMessageBox::Close);
    14. return;
    15. }
    16. //第三段 合并获取到的消息
    17. stream<<getMsg();
    18. break;
    19. case UsrEnter: //用户进入信息
    20. break;
    21. case UsrLeft: //用户离开信息
    22. break;
    23. default:
    24. break;
    25. }
    26. //书写报文 广播发送
    27. udpSocket->writeDatagram(array,QHostAddress::Broadcast,port);
    28. }

    4. 接收DUP信息:

    1. //接收UDP消息
    2. void Widget::ReceiveMsg()
    3. {
    4. //拿到数据报文
    5. qint64 size = udpSocket->pendingDatagramSize();
    6. QByteArray array=QByteArray(10000,0);
    7. udpSocket->readDatagram(array.data(),size);
    8. //解析数据
    9. QDataStream stream(&array,QIODevice::ReadOnly);
    10. int msgType;
    11. QString usrName;
    12. QString msg;
    13. QString time = QDateTime::currentDateTime().toString ("yyyy-MM-dd hh:mm:ss");
    14. stream>>msgType;
    15. switch (msgType)
    16. {
    17. case Msg: //普通聊天
    18. {
    19. stream>>usrName>>msg;
    20. //追加聊天记录
    21. ui->msgBrowser->setTextColor(Qt::blue);
    22. ui->msgBrowser->append("["+usrName+"]"+time);
    23. ui->msgBrowser->append(msg);
    24. break;
    25. }
    26. case UsrEnter:
    27. {
    28. stream>>usrName;
    29. usrEnter(usrName);
    30. break;
    31. }
    32. case UsrLeft:
    33. {
    34. stream>>usrName;
    35. QString time = QDateTime::currentDateTime().toString ("yyyy-MM-dd hh:mm:ss");
    36. usrLeft(usrName,time);
    37. break;
    38. }
    39. default:
    40. break;
    41. }
    42. }

    5. 向控件TableWidget中插入内容(以下例子为插入指定的usrname):

    1. bool isempty = ui->usrtableWidget->findItems(usrname,Qt::MatchExactly).isEmpty();
    2. if (isempty)
    3. {
    4. QTableWidgetItem *usr = new QTableWidgetItem(usrname);
    5. ui->usrtableWidget->insertRow(0);
    6. ui->usrtableWidget->setItem(0,0,usr);
    7. }

                                                         后续其他练习实例同步更新,完结!

  • 相关阅读:
    将本地代码托管到GitHub仓库中
    用Servlet 编写hello world
    《机器学习实战》笔记
    RESTful API 设计指南——开篇词
    SpringSecurity授权--前端进行访问控制
    day30 日期转换
    Multigrid reinforcement learning with reward shaping
    脑间同步:道阻且长
    【pen200-lab】10.11.1.231
    Android 上层执行shell指令
  • 原文地址:https://blog.csdn.net/weixin_50016546/article/details/127689663