聊天项目(19) 实现搜索框和聊天列表

搜索框

我们需要实现如下效果

https://cdn.llfc.club/1717211817129.jpg

输入框默认不显示关闭按钮,当输入文字后显示关闭按钮,点击关闭按钮清空文字

添加CustomizeEdit类,头文件

  1. #ifndef CUSTOMIZEEDIT_H
  2. #define CUSTOMIZEEDIT_H
  3. #include <QLineEdit>
  4. #include <QDebug>
  5. class CustomizeEdit: public QLineEdit
  6. {
  7. Q_OBJECT
  8. public:
  9. CustomizeEdit(QWidget *parent = nullptr);
  10. void SetMaxLength(int maxLen);
  11. protected:
  12. void focusOutEvent(QFocusEvent *event) override
  13. {
  14. // 执行失去焦点时的处理逻辑
  15. //qDebug() << "CustomizeEdit focusout";
  16. // 调用基类的focusOutEvent()方法,保证基类的行为得到执行
  17. QLineEdit::focusOutEvent(event);
  18. //发送失去焦点得信号
  19. emit sig_foucus_out();
  20. }
  21. private:
  22. void limitTextLength(QString text) {
  23. if(_max_len <= 0){
  24. return;
  25. }
  26. QByteArray byteArray = text.toUtf8();
  27. if (byteArray.size() > _max_len) {
  28. byteArray = byteArray.left(_max_len);
  29. this->setText(QString::fromUtf8(byteArray));
  30. }
  31. }
  32. int _max_len;
  33. signals:
  34. void sig_foucus_out();
  35. };
  36. #endif // CUSTOMIZEEDIT_H

源文件

  1. #include "customizeedit.h"
  2. CustomizeEdit::CustomizeEdit(QWidget *parent):QLineEdit (parent),_max_len(0)
  3. {
  4. connect(this, &QLineEdit::textChanged, this, &CustomizeEdit::limitTextLength);
  5. }
  6. void CustomizeEdit::SetMaxLength(int maxLen)
  7. {
  8. _max_len = maxLen;
  9. }

设计师界面类里将ui->search_edit提升为CustomizeEdit

在ChatDialog的构造函数中设置输入的长度限制以及关闭等图标的配置

  1. QAction *searchAction = new QAction(ui->search_edit);
  2. searchAction->setIcon(QIcon(":/res/search.png"));
  3. ui->search_edit->addAction(searchAction,QLineEdit::LeadingPosition);
  4. ui->search_edit->setPlaceholderText(QStringLiteral("搜索"));
  5. // 创建一个清除动作并设置图标
  6. QAction *clearAction = new QAction(ui->search_edit);
  7. clearAction->setIcon(QIcon(":/res/close_transparent.png"));
  8. // 初始时不显示清除图标
  9. // 将清除动作添加到LineEdit的末尾位置
  10. ui->search_edit->addAction(clearAction, QLineEdit::TrailingPosition);
  11. // 当需要显示清除图标时,更改为实际的清除图标
  12. connect(ui->search_edit, &QLineEdit::textChanged, [clearAction](const QString &text) {
  13. if (!text.isEmpty()) {
  14. clearAction->setIcon(QIcon(":/res/close_search.png"));
  15. } else {
  16. clearAction->setIcon(QIcon(":/res/close_transparent.png")); // 文本为空时,切换回透明图标
  17. }
  18. });
  19. // 连接清除动作的触发信号到槽函数,用于清除文本
  20. connect(clearAction, &QAction::triggered, [this, clearAction]() {
  21. ui->search_edit->clear();
  22. clearAction->setIcon(QIcon(":/res/close_transparent.png")); // 清除文本后,切换回透明图标
  23. ui->search_edit->clearFocus();
  24. //清除按钮被按下则不显示搜索框
  25. //ShowSearch(false);
  26. });
  27. ui->search_edit->SetMaxLength(15);

stylesheet.qss 中修改样式

  1. #search_wid{
  2. background-color:rgb(247,247,247);
  3. }
  4. #search_edit {
  5. border: 2px solid #f1f1f1;
  6. }

聊天记录列表

