用qml画坐标

本文介绍了一个使用QML和C++实现的坐标界面绘制方法。通过自定义OrdinateItem类,实现了可配置的坐标轴线宽、颜色及刻度数量等功能,并提供了qml中的使用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在做一个关于坐标的界面,

花了两天用qml画出来了,这里贴出来分享一下;

c++类OrdinateItem

ordinateItem.h文件

#ifndef ORDINATEITEMS_H
#define ORDINATEITEMS_H

#include <QQuickItem>

namespace UtAbCapb {

class OrdinateItem : public QQuickItem//这里必须继承QQuickItem,因为要用到它的很多属性
{
    Q_OBJECT
    Q_PROPERTY(int scaleViewPostion  READ scaleViewPostion WRITE setScaleViewPostion NOTIFY scaleViewPostionChanged)
    Q_PROPERTY(int      lineWidth    READ lineWidth  WRITE setLineWidth  NOTIFY lineWidthChanged)
    Q_PROPERTY(QString  lineColor    READ lineColor  WRITE setLineColor  NOTIFY lineColorChanged)
    Q_PROPERTY(int      rulerTotal   READ rulerTotal WRITE setRulerTotal NOTIFY rulerTotalChanged)

    enum scaleVP{
        RightVP=0,
        LeftVP
    };
public:
    OrdinateItem();//构造

private:
    void init();//初始化
public:
    int     scaleViewPostion();             // 刻度显示位置(0:默认左边,1:右边)
    void    setScaleViewPostion(int vp);    //设置刻度显示位置
    int     lineWidth();                    // 画线线宽(默认3)
    void    setLineWidth(int width);        //设置线宽
    QString lineColor();                    // 画线颜色
    void    setLineColor(QString color);    //设置画线颜色
    int     rulerTotal();                   // 总刻度数(默认4)
    void    setRulerTotal(int count);       //设置刻度数

    QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *);//更新节点

signals:
    void scaleViewPostionChanged();    //刻度显示位置变化信号
    void lineWidthChanged();  //线宽变化
    void lineColorChanged();  //线颜色变化
    void rulerTotalChanged(); //刻度总是变化
    void rulerUnitChanged(); //刻度初始位置信号
public slots:

private:
    int     m_scale_vp;         // 刻度显示位置
    int     m_line_width;       // 线宽
    QString m_line_color;       // 线颜色
    int     m_ruler_count;      // 刻度大小
};

}
#endif // ORDINATEITEMS_H

ordinateItem.cpp文件

 

#include "ordinateitem.h"
#include <QSGGeometryNode>
#include <QSGFlatColorMaterial>
#include <QDebug>

