Qml 中实现任意角为圆角的矩形

【写在前面】

在 Qml 中,矩形(Rectangle)是最常用的元素之一。

然而,标准的矩形元素仅允许设置统一的圆角半径。

在实际开发中,我们经常需要更灵活的圆角设置,例如只对某些角进行圆角处理,或者设置不同角的圆角半径。

本文将介绍如何通过自定义 Qml 元素实现一个任意角可为圆角的矩形。


【正文开始】

效果图

Qml 中实现任意角为圆角的矩形

自定义 Qml 元素:DelRectangle

我们将创建一个名为 DelRectangle 的自定义 Qml 元素,它继承自 QQuickPaintedItem,并重写其 paint() 方法来自定义绘制逻辑。

头文件(delrectangle.h)

#ifndef DELRECTANGLE_H #define DELRECTANGLE_H  #include <QQuickPaintedItem>  class DelPen: public QObject {     Q_OBJECT     Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged FINAL)     Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)     QML_NAMED_ELEMENT(DelPen) public:     DelPen(QObject *parent = nullptr);     qreal width() const;     void setWidth(qreal width);     QColor color() const;     void setColor(const QColor &color);     bool isValid() const; signals:     void widthChanged();     void colorChanged(); private:     qreal m_width = 1;     QColor m_color = Qt::transparent; };  class DelRectangle: public QQuickPaintedItem {     Q_OBJECT     Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged FINAL)     Q_PROPERTY(qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY topLeftRadiusChanged FINAL)     Q_PROPERTY(qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY topRightRadiusChanged FINAL)     Q_PROPERTY(qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius NOTIFY bottomLeftRadiusChanged FINAL)     Q_PROPERTY(qreal bottomRightRadius READ bottomRightRadius WRITE setBottomRightRadius NOTIFY bottomRightRadiusChanged FINAL)     Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)     Q_PROPERTY(DelPen* border READ border CONSTANT)     QML_NAMED_ELEMENT(DelRectangle) public:     explicit DelRectangle(QQuickItem *parent = nullptr);     ~DelRectangle();     qreal radius() const;     void setRadius(qreal radius);     qreal topLeftRadius() const;     void setTopLeftRadius(qreal radius);     qreal topRightRadius() const;     void setTopRightRadius(qreal radius);     qreal bottomLeftRadius() const;     void setBottomLeftRadius(qreal radius);     qreal bottomRightRadius() const;     void setBottomRightRadius(qreal radius);     QColor color() const;     void setColor(QColor color);     DelPen *border();     void paint(QPainter *painter) override; signals:     void radiusChanged();     void topLeftRadiusChanged();     void topRightRadiusChanged();     void bottomLeftRadiusChanged();     void bottomRightRadiusChanged();     void colorChanged(); private:     Q_DECLARE_PRIVATE(DelRectangle);     QSharedPointer<DelRectanglePrivate> d_ptr; };  #endif // DELRECTANGLE_H 

实现文件(delrectangle.cpp)