创建C++ 类ChatUserList

  1. #ifndef CHATUSERLIST_H
  2. #define CHATUSERLIST_H
  3. #include <QListWidget>
  4. #include <QWheelEvent>
  5. #include <QEvent>
  6. #include <QScrollBar>
  7. #include <QDebug>
  8. class ChatUserList: public QListWidget
  9. {
  10. Q_OBJECT
  11. public:
  12. ChatUserList(QWidget *parent = nullptr);
  13. protected:
  14. bool eventFilter(QObject *watched, QEvent *event) override;
  15. signals:
  16. void sig_loading_chat_user();
  17. };
  18. #endif // CHATUSERLIST_H

实现

  1. #include "chatuserlist.h"
  2. ChatUserList::ChatUserList(QWidget *parent):QListWidget(parent)
  3. {
  4. Q_UNUSED(parent);
  5. this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  6. this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  7. // 安装事件过滤器
  8. this->viewport()->installEventFilter(this);
  9. }
  10. bool ChatUserList::eventFilter(QObject *watched, QEvent *event)
  11. {
  12. // 检查事件是否是鼠标悬浮进入或离开
  13. if (watched == this->viewport()) {
  14. if (event->type() == QEvent::Enter) {
  15. // 鼠标悬浮,显示滚动条
  16. this->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  17. } else if (event->type() == QEvent::Leave) {
  18. // 鼠标离开,隐藏滚动条
  19. this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  20. }
  21. }
  22. // 检查事件是否是鼠标滚轮事件
  23. if (watched == this->viewport() && event->type() == QEvent::Wheel) {
  24. QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
  25. int numDegrees = wheelEvent->angleDelta().y() / 8;
  26. int numSteps = numDegrees / 15; // 计算滚动步数
  27. // 设置滚动幅度
  28. this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - numSteps);
  29. // 检查是否滚动到底部
  30. QScrollBar *scrollBar = this->verticalScrollBar();
  31. int maxScrollValue = scrollBar->maximum();
  32. int currentValue = scrollBar->value();
  33. //int pageSize = 10; // 每页加载的联系人数量
  34. if (maxScrollValue - currentValue <= 0) {
  35. // 滚动到底部,加载新的联系人
  36. qDebug()<<"load more chat user";
  37. //发送信号通知聊天界面加载更多聊天内容
  38. emit sig_loading_chat_user();
  39. }
  40. return true; // 停止事件传递
  41. }
  42. return QListWidget::eventFilter(watched, event);
  43. }

在设计师界面类里提升ui->chat_user_list为ChatUserList

在ChatDialog构造函数和搜索清除按钮的回调中增加

  1. ShowSearch(false);

该函数的具体实现

  1. void ChatDialog::ShowSearch(bool bsearch)
  2. {
  3. if(bsearch){
  4. ui->chat_user_list->hide();
  5. ui->con_user_list->hide();
  6. ui->search_list->show();
  7. _mode = ChatUIMode::SearchMode;
  8. }else if(_state == ChatUIMode::ChatMode){
  9. ui->chat_user_list->show();
  10. ui->con_user_list->hide();
  11. ui->search_list->hide();
  12. _mode = ChatUIMode::ChatMode;
  13. }else if(_state == ChatUIMode::ContactMode){
  14. ui->chat_user_list->hide();
  15. ui->search_list->hide();
  16. ui->con_user_list->show();
  17. _mode = ChatUIMode::ContactMode;
  18. }
  19. }

ChatDialog类中声明添加

  1. ChatUIMode _mode;
  2. ChatUIMode _state;
  3. bool _b_loading;

构造函数的初始化列表初始化这些模式和状态

  1. ChatDialog::ChatDialog(QWidget *parent) :
  2. QDialog(parent),
  3. ui(new Ui::ChatDialog),_mode(ChatUIMode::ChatMode),
  4. _state(ChatUIMode::ChatMode),_b_loading(false){//...}

为了让用户聊天列表更美观,修改qss文件

  1. #chat_user_wid{
  2. background-color:rgb(0,0,0);
  3. }
  4. #chat_user_list {
  5. background-color: rgb(247,247,248);
  6. border: none;
  7. }
  8. #chat_user_list::item:selected {
  9. background-color: #d3d7d4;
  10. border: none;
  11. outline: none;
  12. }
  13. #chat_user_list::item:hover {
  14. background-color: rgb(206,207,208);
  15. border: none;
  16. outline: none;
  17. }
  18. #chat_user_list::focus {
  19. border: none;
  20. outline: none;
  21. }