namespace UtAbCapb {

OrdinateItem::OrdinateItem()
{
    setFlag(ItemHasContents, true);//设置标识
    this->init();
}

void OrdinateItem::init()//初始化刻度,线宽,颜色等
{
    m_scale_vp = LeftVP;
    m_line_width = 1;
    m_line_color = "#C5AD63";
    m_ruler_count = 35;//纵坐标刻度35
}

int OrdinateItem::scaleViewPostion()
{
    return m_scale_vp;
}

void OrdinateItem::setScaleViewPostion(int vp)
{
    m_scale_vp = vp;
}

int OrdinateItem::lineWidth()
{
    return m_line_width;
}

void OrdinateItem::setLineWidth(int width)
{
    m_line_width = width;
}

QString OrdinateItem::lineColor()
{
    return m_line_color;
}

void OrdinateItem::setLineColor(QString color)
{
    m_line_color = color;
}

int OrdinateItem::rulerTotal()
{
    return m_ruler_count;
}

void OrdinateItem::setRulerTotal(int count)
{
    if(count>=10 && count<120){
        m_ruler_count = count;
        update();
    }
}

QSGNode *OrdinateItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
{
    float max_x = (width()>30)? 20 : width();//定义获取宽度,这里获取的是qml中设定的宽度
    float max_y = height();//获取高度因为继承了QQuickItem
    float rulerUnit = (max_y-10)/m_ruler_count;//卡尺比例
    float scal_h = max_x;//毫米刻度,
    float scal_m = max_x*0.8;//分米刻度
    float scal_l = max_x*0.5;//厘米刻度
    int totalCount = m_ruler_count*2 + 4;//刻度总数

    QSGGeometryNode *node = 0;//定义node,用于场景图中的所有渲染内容
    node = static_cast<QSGGeometryNode *>(oldNode);
    QSGGeometry *geometry;//定义geometry,为图形提供低级存储
    QSGFlatColorMaterial *material;//定义material,提供了一种在场景图中绘制纯色几何图形的方法
    if(!node) {
        node = new QSGGeometryNode;
        geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), totalCount);//默认2d绘图。创建一个没有几何和材质的新的几何节点
        geometry->setDrawingMode(GL_LINES);//绘图模式,GL画线
        geometry->setLineWidth(m_line_width);//设置线宽
        material = new QSGFlatColorMaterial;//实例
        material->setColor(QColor(m_line_color));//设置颜色
        node->setGeometry(geometry);//将geometry添加到node
        node->setFlag(QSGNode::OwnsGeometry);//
        node->setMaterial(material);//将此几何节点的材质设置为material
    } else {
        geometry = node->geometry();
        material = static_cast<QSGFlatColorMaterial *>(node->material());
        geometry->allocate(totalCount);
    }
    //定义vertices,获取的是geometry,2D的顶点数据指针
    QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D();
    int max = (m_scale_vp == LeftVP)? max_x : 0;//显示的位置
    vertices[0].set(max, 0);//画直线从0开始画到max_y
    vertices[1].set(max, max_y);//根据的是屏幕分辨率高度
    float x0=0;
    float y0=max_y;
    float x;
    float y;
    int cout =1;
    for(int i=0;i<=m_ruler_count;i++){
        int itype = (i%5==0 && i%10==0)? 0 :((i%5==0 && i%10!=0)? 1 : 2); //0:max,1:mid,2:min
        switch (itype) {
        case 0:
            x0 = 0;
            x = scal_h;
            y = y0;
            break;
        case 1:
            x0 = (m_scale_vp == LeftVP)? scal_h*0.2 : 0;
            x = (m_scale_vp == LeftVP)? scal_h : scal_m;
            y = y0;
            break;
        case 2:
            x0 = (m_scale_vp == LeftVP)? scal_l : 0;
            x = (m_scale_vp == LeftVP)? scal_h : scal_l;
            y = y0;
            break;
        default:
            break;
        }
        vertices[++cout].set(x0, y0);//画线起始点
        vertices[++cout].set(x, y);//终止点
        y0 = y0 - rulerUnit;
    }
    node->markDirty(QSGNode::DirtyGeometry);
    return node;
}
}

在主函数main中:

qmlRegisterType<OrdinateItem>("OrdinateItem.com", 1, 0, "OrdinateItem");//注册到qml中

在qml中引用:

import OrdinateItem.com 1.0

 

然后可以用了;

    OrdinateItem{ id: ordId; width: 20; height: parent.height*0.4; rulerTotal: 35; anchors.bottom: horId.top;
        //scaleViewPostion: 1
        MouseArea{ anchors.fill: parent;
            property point clickPos: "0,0"
            onPressed: {
                clickPos = Qt.point(mouse.x,mouse.y);
            }
            onPositionChanged: {
                //鼠标偏移量
                var delta = Qt.point(mouse.x-clickPos.x, mouse.y-clickPos.y)
                coordId.x = coordId.x+delta.x;
                coordId.y = coordId.y+delta.y;
            }
        }
    }


 



 

