怎么使用QGraphicsView实现气泡聊天窗口+排雷功能
发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇"怎么使用QGraphicsView实现气泡聊天窗口+排雷功能"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收
千家信息网最后更新 2026年11月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安全错误
数据库的锁怎样保障安全
涨乐互联网科技
互联网科技开发公司
互联网科技公司电话
亿策互联网科技
美国互联网黑科技
广西互联网科技公司
滨湖区互联网智能科技产品供应
哈尔滨秒房互联网科技有限公司
中国科技互联网公司收入
互联网科技实习生岗位职责
移动互联网年度科技创新
明优互联网科技有限公司
保险科技服务互联网营销体系
互联网企业科技类企业如何做账
互联网科技资金流入
互联网安防科技感
保山互联网科技靠谱吗
众联互联网络科技
第三届互联网大会安防黑科技
互联网科技快速发展优美句子
高科技互联网
科技信息互联网
合肥互联网科技公司
互联网与新科技
亿人帮互联网科技
滨湖区互联网智能科技产品供应
西藏七维空间互联网科技有限公司
消费医药和互联网科技
长沙渺峰互联网科技有限公司
恒生科技指数 中概互联网