聊天项目(24) EventFilter实现搜索隐藏

事件过滤器

我们为了实现点击界面某个位置判断是否隐藏搜索框的功能。我们期待当鼠标点击搜索列表之外的区域时显示隐藏搜索框恢复聊天界面。
点击搜索列表则不隐藏搜索框。可以通过重载ChatDialog的EventFilter函数实现点击功能

  1. bool ChatDialog::eventFilter(QObject *watched, QEvent *event)
  2. {
  3. if (event->type() == QEvent::MouseButtonPress) {
  4. QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
  5. handleGlobalMousePress(mouseEvent);
  6. }
  7. return QDialog::eventFilter(watched, event);
  8. }

具体判断全局鼠标按下位置和功能

  1. void ChatDialog::handleGlobalMousePress(QMouseEvent *event)
  2. {
  3. // 实现点击位置的判断和处理逻辑
  4. // 先判断是否处于搜索模式,如果不处于搜索模式则直接返回
  5. if( _mode != ChatUIMode::SearchMode){
  6. return;
  7. }
  8. // 将鼠标点击位置转换为搜索列表坐标系中的位置
  9. QPoint posInSearchList = ui->search_list->mapFromGlobal(event->globalPos());
  10. // 判断点击位置是否在聊天列表的范围内
  11. if (!ui->search_list->rect().contains(posInSearchList)) {
  12. // 如果不在聊天列表内,清空输入框
  13. ui->search_edit->clear();
  14. ShowSearch(false);
  15. }
  16. }

在ChatDialog构造函数中添加事件过滤器

  1. //检测鼠标点击位置判断是否要清空搜索框
  2. this->installEventFilter(this); // 安装事件过滤器
  3. //设置聊天label选中状态
  4. ui->side_chat_lb->SetSelected(true);

这样就可以实现在ChatDialog中点击其他位置隐藏SearchList列表了。

查找结果

在项目中添加FindSuccessDlg设计师界面类,其布局如下

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

属性管理界面如下

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

FindSuccessDlg声明如下

  1. class FindSuccessDlg : public QDialog
  2. {
  3. Q_OBJECT
  4. public:
  5. explicit FindSuccessDlg(QWidget *parent = nullptr);
  6. ~FindSuccessDlg();
  7. void SetSearchInfo(std::shared_ptr<SearchInfo> si);
  8. private slots:
  9. void on_add_friend_btn_clicked();
  10. private:
  11. Ui::FindSuccessDlg *ui;
  12. QWidget * _parent;
  13. std::shared_ptr<SearchInfo> _si;
  14. };

FindSuccessDlg实现如下

  1. FindSuccessDlg::FindSuccessDlg(QWidget *parent) :
  2. QDialog(parent),
  3. ui(new Ui::FindSuccessDlg)
  4. {
  5. ui->setupUi(this);
  6. // 设置对话框标题
  7. setWindowTitle("添加");
  8. // 隐藏对话框标题栏
  9. setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
  10. // 获取当前应用程序的路径
  11. QString app_path = QCoreApplication::applicationDirPath();
  12. QString pix_path = QDir::toNativeSeparators(app_path +
  13. QDir::separator() + "static"+QDir::separator()+"head_1.jpg");
  14. QPixmap head_pix(pix_path);
  15. head_pix = head_pix.scaled(ui->head_lb->size(),
  16. Qt::KeepAspectRatio, Qt::SmoothTransformation);
  17. ui->head_lb->setPixmap(head_pix);
  18. ui->add_friend_btn->SetState("normal","hover","press");
  19. this->setModal(true);
  20. }
  21. FindSuccessDlg::~FindSuccessDlg()
  22. {
  23. qDebug()<<"FindSuccessDlg destruct";
  24. delete ui;
  25. }
  26. void FindSuccessDlg::SetSearchInfo(std::shared_ptr<SearchInfo> si)
  27. {
  28. ui->name_lb->setText(si->_name);
  29. _si = si;
  30. }
  31. void FindSuccessDlg::on_add_friend_btn_clicked()
  32. {
  33. //todo... 添加好友界面弹出
  34. }

在SearchList 的slot_item_clicked函数中添加点击条目处理逻辑

  1. void SearchList::slot_item_clicked(QListWidgetItem *item)
  2. {
  3. QWidget *widget = this->itemWidget(item); //获取自定义widget对象
  4. if(!widget){
  5. qDebug()<< "slot item clicked widget is nullptr";
  6. return;
  7. }
  8. // 对自定义widget进行操作, 将item 转化为基类ListItemBase
  9. ListItemBase *customItem = qobject_cast<ListItemBase*>(widget);
  10. if(!customItem){
  11. qDebug()<< "slot item clicked widget is nullptr";
  12. return;
  13. }
  14. auto itemType = customItem->GetItemType();
  15. if(itemType == ListItemType::INVALID_ITEM){
  16. qDebug()<< "slot invalid item clicked ";
  17. return;
  18. }
  19. if(itemType == ListItemType::ADD_USER_TIP_ITEM){
  20. //todo ...
  21. _find_dlg = std::make_shared<FindSuccessDlg>(this);
  22. auto si = std::make_shared<SearchInfo>(0,"llfc","llfc","hello , my friend!",0);
  23. (std::dynamic_pointer_cast<FindSuccessDlg>(_find_dlg))->SetSearchInfo(si);
  24. _find_dlg->show();
  25. return;
  26. }
  27. //清楚弹出框
  28. CloseFindDlg();
  29. }