<think>嗯,用户想用QML实现带断裂效果的坐标轴。首先,我需要理解断裂效果是什么样的。通常,坐标轴断裂用于当数据存在较大间隔时,省略中间不重要的部分,让图表更紧凑。比如,Y轴可能在中间某段断开,表示数值跳跃。 接下来,我应该考虑QML中的绘图组件。QML有Canvas元素,可以用来绘制自定义图形。所以,可能需要用Canvas来手动绘制坐标轴,并在适当的位置添加断裂效果。 然后,步骤分解。首先绘制主轴线,然后在需要断裂的位置两个倾斜的短线,模拟断裂效果。可能需要计算断裂的位置,比如在Y轴的某个区间中间断开。例如,数据范围是0-50和100-150,中间的50-100被省略,这时候在Y轴的中间位置断裂符号。 代码结构方面,可能需要一个Canvas元素,在onPaint事件中使用Context2D进行绘制。需要处理坐标轴的刻度、标签以及断裂处的标记。断裂符号通常用两条斜线表示,中间断开。 另外,数据的分段处理也很重要。用户可能需要将数据分为多个段,每个段对应不同的轴区间。例如,使用两个区域,中间有断裂。这时候坐标轴的标签也需要相应调整,显示正确的数值。 可能遇到的问题是如何动态调整断裂的位置和数量,以及如何让断裂后的坐标轴与数据正确对应。比如,如果数据分为三段,就需要两处断裂。代码可能需要支持多个断裂点,并根据数据自动计算位置。 测试示例的话,可以创建一个简单的例子,数据分为两部分,中间有断裂。然后根据这个数据绘制Y轴,并在断裂处添加标记。同时,X轴保持不变,或者也添加断裂,视需求而定。 另外,参考用户提供的引用[^1],虽然内容是关于QML 3D打印的,但可能有一些通用的QML绘图技巧可以借鉴,比如使用ShaderEffect或者自定义组件来实现复杂的视觉效果。不过,断裂效果可能用Canvas就足够了。 最后,确保代码正确使用Canvas的API,比如beginPath, moveTo, lineTo, stroke等,正确设置线条颜色和宽度。可能还需要处理缩放和动态调整,使得坐标轴在不同尺寸下也能正确显示断裂效果。</think>### QML实现带断裂效果的坐标轴步骤 在QML中实现带断裂效果的坐标轴,可以通过`Canvas`元素自定义绘制逻辑。核心思路是通过分段绘制轴线,并在断裂处添加特殊标记。以下是具体实现步骤: 1. **坐标系定义** ```qml Canvas { id: axisCanvas anchors.fill: parent onPaint: { var ctx = getContext("2d") ctx.clearRect(0, 0, width, height) ctx.strokeStyle = "black" ctx.lineWidth = 2 } } ``` 2. **绘制断裂标记函数** ```qml function drawBreak(ctx, startX, startY) { ctx.beginPath() ctx.moveTo(startX - 5, startY - 5) ctx.lineTo(startX + 5, startY + 5) // 绘制斜线 ctx.moveTo(startX - 5, startY + 5) ctx.lineTo(startX + 5, startY - 5) // 交叉斜线形成断裂效果 ctx.stroke() } ``` 3. **分段绘制Y轴示例** ```qml // 下半段绘制(0-40) ctx.beginPath() ctx.moveTo(50, 300) ctx.lineTo(50, 200) ctx.stroke() drawBreak(ctx, 50, 190) // 添加断裂标记 // 上半段绘制(60-100) ctx.beginPath() ctx.moveTo(50, 100) ctx.lineTo(50, 50) ctx.stroke() drawBreak(ctx, 50, 110) // 添加断裂标记 ``` 4. **动态数据绑定(示例数据模型)** ```qml ListModel { id: axisData ListElement { segment: "lower"; start: 0; end: 40 } ListElement { segment: "upper"; start: 60; end: 100 } } ``` 5. **完整组件实现** ```qml Canvas { id: axisCanvas anchors.fill: parent onPaint: { var ctx = getContext("2d") ctx.clearRect(0, 0, width, height) // 绘制主轴线 ctx.strokeStyle = "#333" ctx.lineWidth = 2 // 绘制下段轴(0-40) ctx.beginPath() ctx.moveTo(50, height - 50) ctx.lineTo(50, height/2 + 20) ctx.stroke() drawBreak(ctx, 50, height/2 + 10) // 绘制上段轴(60-100) ctx.beginPath() ctx.moveTo(50, height/2 - 20) ctx.lineTo(50, 50) ctx.stroke() drawBreak(ctx, 50, height/2 - 10) // 添加刻度标签 ctx.fillStyle = "black" ctx.font = "12px Arial" ctx.fillText("0", 30, height - 45) ctx.fillText("40", 30, height/2 + 25) ctx.fillText("60", 30, height/2 - 25) ctx.fillText("100", 30, 55) } function drawBreak(ctx, x, y) { ctx.save() ctx.strokeStyle = "#ff4444" ctx.beginPath() ctx.moveTo(x - 8, y - 3) ctx.lineTo(x + 8, y + 3) ctx.moveTo(x - 8, y + 3) ctx.lineTo(x + 8, y - 3) ctx.stroke() ctx.restore() } } ``` ### 关键实现细节说明 1. **坐标系分段**:通过计算将Y轴分为上下两个区段,中间保留断裂间距 2. **视觉标记**:使用交叉斜线作为断裂标识符,增强视觉效果 3. **动态适配**:通过`height/2`等相对坐标实现组件尺寸自适应 4. **标签对齐**:需要根据实际数据范围进行比例换算,示例中使用固定数值简化演示
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

vqt5_qt6

你的鼓励是我们创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值