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

 更新时间:2025年01月16日 08:59:53   作者:梦起丶  
在 Qml 中,矩形(Rectangle)是最常用的元素之一,本文将介绍如何通过自定义 Qml 元素实现一个任意角可为圆角的矩形,感兴趣的可以了解下

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

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

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

本文将介绍如何通过自定义 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();
}

关键点解析

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

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

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

绘制逻辑:在 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 将提供同等功能( 作为技术预览 ),并且效果更好:

到此这篇关于Qt Qml实现任意角为圆角的矩形的文章就介绍到这了,更多相关Qt Qml圆角矩形内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解C++虚函数的工作原理

    详解C++虚函数的工作原理

    这篇文章主要介绍了C++虚函数的工作原理的的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • Opencv透视变换综合实例详解

    Opencv透视变换综合实例详解

    这篇文章主要为大家详细介绍了Opencv透视变换综合实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • C++ 二进制文件读写方式及示例详解

    C++ 二进制文件读写方式及示例详解

    这篇文章主要为大家介绍了C++ 二进制文件读写实现方式及示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • C++之openFrameworks框架介绍

    C++之openFrameworks框架介绍

    本章我们将介绍一个非常好用的跨平台的 C++开源框架 openFrameworks。它是一个开源的跨平台的C++工具包,方便开发者创建出一个更简单和直观的框架,擅长开发图像和动画,感兴趣的同学可以参考一下
    2023-05-05
  • C++深入探究继承的概念与使用

    C++深入探究继承的概念与使用

    继承是C++面向对象编程中的一门。继承是子类继承父类的特征和行为,或者是继承父类得方法,使的子类具有父类得的特性和行为。重写是子类对父类的允许访问的方法实行的过程进行重新编写,返回值和形参都不能改变。就是对原本的父类进行重新编写,但是外部接口不能被重写
    2022-05-05
  • c++算法进阶删除有序链表中的重复元素

    c++算法进阶删除有序链表中的重复元素

    这篇文章主要为大家介绍了c++算法进阶删除有序链表中的重复元素示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • C++小游戏BrickHit实例代码

    C++小游戏BrickHit实例代码

    本文通过实例代码给大家介绍了C++小游戏BrickHit的相关资料,需要的朋友可以参考下
    2018-02-02
  • C++实现雷霆战机可视化小游戏

    C++实现雷霆战机可视化小游戏

    这篇文章主要为大家详细介绍了C++实现雷霆战机可视化小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • c语言malloc函数的用法示例和意义

    c语言malloc函数的用法示例和意义

    mallo函数返回的实际是一个无类型指针,必须在其前面加上指针类型强制转换才可以使用,这篇文章主要介绍了c语言malloc函数的用法示例和意义,需要的朋友可以参考下
    2022-12-12
  • C++中的string类型

    C++中的string类型

    这篇文章主要介绍了C++中的string类型,在C++当中,除了char 类型,还有专门的字符串类型,就叫做string,下面文字将围绕其相关资料展开详细内容,需要的朋友可以参考一下,希望对你有所帮助
    2021-11-11

最新评论