#include "delrectangle.h"  #include <QPainter> #include <QPainterPath> #include <private/qqmlglobal_p.h>  class DelRectanglePrivate { public:     QColor m_color = { 0xffffff };     DelPen *m_pen = nullptr;     qreal m_radius = 0;     qreal m_topLeftRadius = 0;     qreal m_topRightRadius = 0;     qreal m_bottomLeftRadius = 0;     qreal m_bottomRightRadius = 0; };  DelRectangle::DelRectangle(QQuickItem *parent)     : QQuickPaintedItem{parent}     , d_ptr(new DelRectanglePrivate) { }  DelRectangle::~DelRectangle() { }  qreal DelRectangle::radius() const {     Q_D(const DelRectangle);     return d->m_radius; }  void DelRectangle::setRadius(qreal radius) {     Q_D(DelRectangle);     if (d->m_radius != radius) {         d->m_radius = radius;         d->m_topLeftRadius = radius;         d->m_topRightRadius = radius;         d->m_bottomLeftRadius = radius;         d->m_bottomRightRadius = radius;         emit radiusChanged();         update();     } }  // 其他 getter 和 setter 方法省略...  QColor DelRectangle::color() const {     Q_D(const DelRectangle);     return d->m_color; }  void DelRectangle::setColor(QColor color) {     Q_D(DelRectangle);     if (d->m_color != color) {         d->m_color = color;         emit colorChanged();         update();     } }  DelPen *DelRectangle::border() {     Q_D(DelRectangle);     if (!d->m_pen) {         d->m_pen = new DelPen;         QQml_setParent_noEvent(d->m_pen, this);         connect(d->m_pen, &DelPen::colorChanged, this, [this]{ update(); });         connect(d->m_pen, &DelPen::widthChanged, this, [this]{ update(); });         update();     }     return d->m_pen; }  void DelRectangle::paint(QPainter *painter) {     Q_D(DelRectangle);     painter->save();     painter->setRenderHint(QPainter::Antialiasing);     QRectF rect = boundingRect();     if (d->m_pen && d->m_pen->isValid()) {         rect = boundingRect();         if (rect.width() > d->m_pen->width() * 2) {             auto dx = d->m_pen->width() * 0.5;             rect.adjust(dx, 0, -dx, 0);         }         if (rect.height() > d->m_pen->width() * 2) {             auto dy = d->m_pen->width() * 0.5;             rect.adjust(0, dy, 0, -dy);         }         painter->setPen(QPen(d->m_pen->color(), d->m_pen->width(), Qt::SolidLine, Qt::SquareCap, Qt::SvgMiterJoin));     }     QPainterPath path;     path.moveTo(rect.bottomRight() - QPointF(0, d->m_bottomRightRadius));     path.lineTo(rect.topRight() + QPointF(0, d->m_topRightRadius));     path.arcTo(QRectF(QPointF(rect.topRight() - QPointF(d->m_topRightRadius * 2, 0)), QSize(d->m_topRightRadius * 2, d->m_topRightRadius * 2)), 0, 90);     path.lineTo(rect.topLeft() + QPointF(d->m_topLeftRadius, 0));     path.arcTo(QRectF(QPointF(rect.topLeft()), QSize(d->m_topLeftRadius * 2, d->m_topLeftRadius * 2)), 90, 90);     path.lineTo(rect.bottomLeft() - QPointF(0, d->m_bottomLeftRadius));     path.arcTo(QRectF(QPointF(rect.bottomLeft().x(), rect.bottomLeft().y() - d->m_bottomLeftRadius * 2), QSize(d->m_bottomLeftRadius * 2, d->m_bottomLeftRadius * 2)), 180, 90);     path.lineTo(rect.bottomRight() - QPointF(d->m_bottomRightRadius, 0));     path.arcTo(QRectF(QPointF(rect.bottomRight() - QPointF(d->m_bottomRightRadius * 2, d->m_bottomRightRadius * 2)), QSize(d->m_bottomRightRadius * 2, d->m_bottomRightRadius * 2)), 270, 90);     painter->setBrush(d->m_color);     painter->drawPath(path);     painter->restore(); } 

关键点解析

  1. 自定义 Qml 元素:通过继承 QQuickPaintedItem 并使用 QML_NAMED_ELEMENT 宏,我们可以将自定义的 C++ 类注册为 Qml 元素。

  2. 属性定义:我们定义了多个属性来控制矩形的圆角半径和颜色,例如 radiustopLeftRadiuscolor 等,并提供了相应的 getter 和 setter 方法。

  3. 边框管理:通过 DelPen 类来管理矩形的边框样式,包括边框颜色和宽度。

  4. 绘制逻辑:在 paint() 方法中,我们使用 QPainterPath 来绘制具有不同圆角的矩形。通过组合使用直线和弧线,我们可以实现任意角的圆角效果。

使用示例

在 Qml 文件中,我们可以像使用标准的 Rectangle 元素一样使用 DelRectangle

import QtQuick 2.15 import QtQuick.Window 2.15 import DelegateUI.Controls 1.0  Window {     visible: true     width: 400     height: 300     title: "DelRectangle Example"      DelRectangle {         anchors.centerIn: parent         width: 200         height: 150         color: "lightblue"         topLeftRadius: 20         bottomRightRadius: 30         border {             width: 2             color: "blue"         }     } } 

总结

通过自定义 Qml 元素 DelRectangle,我们实现了对矩形圆角的更灵活控制,使其能够满足更多实际开发需求。

要注意的是,在 Qt 6.7 版本以后,内置的 Rectangle 将提供同等功能( 作为技术预览 ),并且效果更好:

Qml 中实现任意角为圆角的矩形

最后:项目链接(多多star呀..⭐_⭐):

Github: https://github.com/mengps/QmlControls

Gitee: https://gitee.com/MenPenS/QmlControls

发表评论

相关文章