这样我们在输入框输入文字,点击搜索列表中搜索添加好友的item,就能弹出搜索结果对话框了。这里只做界面演示,之后会改为像服务器发送请求获取搜索结果。

pro的改写

我们对项目的pro做了调整,更新了static文件夹的拷贝以及编码utf-8的设定

  1. QT += core gui network
  2. greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
  3. TARGET = llfcchat
  4. TEMPLATE = app
  5. RC_ICONS = icon.ico
  6. DESTDIR = ./bin
  7. DEFINES += QT_DEPRECATED_WARNINGS
  8. CONFIG += c++11
  9. SOURCES += \
  10. adduseritem.cpp \
  11. bubbleframe.cpp \
  12. chatdialog.cpp \
  13. chatitembase.cpp \
  14. chatpage.cpp \
  15. chatuserlist.cpp \
  16. chatuserwid.cpp \
  17. chatview.cpp \
  18. clickedbtn.cpp \
  19. clickedlabel.cpp \
  20. customizeedit.cpp \
  21. findsuccessdlg.cpp \
  22. global.cpp \
  23. httpmgr.cpp \
  24. listitembase.cpp \
  25. loadingdlg.cpp \
  26. logindialog.cpp \
  27. main.cpp \
  28. mainwindow.cpp \
  29. messagetextedit.cpp \
  30. picturebubble.cpp \
  31. registerdialog.cpp \
  32. resetdialog.cpp \
  33. searchlist.cpp \
  34. statewidget.cpp \
  35. tcpmgr.cpp \
  36. textbubble.cpp \
  37. timerbtn.cpp \
  38. userdata.cpp \
  39. usermgr.cpp
  40. HEADERS += \
  41. adduseritem.h \
  42. bubbleframe.h \
  43. chatdialog.h \
  44. chatitembase.h \
  45. chatpage.h \
  46. chatuserlist.h \
  47. chatuserwid.h \
  48. chatview.h \
  49. clickedbtn.h \
  50. clickedlabel.h \
  51. customizeedit.h \
  52. findsuccessdlg.h \
  53. global.h \
  54. httpmgr.h \
  55. listitembase.h \
  56. loadingdlg.h \
  57. logindialog.h \
  58. mainwindow.h \
  59. messagetextedit.h \
  60. picturebubble.h \
  61. registerdialog.h \
  62. resetdialog.h \
  63. searchlist.h \
  64. singleton.h \
  65. statewidget.h \
  66. tcpmgr.h \
  67. textbubble.h \
  68. timerbtn.h \
  69. userdata.h \
  70. usermgr.h
  71. FORMS += \
  72. adduseritem.ui \
  73. chatdialog.ui \
  74. chatpage.ui \
  75. chatuserwid.ui \
  76. findsuccessdlg.ui \
  77. loadingdlg.ui \
  78. logindialog.ui \
  79. mainwindow.ui \
  80. registerdialog.ui \
  81. resetdialog.ui
  82. # Default rules for deployment.
  83. qnx: target.path = /tmp/$${TARGET}/bin
  84. else: unix:!android: target.path = /opt/$${TARGET}/bin
  85. !isEmpty(target.path): INSTALLS += target
  86. RESOURCES += \
  87. rc.qrc
  88. DISTFILES += \
  89. config.ini
  90. CONFIG(debug, debug | release) {
  91. #指定要拷贝的文件目录为工程目录下release目录下的所有dll、lib文件,例如工程目录在D:\QT\Test
  92. #PWD就为D:/QT/Test,DllFile = D:/QT/Test/release/*.dll
  93. TargetConfig = $${PWD}/config.ini
  94. #将输入目录中的"/"替换为"\"
  95. TargetConfig = $$replace(TargetConfig, /, \\)
  96. #将输出目录中的"/"替换为"\"
  97. OutputDir = $${OUT_PWD}/$${DESTDIR}
  98. OutputDir = $$replace(OutputDir, /, \\)
  99. //执行copy命令
  100. QMAKE_POST_LINK += copy /Y \"$$TargetConfig\" \"$$OutputDir\" &
  101. # 首先,定义static文件夹的路径
  102. StaticDir = $${PWD}/static
  103. # 将路径中的"/"替换为"\"
  104. StaticDir = $$replace(StaticDir, /, \\)
  105. #message($${StaticDir})
  106. # 使用xcopy命令拷贝文件夹,/E表示拷贝子目录及其内容,包括空目录。/I表示如果目标不存在则创建目录。/Y表示覆盖现有文件而不提示。
  107. QMAKE_POST_LINK += xcopy /Y /E /I \"$$StaticDir\" \"$$OutputDir\\static\\\"
  108. }else{
  109. #release
  110. message("release mode")
  111. #指定要拷贝的文件目录为工程目录下release目录下的所有dll、lib文件,例如工程目录在D:\QT\Test
  112. #PWD就为D:/QT/Test,DllFile = D:/QT/Test/release/*.dll
  113. TargetConfig = $${PWD}/config.ini
  114. #将输入目录中的"/"替换为"\"
  115. TargetConfig = $$replace(TargetConfig, /, \\)
  116. #将输出目录中的"/"替换为"\"
  117. OutputDir = $${OUT_PWD}/$${DESTDIR}
  118. OutputDir = $$replace(OutputDir, /, \\)
  119. //执行copy命令
  120. QMAKE_POST_LINK += copy /Y \"$$TargetConfig\" \"$$OutputDir\"
  121. # 首先,定义static文件夹的路径
  122. StaticDir = $${PWD}/static
  123. # 将路径中的"/"替换为"\"
  124. StaticDir = $$replace(StaticDir, /, \\)
  125. #message($${StaticDir})
  126. # 使用xcopy命令拷贝文件夹,/E表示拷贝子目录及其内容,包括空目录。/I表示如果目标不存在则创建目录。/Y表示覆盖现有文件而不提示。
  127. QMAKE_POST_LINK += xcopy /Y /E /I \"$$StaticDir\" \"$$OutputDir\\static\\\"
  128. }
  129. win32-msvc*:QMAKE_CXXFLAGS += /wd"4819" /utf-8

