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

news/2025/3/1 16:20:14/文章来源:https://www.cnblogs.com/mengps/p/18673575

【写在前面】

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

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

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

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


【正文开始】

效果图

自定义 Qml 元素:DelRectangle

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

头文件(delrectangle.h)

#ifndef DELRECTANGLE_H
#define DELRECTANGLE_H#include <QQuickPaintedItem>class DelPen: public QObject
{Q_OBJECTQ_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_OBJECTQ_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.0Window {visible: truewidth: 400height: 300title: "DelRectangle Example"DelRectangle {anchors.centerIn: parentwidth: 200height: 150color: "lightblue"topLeftRadius: 20bottomRightRadius: 30border {width: 2color: "blue"}}
}

总结

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/869721.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【附源码】JAVA在线投票系统源码+SpringBoot+VUE+前后端分离

学弟,学妹好,我是爱学习的学姐,今天带来一款优秀的项目:在线投票系统源码 。 本文介绍了系统功能与部署安装步骤,如果您有任何问题,也请联系学姐,偶现在是经验丰富的程序员! 一. 系统演示 系统测试截图系统视频演示https://githubs.xyz/show/340.mp4二. 系统概述【 系统…

Python Playwright学习笔记(一)

一、简介 1.1Playwright 是什么? 它是微软在 2020 年初开源的新一代自动化测试工具,其功能和 selenium 类似,都可以驱动浏览器进行各种自动化操作。 1.2、特点是什么支持当前所有的主流浏览器,包括 chrome、edge、firefox、safari; 支持跨平台多语言:支持Windows、Linux、…

智能驾驶数据采集回注测评工具 - ARS

在数据驱动智能驾驶的时代背景下,开发者们总结了一条适用于智能驾驶的数据闭环开发流程,这条开发线路大致包括实车数据采集->数据存储->数据处理->数据分析->数据标注->模型训练->仿真测试->实车测试->部署发布等关键环节,通过不断开发迭代,逐步完…

2025.1.15 学习

2025.1.15 学习 api开放平台 我们希望在后端使用Http请求调用接口,应该怎么做呢 可以用Hutool工具库中的Http请求工具类,使用如下: public class ApiClient {public String getNameByGet(String name){HashMap<String, Object> paramMap = new HashMap<>();para…

2024龙信年终技术考核

1. 分析手机备份文件,该机主的QQ号为?(标准格式:123) 看了下,备份里没有QQ,但是有微信,所以应该是微信绑定的QQ号(早期微信推广时可以用QQ直接注册登录)经过测试,对应的是这个结果为1203494553 2. 分析手机备份文件,该机主的微信号为?(标准格式:abcdefg)结果为…

Dex文件结构】ReadDex解析器实现

# APP加壳脱壳 # DEX文件结构 近期学习DEX文件结构为学习APP加壳脱壳打基础,自实现了一个简易的DEX解析器加深理解。DEX文件结构整体看不复杂,深究时发现DexCLassDef结构非常复杂,编码的数据结构,嵌套和指向关系。本文作为近期学习的一个阶段总结以及知识分享,后期再完…

记录---浏览器多窗口通信有效实践总结

🧑‍💻 写在开头 点赞 + 收藏 === 学会🤣🤣🤣如何跨越不同窗口通信 在现代 Web 开发中,多个窗口或标签页之间的通信成为了越来越常见的需求,尤其是在应用需要同步数据、共享状态或进行实时更新的场景中。不同窗口间的通信方式有很多种,选择合适的方式可以大大提高…

python 按时间戳删除3232数组的前2列和后9列

还是雨滴谱文件,这次尝试批量处理 首先处理1个单独的txt文件#!usr/bin/env python # -*- coding:utf-8 _*- """@author:Suyue @file:raindrop.py @time:2025/01/15 {DAY} @desc: """ import numpy as np import redef process_file(input_file,…

电源中TL431及光耦的实战运用

首先了解一下TL431的基本原理;由一个运放及三极管组成;运放的应用前文略有几笔,此处未加反馈,运放只需要同相端与反相端做差在输出对应电压即可,而三极管是电压驱动;当VREF>2.5V即同相端大于反相端,输出正电压,三极管导通,当VREF<2.5V即同相端小于反相端,输出负…

在OERV也可以玩MC(下)

话接上回,上期讲述了在OERV安装HMCL的历程,这期讲讲HMCL的打包。Show openEuler:24.09 / HMCL - 开源软件构建与测试。在这个网站里,可以看到有好几个文件,这些都跟HMCL打包有关。 第一个是_service文件,这个文件用于从特定仓库里面拉取代码文件到当前平台,可以看见每个文…

JS-38 对象概述

什么是对象?对象(object)是JavaScript语言的核心概念,也是最重要的数据类型 简单说,对象就是一组“键对值”(key-value)的合集,是一种无序的复合数据集合 var user={ name:zifuchuan, age:13 };对象的每一个键名又称为属性(property),它的“键值”可以是任何数据类型…