outlinetextitemborder.h
#ifndef OUTLINETEXTITEMBORDER_H
#define OUTLINETEXTITEMBORDER_H
#include <QObject>
#include <QColor>
//这是一个简单的基于 QObject 的类,包含许多属性
class OutlineTextItemBorder : public QObject {
Q_OBJECT
//您可以看到 Q_PROPERTY 宏没有我们迄今为止一直使用的 READ 和 WRITE 关键字
//我们让 moc 生成代码,通过直接访问给定的类成员来对属性进行操作
//MEMBER 关键字的好处在于,如果我们还提供 NOTIFY 信号,则生成的代码将在属性值更改时发出该信号
Q_PROPERTY(int width MEMBER m_width NOTIFY widthChanged)
Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged)
Q_PROPERTY(Qt::PenStyle style MEMBER m_style NOTIFY styleChanged)
public:
OutlineTextItemBorder(QObject *parent = 0);
int width() const;
QColor color() const;
Qt::PenStyle style() const;
QPen pen() const;
signals:
void widthChanged(int);
void colorChanged(QColor);
void styleChanged(int);
private:
int m_width;
QColor m_color;
Qt::PenStyle m_style;
};
#endif // OUTLINETEXTITEMBORDER_H
outlinetextitemborder.cpp
#include "outlinetextitemborder.h"
#include <QPen>
OutlineTextItemBorder::OutlineTextItemBorder(QObject *parent) :
QObject(parent),
m_width(0),
m_color(Qt::transparent),
m_style(Qt::SolidLine)
{
}
int OutlineTextItemBorder::width() const {
return m_width;
}
QColor OutlineTextItemBorder::color() const {
return m_color;
}
Qt::PenStyle OutlineTextItemBorder::style() const {
return m_style;
}
QPen OutlineTextItemBorder::pen() const {
QPen p;
p.setColor(m_color);
p.setWidth(m_width);
p.setStyle(m_style);
return p;
}
outlinetextitem.h
#ifndef OUTILINETEXTITEM_H
#define OUTILINETEXTITEM_H
#include <QQuickPaintedItem>
#include <QPainterPath>
class OutlineTextItemBorder;
//该类将为我们的主要项目类提供分组属性
class OutlineTextItem : public QQuickPaintedItem
{
Q_OBJECT
//除了颜色、字体和轮廓数据的分组属性之外,该界面还定义了要绘制的文本的属性
//同样,我们使用 MEMBER 来避免必须手动实现 getter 和 setter
Q_PROPERTY(QString text MEMBER m_text
NOTIFY textChanged)
Q_PROPERTY(QColor color MEMBER m_color
NOTIFY colorChanged)
Q_PROPERTY(OutlineTextItemBorder* border READ border
NOTIFY borderChanged)
Q_PROPERTY(QString fontFamily MEMBER m_fontFamily
NOTIFY fontFamilyChanged)
Q_PROPERTY(int fontPixelSize MEMBER m_fontPixelSize
NOTIFY fontPixelSizeChanged)
public:
OutlineTextItem(QQuickItem *parent = 0);
void paint(QPainter *painter);
OutlineTextItemBorder* border() const;
QPainterPath borderShape(const QPainterPath &path) const;
private slots:
//我们也直接调用同一个槽来准备item的初始状态。
void updateItem();
signals:
void textChanged(QString);
void colorChanged(QColor);
void borderChanged();
void fontFamilyChanged(QString);
void fontPixelSizeChanged(int);
private:
OutlineTextItemBorder* m_border;
QPainterPath m_path;
QRectF m_boundingRect;
QString m_text;
QColor m_color;
QString m_fontFamily;
int m_fontPixelSize;
};
#endif // OUTILINETEXTITEM_H
outlinetextitem.cpp
#include "outlinetextitem.h"
#include "outlinetextitemborder.h"
#include <QPainter>
OutlineTextItem::OutlineTextItem(QQuickItem *parent) :
QQuickPaintedItem(parent)
{
//我们基本上将来自对象及其分组属性对象的所有属性更改信号连接到同一个插槽
//如果项目的任何组件被修改,该插槽将更新Item的数据
m_border = new OutlineTextItemBorder(this);
connect(this, &OutlineTextItem::textChanged,
this, &OutlineTextItem::updateItem);
connect(this, &OutlineTextItem::colorChanged,
this, &OutlineTextItem::updateItem);
connect(this, &OutlineTextItem::fontFamilyChanged,
this, &OutlineTextItem::updateItem);
connect(this, &OutlineTextItem::fontPixelSizeChanged,
this, &OutlineTextItem::updateItem);
connect(m_border, &OutlineTextItemBorder::widthChanged,
this, &OutlineTextItem::updateItem);
connect(m_border, &OutlineTextItemBorder::colorChanged,
this, &OutlineTextItem::updateItem);
connect(m_border, &OutlineTextItemBorder::styleChanged,
this, &OutlineTextItem::updateItem);
updateItem();
}
void OutlineTextItem::paint(QPainter *painter) {
if(m_text.isEmpty()) return;
painter->setPen(m_border->pen());
painter->setBrush(m_color);
painter->setRenderHint(QPainter::Antialiasing, true);
painter->translate(-m_boundingRect.topLeft());
painter->drawPath(m_path);
}
OutlineTextItemBorder *OutlineTextItem::border() const {
return m_border;
}
QPainterPath OutlineTextItem::borderShape(const QPainterPath &path) const
{
//borderShape() 函数返回一个新的画家路径,其中包括原始路径及其使用 QPainterPathStroker 对象创建的轮廓。 这是为了在计算边界矩形时正确考虑笔划的宽度。
QPainterPathStroker pathStroker;
pathStroker.setWidth(m_border->width());
QPainterPath p = pathStroker.createStroke(path);
p.addPath(path);
return p;
}
//我们也直接调用同一个槽来准备item的初始状态。
void OutlineTextItem::updateItem() {
//开始时,该函数重置作为绘制轮廓文本的后端的画家路径对象
//并使用使用字体集绘制的文本对其进行初始化
QFont font(m_fontFamily, m_fontPixelSize);
m_path = QPainterPath();
m_path.addText(0, 0, font, m_text);
//槽使用我们将很快看到的 borderShape() 函数计算路径的边界矩形
//我们使用 controlPointRect() 来计算边界矩形
//因为它比 boundingRect() 快得多,并且返回一个大于或等于一个 boundingRect() 的区域
m_boundingRect = borderShape(m_path).controlPointRect();
setImplicitWidth(m_boundingRect.width());
setImplicitHeight(m_boundingRect.height());
//并要求项目使用 update() 调用重新绘制自身
update();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.3
import OutlineTextItem 1.0
Window {
visible: true
width: 800
height: 400
title: qsTr("Hello World")
Rectangle {
anchors.fill: parent
OutlineTextItem {
anchors.centerIn: parent
text: "This is outlined text"
fontFamily: "Arial"
fontPixelSize: 64
color: "#33ff0000"
antialiasing: true
border {
color: "blue"
width: 2
style: Qt.DotLine
}
}
}
}
main.cpp
#include <QQuickView>
#include <QGuiApplication>
#include <QtQml>
#include "outlinetextitem.h"
#include "outlinetextitemborder.h"
int main(int argc, char **argv) {
QGuiApplication app(argc, argv);
qmlRegisterUncreatableType<OutlineTextItemBorder>(
"OutlineTextItem", 1, 0, "OutlineTextItemBorder", "");
qmlRegisterType<OutlineTextItem>(
"OutlineTextItem", 1, 0, "OutlineTextItem");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}