怎么使用QGraphicsView实现气泡聊天窗口+排雷功能
发表于:2025-02-01 作者:千家信息网编辑
千家信息网最后更新 2025年02月01日,这篇"怎么使用QGraphicsView实现气泡聊天窗口+排雷功能"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收
千家信息网最后更新 2025年02月01日怎么使用QGraphicsView实现气泡聊天窗口+排雷功能
这篇"怎么使用QGraphicsView实现气泡聊天窗口+排雷功能"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇"怎么使用QGraphicsView实现气泡聊天窗口+排雷功能"文章吧。
最终效果:
存在问题:无法选择文字及跨选(但理论上可以通过重写鼠标相关事件,达到模拟选择的效果)
左侧和右侧的消息分别是封装的两个Item,而这两个Item又从同一个基类继承而来。
气泡通过重写void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
函数,在里面根据文字的宽高计算气泡的位置并画上去,然后再把字写上去。
并且当窗口大小发生变化时,需要重新计算文字尺寸,进行绘制。
#pragma once#include//聊天元素所有item的基类class ChatBaseItem : public QGraphicsRectItem{public: ChatBaseItem(); virtual ~ChatBaseItem(); virtual int Resize(int width); //传入值为viewport宽,返回值为item高};
#include "chatbaseitem.h"ChatBaseItem::ChatBaseItem() : QGraphicsRectItem(){}ChatBaseItem::~ChatBaseItem()int ChatBaseItem::Resize(int width) return 0;
左侧聊天气泡Item
#pragma once#include "chatbaseitem.h"#includeclass OtherMsgItem : public ChatBaseItem{public: OtherMsgItem(QPixmap icon, QString name, QString msg, QDateTime datetime = QDateTime()); virtual ~OtherMsgItem(); virtual int Resize(int width); //返回整个item的高度protected: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); virtual QRectF boundingRect() const;private: QGraphicsPixmapItem icon_item_; QGraphicsSimpleTextItem name_item_; QString text_; QSize text_size_; //文字尺寸 QDateTime datetime_;};
#include#include #include #include "othermsgitem.h"const int kMsgFontSize = 14;const int kNameFontSize = 13;const QPoint kNamePos = QPoint(64, 0);const QPoint kIconPos = QPoint(20, 8);const QPoint kBorderPos = QPoint(kNamePos.x(), kNamePos.y()+18);const QMargins kMargins = QMargins(12,11,12,11); //文字距边框的距离const QPoint kTextPos = QPoint(kBorderPos.x()+ kMargins.left(), kBorderPos.y() + kMargins.top());const int kMarginRight = 40; //边框距窗口右侧的距离OtherMsgItem::OtherMsgItem(QPixmap icon, QString name, QString msg, QDateTime datetime /*= QDateTime()*/) : ChatBaseItem() , datetime_(datetime){ icon_item_.setPixmap(icon); icon_item_.setPos(kIconPos); text_ = msg; QFont font("Microsoft YaHei"); font.setPixelSize(kNameFontSize); name_item_.setText(name); name_item_.setPos(kNamePos); name_item_.setFont(font); name_item_.setBrush(QColor(153, 153, 153)); icon_item_.setParentItem(this); name_item_.setParentItem(this);}OtherMsgItem::~OtherMsgItem()int OtherMsgItem::Resize(int width) //每行最大可容纳文字的宽度 int row_width = width - kTextPos.x() - kMarginRight-kMargins.right(); //计算文字总共需要多宽 font.setPixelSize(kMsgFontSize); QFontMetrics font_matrics(font); int text_total_width = font_matrics.width(text_); int text_row_height = font_matrics.lineSpacing(); if(row_width setPen(QPen(QColor(229, 229, 229), 1, Qt::SolidLine)); painter->drawRoundedRect(border.x(), border.y(), border.width(), border.height(), rnd.width(), rnd.height()); //气泡 painter->setBrush(QBrush(Qt::white)); painter->setPen(Qt::NoPen); painter->drawRoundedRect(border.x()+1, border.y()+1, border.width()-2, border.height()-2, rnd.width(), rnd.height()); //三角,用矩形实现 QRect rect1(border.x()+1, border.y()+1, 20, 20); painter->drawRect(rect1); //三角加边 QPen pen; pen.setColor(QColor(229, 229, 229)); painter->setPen(pen); painter->drawLine(border.x() , border.y() , border.x() +20, border.y() ); painter->drawLine(border.x() , border.y() , border.x() , border.y() +20); QPen penText; penText.setColor(QColor(51, 51, 51)); painter->setPen(penText); QTextOption option1(Qt::AlignLeft); option1.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); painter->setFont(font); QRectF text_rect(kTextPos.x(), kTextPos.y(), text_size_.width(), text_size_.height()); painter->drawText(text_rect, text_, option1);QRectF OtherMsgItem::boundingRect() const QRectF border(kBorderPos.x(), kBorderPos.y(), text_size_.width() + kMargins.left() + kMargins.right(), text_size_.height() + kMargins.top() + kMargins.bottom()); return QRectF(0,0,border.width(),border.height());
右侧气泡和左侧气泡不同,计算位置时,左端点需要根据窗口宽度事实计算。
#pragma once#include "chatbaseitem.h"#includeclass SelfMsgItem : public ChatBaseItem{public: SelfMsgItem(QPixmap icon, QString msg, QDateTime datetime = QDateTime()); virtual ~SelfMsgItem(); virtual int Resize(int width); //返回整个item的高度protected: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); virtual QRectF boundingRect() const;private: QGraphicsPixmapItem icon_item_; QString text_; QSize text_size_; //文字尺寸 QDateTime datetime_; int port_width_;};
#include#include #include "selfmsgitem.h"const int kMsgFontSize = 14;const int kNameFontSize = 13;const int kIconY = 0;const int kBorderY = 10;const int kIconWidth = 34;const QMargins kIconMargins = QMargins(10,0,20,0);const QMargins kMargins = QMargins(12, 11, 12, 11); //文字距边框的距离const int kMarginLeft = 40; //边框距窗口左侧的距离SelfMsgItem::SelfMsgItem(QPixmap icon, QString msg, QDateTime datetime /*= QDateTime()*/) : ChatBaseItem() , datetime_(datetime) , text_(msg){ icon_item_.setPixmap(icon); icon_item_.setY(kIconY); icon_item_.setParentItem(this);}SelfMsgItem::~SelfMsgItem(){}int SelfMsgItem::Resize(int width){ port_width_ = width; //每行最大可容纳文字的宽度 int row_width = width - kMarginLeft - kMargins.left() - kMargins.right() - kIconWidth - kIconMargins.left() - kIconMargins.right(); //计算文字总共需要多宽 QFont font("Microsoft YaHei"); font.setPixelSize(kMsgFontSize); QFontMetrics font_matrics(font); int text_total_width = font_matrics.width(text_); int text_row_height = font_matrics.lineSpacing(); if (row_width < text_total_width) { int row = text_total_width / row_width; int text_total_height = (row+1)* text_row_height; //row从零开始,需要补加1 text_size_.setWidth(row_width); text_size_.setHeight(text_total_height); } else { text_size_.setWidth(text_total_width); text_size_.setHeight(text_row_height); } return text_size_.height() + kMargins.top() + kMargins.bottom() + kBorderY;}void SelfMsgItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){ QSize rnd(17, 17); //气泡聊天框左端点需要根据控件宽度计算 QRectF border(port_width_- kIconMargins.left()-kIconMargins.right()-kIconWidth-text_size_.width()-kMargins.left()-kMargins.right() , kBorderY , text_size_.width() + kMargins.left() + kMargins.right() , text_size_.height() + kMargins.top() + kMargins.bottom()); icon_item_.setX(border.x()+ border.width() + 10); //气泡 painter->setBrush(QBrush(QColor(149,182,57))); painter->setPen(Qt::NoPen); painter->drawRoundedRect(border.x(), border.y(), border.width(), border.height(), rnd.width(), rnd.height()); //三角,用矩形实现 QRect rect1(border.x() + border.width() - 20, border.y(), 20, 20); painter->setPen(Qt::NoPen); painter->setBrush(QBrush(QColor(149, 182, 57))); painter->drawRect(rect1); QPen penText; penText.setColor(QColor(255, 255, 255)); painter->setPen(penText); QTextOption option1(Qt::AlignLeft); option1.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); QFont font("Microsoft YaHei"); font.setPixelSize(kMsgFontSize); painter->setFont(font); QRectF text_rect(border.x()+kMargins.left(), border.y() + kMargins.top(), text_size_.width(), text_size_.height()); painter->drawText(text_rect, text_, option1);}QRectF SelfMsgItem::boundingRect() const{ QRectF border(port_width_ - kIconMargins.left() - kIconMargins.right() - kIconWidth - text_size_.width() - kMargins.left() - kMargins.right() , kBorderY , text_size_.width() + kMargins.left() + kMargins.right() , text_size_.height() + kMargins.top() + kMargins.bottom()); return QRectF(0, 0, border.width(), border.height());}
接下是view调用
#pragma once#include#include #include class ChatBaseItem;class ChatView : public QGraphicsView{ Q_OBJECTpublic: ChatView(QWidget *parent); ~ChatView(); void Resize(int width); void ClearAll(); void AppendSelfMessage(QPixmap icon, QString msg, QDateTime datetime); void AppendOtherMessage(QPixmap icon,QString name, QString msg, QDateTime datetime);protected: virtual void mousePressEvent(QMouseEvent *e);private: void CheckTime(QDateTime datetime); //检查是否需要插入时间,传入值为当前消息时间 void AppendTime(QDateTime datetime, QString time); QMap items_;};
#include#include #include #include #include "chatview.h"#include "chatbaseitem.h"#include "selfmsgitem.h"#include "othermsgitem.h"#include "chattimeitem.h"#include "src/vapplication.h"const int kMarkRole = Qt::UserRole;const int kRoleOtherMsg = kMarkRole + 1;const int kRoleSelfMsg = kRoleOtherMsg + 1;const int kRoleTime = kRoleSelfMsg + 1;ChatView::ChatView(QWidget *parent) : QGraphicsView(parent){ setScene(new QGraphicsScene()); this->setAlignment(Qt::AlignLeft | Qt::AlignTop); setStyleSheet("background: rgb(245,245,245) ;border:0px"); this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); this->verticalScrollBar()->setStyleSheet(theStyleSheet["scrollbar"]);}ChatView::~ChatView(){ ClearAll();}void ChatView::Resize(int width){ int height = 20; for (ChatBaseItem* item : items_) { item->setPos(0, height); height = height + item->Resize(width); height += 20; } this->scene()->setSceneRect(QRectF(0, 0, width, height));}void ChatView::ClearAll(){ for (auto it = items_.begin(); it != items_.end();) { ChatBaseItem* item = it.value(); it = items_.erase(it); delete item; }}void ChatView::AppendSelfMessage(QPixmap icon, QString msg, QDateTime datetime){ ChatBaseItem* item = new SelfMsgItem(icon, msg, datetime); item->setData(kMarkRole,kRoleSelfMsg); CheckTime(datetime); scene()->addItem(item); items_.insert(datetime, item); Resize(this->viewport()->width()); update(); //滚动到底部 QScrollBar *vScrollBar = verticalScrollBar(); vScrollBar->setValue(vScrollBar->maximum());}void ChatView::AppendOtherMessage(QPixmap icon, QString name, QString msg, QDateTime datetime){ ChatBaseItem* item = new OtherMsgItem(icon, name, msg, datetime); item->setData(kMarkRole, kRoleOtherMsg); CheckTime(datetime); scene()->addItem(item); items_.insert(datetime, item); Resize(this->viewport()->width()); update(); QScrollBar *vScrollBar = verticalScrollBar(); vScrollBar->setValue(vScrollBar->maximum());}void ChatView::mousePressEvent(QMouseEvent *e){ //截获鼠标点击事件}void ChatView::CheckTime(QDateTime datetime){ if (items_.size() == 0|| datetime.secsTo(items_.lastKey())>60 * 5/*5分钟*/) { //第一条消息前插入时间 QDateTime dt = datetime.addMSecs(-1); AppendTime(dt,dt.toString("hh:mm:ss")); }}void ChatView::AppendTime(QDateTime datetime, QString time){ ChatBaseItem* item = new ChatTimeItem(time); item->setData(kMarkRole, kRoleTime); scene()->addItem(item); items_.insert(datetime, item); Resize(this->viewport()->width()); update();}
#pragma once#include "chatbaseitem.h"#includeclass ChatTimeItem : public ChatBaseItem{public: ChatTimeItem(QString time); virtual ~ChatTimeItem(); virtual int Resize(int width);protected: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);private: QGraphicsSimpleTextItem text_item_; QString text_; int port_width_;};
#include#include #include "chattimeitem.h"#include "color.h"ChatTimeItem::ChatTimeItem(QString time) : ChatBaseItem(){ text_item_.setText(time); item_tool::SetFontColor(&text_item_, 13, false, colorspace::GetTextLightColor()); text_item_.setParentItem(this);}ChatTimeItem::~ChatTimeItem()int ChatTimeItem::Resize(int width) port_width_ = width; return text_item_.boundingRect().height();void ChatTimeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) int width = text_item_.boundingRect().width(); text_item_.setX((port_width_ - width) / 2);
在子类化Item时,一定要注意重写virtual QRectF boundingRect() const;
方法,返回实际item的尺寸,让scene知道,并且要加入const,不然当消息左上角超出窗口范围时,会出现无法触发paint的问题。
以上就是关于"怎么使用QGraphicsView实现气泡聊天窗口+排雷功能"这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注行业资讯频道。
文字
气泡
内容
尺寸
消息
边框
功能
右侧
宽度
时间
三角
最大
两个
事件
位置
效果
文章
知识
矩形
篇文章
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
银行的软件开发岗笔试
云服务器和实验室服务器
网络安全感想30字
小学二年级网络安全教育班会
刀片服务器管理器
河源市数字通讯网络技术发展报告
网络安全线上主题晚会直播
方舟私人服务器怎么管理
多选框的值怎么存入数据库
山西大数据网络技术咨询机构
数据与事实性数据库的特点
乐陵软件开发怎么选
高中自学网络安全
国家对软件开发的新政策文件
奇安信服务器地址
浪潮服务器主板怎么设置硬盘
公司服务器一般是多少位
云计算数据库技术的应用
组建家用服务器需要什么主板
互联网科技网站导航
网络安全教育手抄报简单
服务器防护团队
郑州工控软件开发费用
新华三边缘计算服务器招标
迪普服务器
百信服务器供应公司
网络安全博览会展示什么
学习软件开发的培训学校
服务器端口开放后不通
连不上2k服务器