QGraphicsItem 是场景中 item 的基类。图形视图提供了一些典型形状的标准 item,例如:矩形 ( QGraphicsRectItem )、椭圆 ( QGraphicsEllipseItem ) 、文本项 ( QGraphicsTextItem )。当这些不满足需求时(例如:需要一些特定形状时),往往需要自定义,通常的做法就是继承 QGraphicsItem(QGraphicsObject)。
必须要实现两个虚函数,否则会报allocating an object of abstract class type “xxxx”
。
除此之外,可能还需要附加其他需求,
例如:QPainterPath shape() - item 的形状 由contains() 和 collidesWithPath() 用于碰撞检测。如果未实现,则默认为 boundingRect()。
使用信号/槽、属性机制:继承 QObject 和 QGraphicsItem(或直接继承 QGraphicsObject)
将 item 的外边界定义为矩形,所有绘制必须限制在此区域内,QGraphicsView 使用它来确定 item 是否需要重绘。
虽然 item 的形状可以是任意的(例如:直线、椭圆、矩形 ),但是 bounding rect 总是矩形,并且不受 item 变换的影响。
它是一个虚函数,具体需求依靠自己去实现。
以本地坐标中的 QPainterPath 形式返回 item 的形状。形状可用于许多事情,包括:碰撞检测,命中测试以及 QGraphicsScene::items() 函数。
shape() 默认实现调用 boundingRect() 返回一个简单的矩形形状,但子类可以重新实现该函数,以返回非矩形 item 更准确的形状。例如,一个圆形 item 可以选择返回椭圆形状,以便更好地进行碰撞检测。
shape() 由 contains() 和 collidesWithPath() 的默认实现调用。
QT版本:5.14.2
操作系统:win10 64位
最近做一个播放器的项目,需要用户自定义编辑定制视频的字幕;为了方便编辑字幕,这里QGraphicsTextItem需要支持任意拖动,回车确认,鼠标单机进入编辑状态等操作。
实现思路: 重写QGraphicsTextItem,完成功能定义。
软件运行效果如下:
.h文件
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class TextItem :
public QGraphicsTextItem
{
public:
explicit TextItem(const QRectF &rt,QGraphicsItem *parent = nullptr);
~TextItem();
void init();
void setRect(const QRectF &rect);
void updateFontInfo();
void setText(QString text);
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)override;
QRectF boundingRect()const override;
int type() const;
virtual void focusInEvent(QFocusEvent *focusEvent)override;//焦点进入事件
void focusOutEvent(QFocusEvent *focusEvent)override;
void mousePressEvent(QGraphicsSceneMouseEvent *event)override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)override;
QPainterPath shape() const;
private:
QRectF m_rect;
QPen m_pen;
QString m_text;
QFontMetricsF *m_fontMetricsF = NULL;
qreal m_fontWidth;
qreal m_fontHeight;
bool isMousePress = false;
};
.cpp文件
#include "TextItem.h"
#include
TextItem::TextItem(const QRectF & rt, QGraphicsItem *parent)
:QGraphicsTextItem(parent)
{
m_rect = rt;
init();
}
TextItem::~TextItem()
{
if (m_fontMetricsF != NULL) {
delete m_fontMetricsF;
m_fontMetricsF = NULL;
}
}
void TextItem::init()
{
m_text = "";
m_color = QColor(255, 255, 255);
m_font = QFont("SimSun", 20);
//m_font.setPixelSize(30);
m_focus = false;
setFont(m_font);
setDefaultTextColor(QColor(255, 255, 255));//设置默认文本颜色
setEnabled(true);
setTextInteractionFlags(Qt::TextEditorInteraction);//设置item可编辑
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
}
void TextItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
{
painter->save();//保存painter初始状态
painter->setPen(QPen(QBrush(m_color), 2, Qt::SolidLine));
//当选择该item时,背景颜色变为白色透明
if (isSelected())
{
painter->setBrush(QColor(240, 240, 240, 60));//颜色是rgb 240 240 240 透明度为60
}
painter->setFont(m_font);
painter->setRenderHint(QPainter::Antialiasing, true);//防止出现锯齿现象(反锯齿绘制)
painter->drawRect(boundingRect());//画框
QString text = toPlainText();
setText(text);//更新文本框长度
painter->restore();//恢复painter状态
QGraphicsTextItem::paint(painter, option, widget);
}
QRectF TextItem::boundingRect() const
{
//qreal labelwidth = 50;
return QRectF(m_rect.x(), m_rect.y(), m_rect.width(), m_rect.height());
}
//类对象可调用此函数实现自定义编辑框大小
void TextItem::setRect(const QRectF & rect)
{
m_rect = rect;
}
//计算m_fontWidth,m_fontHeight给自定义的boundingRect使用,同时为QGraphicsTextItem设置font和plainTex
void TextItem::updateFontInfo()
{
if (Q_NULLPTR != m_fontMetricsF)
delete m_fontMetricsF;
m_fontMetricsF = new QFontMetricsF(m_font);
m_fontWidth = m_fontMetricsF->width(m_text);
m_fontHeight = m_fontMetricsF->height();
//当输入文字大于文本框时
if (m_fontWidth > boundingRect().width())
{
qreal adjust = 5;
this->setRect(QRectF(boundingRect().x(), boundingRect().y(), m_fontWidth + adjust,boundingRect().height()));
this->setFont(m_font);
this->setPlainText(m_text);
//当更新文本框后,光标的位置设置,文本的末尾
QTextCursor cursor = this->textCursor();
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor, 1);
this->setTextCursor(cursor);
}
//当输入文字小于文本框时
if (m_fontHeight > boundingRect().height())
{
qreal adjust = 5;
this->setRect(QRectF(boundingRect().x(), boundingRect().y(), boundingRect().width(), m_fontHeight + adjust));
this->setFont(m_font);
this->setPlainText(m_text);
}
}
void TextItem::setText(QString text)
{
m_text = text;
updateFontInfo();
}
//设置自定义的TextItem的类型
int TextItem::type() const
{
return UserType + 5;
}
//获取焦点事件
void TextItem::focusInEvent(QFocusEvent *focusEvent)
{
qDebug()<<"********** focus in *************";
setTextInteractionFlags(Qt::TextEditorInteraction);
QGraphicsTextItem::focusInEvent(focusEvent);
}
//失去焦点事件
void TextItem::focusOutEvent(QFocusEvent *focusEvent)
{
qDebug()<<"focus out";
//setTextInteractionFlags(Qt::NoTextInteraction);
QGraphicsTextItem::focusOutEvent(focusEvent);
isMousePress = false;
}
void TextItem::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
QGraphicsTextItem::mousePressEvent(event);
Q_UNUSED(event);
if (!isMousePress)
{
QTextCursor cursor = this->textCursor();
cursor.select(QTextCursor::Document);//选中文本
this->setTextCursor(cursor);
isMousePress = true;
}
}
void TextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
QGraphicsTextItem::mouseReleaseEvent(event);
Q_UNUSED(event);
}
QPainterPath TextItem::shape() const
{
QPainterPath path;
path.addRect(boundingRect());
return path;
}
参考博主:(https://blog.csdn.net/liunanya/article/details/94377779)