添加聊天item

我们要为聊天列表添加item,每个item包含的样式为

https://cdn.llfc.club/1717215988933.jpg

对于这样的列表元素,我们采用设计师界面类设计非常方便, 新建设计师界面类ChatUserWid, 在ChatUserWid.ui中拖动布局如下

https://cdn.llfc.club/1717217007100.jpg

我们定义一个基类ListItemBase

  1. #ifndef LISTITEMBASE_H
  2. #define LISTITEMBASE_H
  3. #include <QWidget>
  4. #include "global.h"
  5. class ListItemBase : public QWidget
  6. {
  7. Q_OBJECT
  8. public:
  9. explicit ListItemBase(QWidget *parent = nullptr);
  10. void SetItemType(ListItemType itemType);
  11. ListItemType GetItemType();
  12. private:
  13. ListItemType _itemType;
  14. public slots:
  15. signals:
  16. };
  17. #endif // LISTITEMBASE_H

我们实现这个基类

  1. #include "listitembase.h"
  2. ListItemBase::ListItemBase(QWidget *parent) : QWidget(parent)
  3. {
  4. }
  5. void ListItemBase::SetItemType(ListItemType itemType)
  6. {
  7. _itemType = itemType;
  8. }
  9. ListItemType ListItemBase::GetItemType()
  10. {
  11. return _itemType;
  12. }

我们实现ChatUserWid

  1. #ifndef CHATUSERWID_H
  2. #define CHATUSERWID_H
  3. #include <QWidget>
  4. #include "listitembase.h"
  5. namespace Ui {
  6. class ChatUserWid;
  7. }
  8. class ChatUserWid : public ListItemBase
  9. {
  10. Q_OBJECT
  11. public:
  12. explicit ChatUserWid(QWidget *parent = nullptr);
  13. ~ChatUserWid();
  14. QSize sizeHint() const override {
  15. return QSize(250, 70); // 返回自定义的尺寸
  16. }
  17. void SetInfo(QString name, QString head, QString msg);
  18. private:
  19. Ui::ChatUserWid *ui;
  20. QString _name;
  21. QString _head;
  22. QString _msg;
  23. };
  24. #endif // CHATUSERWID_H