视频

https://www.bilibili.com/video/BV1ww4m1e72G/

源码链接

https://gitee.com/secondtonone1/llfcchat

热门评论

热门文章

  1. Linux环境搭建和编码

    喜欢(594) 浏览(6835)
  2. windows环境搭建和vscode配置

    喜欢(587) 浏览(1850)
  3. slice介绍和使用

    喜欢(521) 浏览(1927)
  4. 解密定时器的实现细节

    喜欢(566) 浏览(2262)
  5. C++ 类的继承封装和多态

    喜欢(588) 浏览(3175)

最新评论

  1. 线程基础 mzx2023:新手好奇问一下,这是什么原因呢?
  2. interface应用 secondtonone1:interface是万能类型,但是使用时要转换为实际类型来使用。interface丰富了go的多态特性,也降低了传统面向对象语言的耦合性。
  3. 堆排序 secondtonone1:堆排序非常实用,定时器就是这个原理制作的。
  4. asio多线程模式IOThreadPool secondtonone1:这么优秀吗
  5. 互斥与死锁 Vstronzw://仅提供一份参考代码给同样初学者的我们[脱单doge] /* 定义了如下栈, 对于多线程访问时判断栈是否为空, 此后两个线程同时出栈,可能会造成崩溃 */ #include <iostream> #include <string> #include <thread> #include <vector> #include <mutex> #include <stack> #include <exception> using namespace std; struct empty_stack : public std::exception { public: const char* what()const throw() //函数后面必须跟throw(),括号里面不能有任务参数,表示不抛出任务异常 //因为这个已经是一个异常处理信息了,不能再抛异常。 { return "empty_stack"; } }; //struct empty_stack : std::exception //{ // const char* what() const throw(); //}; template<typename T> class threadsafe_stack { private: std::stack<T> data; mutable std::mutex m; public: threadsafe_stack() {} threadsafe_stack(const threadsafe_stack& other) { std::lock_guard<std::mutex> lock(other.m); //①在构造函数的函数体(constructor body)内进行复制操作 data = other.data; } threadsafe_stack& operator=(const threadsafe_stack&) = delete; void push(T new_value) { std::lock_guard<std::mutex> lock(m); data.push(std::move(new_value)); } T pop() { std::lock_guard<std::mutex> lock(m); if (data.empty()) throw empty_stack(); auto element = data.top(); data.pop(); return element; } bool empty() const { std::lock_guard<std::mutex> lock(m); return data.empty(); } }; void test_threadsafe_stack() { threadsafe_stack<int> safe_stack; safe_stack.push(1); std::thread t1([&safe_stack]() { if (!safe_stack.empty()) { std::this_thread::sleep_for(std::chrono::seconds(1)); try { safe_stack.pop(); } catch (empty_stack &e) { cout << e.what() << endl; } } }); std::thread t2([&safe_stack]() { if (!safe_stack.empty()) { std::this_thread::sleep_for(std::chrono::seconds(1)); try { safe_stack.pop(); } catch (empty_stack &e) { cout << e.what() << endl; } } }); t1.join(); t2.join(); } int main() { test_threadsafe_stack(); return 0; } /* empty_stack */

个人公众号

个人微信