具体实现

  1. #include "chatuserwid.h"
  2. #include "ui_chatuserwid.h"
  3. ChatUserWid::ChatUserWid(QWidget *parent) :
  4. ListItemBase(parent),
  5. ui(new Ui::ChatUserWid)
  6. {
  7. ui->setupUi(this);
  8. SetItemType(ListItemType::CHAT_USER_ITEM);
  9. }
  10. ChatUserWid::~ChatUserWid()
  11. {
  12. delete ui;
  13. }
  14. void ChatUserWid::SetInfo(QString name, QString head, QString msg)
  15. {
  16. _name = name;
  17. _head = head;
  18. _msg = msg;
  19. // 加载图片
  20. QPixmap pixmap(_head);
  21. // 设置图片自动缩放
  22. ui->icon_lb->setPixmap(pixmap.scaled(ui->icon_lb->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
  23. ui->icon_lb->setScaledContents(true);
  24. ui->user_name_lb->setText(_name);
  25. ui->user_chat_lb->setText(_msg);
  26. }

在ChatDialog里定义一些全局的变量用来做测试

  1. std::vector<QString> strs ={"hello world !",
  2. "nice to meet u",
  3. "New year,new life",
  4. "You have to love yourself",
  5. "My love is written in the wind ever since the whole world is you"};
  6. std::vector<QString> heads = {
  7. ":/res/head_1.jpg",
  8. ":/res/head_2.jpg",
  9. ":/res/head_3.jpg",
  10. ":/res/head_4.jpg",
  11. ":/res/head_5.jpg"
  12. };
  13. std::vector<QString> names = {
  14. "llfc",
  15. "zack",
  16. "golang",
  17. "cpp",
  18. "java",
  19. "nodejs",
  20. "python",
  21. "rust"
  22. };

这些数据只是测试数据,实际数据是后端传输过来的,我们目前只测试界面功能,用测试数据即可,写一个函数根据上面的数据添加13条item记录

  1. void ChatDialog::addChatUserList()
  2. {
  3. // 创建QListWidgetItem,并设置自定义的widget
  4. for(int i = 0; i < 13; i++){
  5. int randomValue = QRandomGenerator::global()->bounded(100); // 生成0到99之间的随机整数
  6. int str_i = randomValue%strs.size();
  7. int head_i = randomValue%heads.size();
  8. int name_i = randomValue%names.size();
  9. auto *chat_user_wid = new ChatUserWid();
  10. chat_user_wid->SetInfo(names[name_i], heads[head_i], strs[str_i]);
  11. QListWidgetItem *item = new QListWidgetItem;
  12. //qDebug()<<"chat_user_wid sizeHint is " << chat_user_wid->sizeHint();
  13. item->setSizeHint(chat_user_wid->sizeHint());
  14. ui->chat_user_list->addItem(item);
  15. ui->chat_user_list->setItemWidget(item, chat_user_wid);
  16. }
  17. }

在ChatDialog构造函数中添加

  1. addChatUserList();

完善界面效果,新增qss

  1. ChatUserWid {
  2. background-color:rgb(247,247,247);
  3. border: none;
  4. }
  5. #user_chat_lb{
  6. color:rgb(153,153,153);
  7. font-size: 12px;
  8. font-family: "Microsoft YaHei";
  9. }
  10. #user_name_lb{
  11. color:rgb(0,0,0);
  12. font-size: 14px;
  13. font-weight: normal;
  14. font-family: "Microsoft YaHei";
  15. }
  16. #time_wid #time_lb{
  17. color:rgb(140,140,140);
  18. font-size: 12px;
  19. font-family: "Microsoft YaHei";
  20. }
  21. QScrollBar:vertical {
  22. background: transparent; /* 将轨道背景设置为透明 */
  23. width: 8px; /* 滚动条宽度,根据需要调整 */
  24. margin: 0px; /* 移除滚动条与滑块之间的间距 */
  25. }
  26. QScrollBar::handle:vertical {
  27. background: rgb(173,170,169); /* 滑块颜色 */
  28. min-height: 10px; /* 滑块最小高度,根据需要调整 */
  29. border-radius: 4px; /* 滑块边缘圆角,调整以形成椭圆形状 */
  30. }
  31. QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
  32. height: 0px; /* 移除上下按钮 */
  33. border: none; /* 移除边框 */
  34. background: transparent; /* 背景透明 */
  35. }
  36. QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
  37. background: none; /* 页面滚动部分背景透明 */
  38. }

测试效果

https://cdn.llfc.club/1717218961063.jpg

源码链接

https://gitee.com/secondtonone1/llfcchat

视频链接

https://www.bilibili.com/video/BV13Z421W7WA/?spm_id_from=333.788&vd_source=8be9e83424c2ed2c9b2a3ed1d01385e9

热门评论

热门文章

  1. vscode搭建windows C++开发环境

    喜欢(596) 浏览(83635)
  2. 聊天项目(28) 分布式服务通知好友申请

    喜欢(507) 浏览(6104)
  3. 使用hexo搭建个人博客

    喜欢(533) 浏览(11922)
  4. Linux环境搭建和编码

    喜欢(594) 浏览(13569)
  5. Qt环境搭建

    喜欢(517) 浏览(24775)

最新评论

  1. 无锁并发队列 TenThousandOne:_head  和 _tail  替换为原子变量。那里pop的逻辑,val = _data[h] 可以移到循环外面吗
  2. 堆排序 secondtonone1:堆排序非常实用,定时器就是这个原理制作的。
  3. 答疑汇总(thread,async源码分析) Yagus:如果引用计数为0,则会执行 future 的析构进而等待任务执行完成,那么看到的输出将是 这边应该不对吧,std::future析构只在这三种情况都满足的时候才回block: 1.共享状态是std::async 创造的(类型是_Task_async_state) 2.共享状态没有ready 3.这个future是共享状态的最后一个引用 这边共享状态类型是“_Package_state”,引用计数即使为0也不应该block啊
  4. 再谈单例模式 secondtonone1:是的,C++11以后返回局部static变量对象能保证线程安全了。
  5. C++ 并发三剑客future, promise和async Yunfei:大佬您好,如果这个线程池中加入的异步任务的形参如果有右值引用,这个commit中的返回类型推导和bind绑定就会出现问题,请问实际工程中,是不是不会用到这种任务,如果用到了,应该怎么解决?
  6. interface应用 secondtonone1:interface是万能类型,但是使用时要转换为实际类型来使用。interface丰富了go的多态特性,也降低了传统面向对象语言的耦合性。
  7. 类和对象 陈宇航:支持!!!!
  8. 聊天项目(7) visualstudio配置grpc diablorrr:cmake文件得改一下 find_package(Boost REQUIRED COMPONENTS system filesystem),要加上filesystem。在target_link_libraries中也同样加上
  9. 解决博客回复区被脚本注入的问题 secondtonone1:走到现在我忽然明白一个道理,无论工作也好生活也罢,最重要的是开心,即使一份安稳的工作不能给我带来事业上的积累也要合理的舍弃,所以我还是想去做喜欢的方向。
  10. boost::asio之socket的创建和连接 项空月:发现一些错别字 :每隔vector存储  是不是是每个. asio::mutable_buffers_1 o或者    是不是多打了个o
  11. string类 WangQi888888:确实错了,应该是!isspace(sind[index]). 否则不进入循环,还是原来的字符串“some string”
  12. 网络编程学习方法和图书推荐 Corleone:啥程度可以找工作
  13. 创建项目和编译 secondtonone1:谢谢支持
  14. visual studio配置boost库 一giao里我离giaogiao:请问是修改成这样吗:.\b2.exe toolset=MinGW
  15. 面试题汇总(一) secondtonone1:看到网络上经常提问的go的问题,做了一下汇总,结合自己的经验给出的答案,如有纰漏,望指正批评。
  16. protobuf配置和使用 熊二:你可以把dll放到系统目录,也可以配置环境变量,还能把dll丢到lib里
  17. 聊天项目(15) 客户端实现TCP管理者 lkx:已经在&QTcpSocket::readyRead 回调函数中做了处理了的。
  18. Qt 对话框 Spade2077:QDialog w(); //这里是不是不需要带括号
  19. 处理网络粘包问题 zyouth: //消息的长度小于头部规定的长度,说明数据未收全,则先将部分消息放到接收节点里 if (bytes_transferred < data_len) { memcpy(_recv_msg_node->_data + _recv_msg_node->_cur_len, _data + copy_len, bytes_transferred); _recv_msg_node->_cur_len += bytes_transferred; ::memset(_data, 0, MAX_LENGTH); _socket.async_read_some(boost::asio::buffer(_data, MAX_LENGTH), std::bind(&CSession::HandleRead, this, std::placeholders::_1, std::placeholders::_2, shared_self)); //头部处理完成 _b_head_parse = true; return; } 把_b_head_parse = true;放在_socket.async_read_some前面是不是更好
  20. 构造函数 secondtonone1:构造函数是类的基础知识,要着重掌握
  21. 聊天项目(9) redis服务搭建 pro_lin:redis线程池的析构函数,除了pop出队列,还要free掉redis连接把
  22. Qt MVC结构之QItemDelegate介绍 胡歌-此生不换:gpt, google
  23. 利用栅栏实现同步 Dzher:作者你好!我觉得 std::thread a(write_x); std::thread b(write_y); std::thread c(read_x_then_y); std::thread d(read_y_then_x); 这个例子中的assert fail并不会发生,原子变量设定了非relaxed内存序后一个线程的原子变量被写入,那么之后的读取一定会被同步的,c和d线程中只可能同时发生一个z++未执行的情况,最终z不是1就是2了,我测试了很多次都没有assert,请问我这个观点有什么错误,谢谢!

个人公众号

个人微信