精华内容
下载资源
问答
  • 龚建波
    2022-02-10 22:49:36

    效果展示:

    代码链接:https://github.com/gongjianbo/MyTestCode/tree/master/Qml/TestQml_20220210_ProgressBar

    环形进度条代码:

    import QtQuick 2.12
    import QtQuick.Controls 2.12
    
    //canvas绘制环形进度条
    //龚建波 2022-02-10
    Item {
        id: control
    
        implicitWidth: 160
        implicitHeight: 160
    
        property real from: 0 //最小值
        property real to: 100 //最大值
        property real value: 0 //当前值
        property real percent: (value-from)/(to-from) //百分比[0,1]
        //
        property int canvasMargin: 3
        property int canvasWidth: width < height ? width : height
        property int canvasRadius: canvasWidth / 2 - canvasMargin
        property int lineWidth: 16
        property int lineBorder: 6
        property color bgColor: "#0486FF"
        property color valueColor: "#0486FF"
        //
        property real _valueAngle: Math.PI/2+Math.PI*2*percent
        on_ValueAngleChanged: canvas.requestPaint()
        //
        property real speed: 0.1
        property real offset: 0
    
        Canvas {
            id: canvas
            width: canvasWidth
            height: canvasWidth
            onPaint: {
                var ctx = getContext("2d");
                ctx.clearRect(0, 0, canvasWidth, canvasWidth);
                ctx.lineCap = "round";
    
                drawBg(ctx);
                drawValue(ctx);
            }
    
            //context.arc(x,y,r,sAngle,eAngle,counterclockwise);
            //x:圆的中心的x坐标。
            //y:圆的中心的y坐标。
            //r:圆的半径。
            //sAngle:起始角,以弧度计。(弧的圆形的三点钟位置是0度)。
            //eAngle:结束角,以弧度计。
            //counterclockwise:可选参数。False=顺时针,true=逆时针。
            function drawBg(ctx)
            {
                var color_offset = offset>Math.PI?(1-(offset-Math.PI)/Math.PI):(offset/Math.PI);
    
                ctx.beginPath();
                ctx.arc(canvasWidth/2, canvasWidth/2, canvasRadius-lineWidth/2-lineBorder, 0, 2*Math.PI);
                ctx.lineWidth = lineWidth+lineBorder*2;
                ctx.strokeStyle = Qt.lighter(bgColor, 1.7+0.2*color_offset);
                ctx.stroke();
    
                ctx.beginPath();
                ctx.arc(canvasWidth/2, canvasWidth/2, canvasRadius-lineWidth/2-lineBorder, 0, 2*Math.PI);
                ctx.lineWidth = lineWidth;
                ctx.strokeStyle = Qt.lighter(bgColor, 1.6+0.2*color_offset);
                ctx.stroke();
            }
    
            function drawValue(ctx)
            {
                //ctx.save();
                //带阴影效果时cpu占用明显增高
                //ctx.shadowColor= valueColor;
                //ctx.shadowBlur= lineWidth/4;
                ctx.beginPath();
                ctx.arc(canvasWidth/2, canvasWidth/2, canvasRadius-lineWidth/2-lineBorder, Math.PI/2, _valueAngle, false);
                ctx.lineWidth = lineWidth;
                ctx.strokeStyle = valueColor;
                ctx.stroke();
                //ctx.restore();
            }
    
            Text {
                id: txt
                anchors.centerIn: parent
                font.family: "Microsoft YaHei"
                font.pixelSize: (canvasWidth-lineWidth)/6;
                font.bold: true
                color: valueColor
                text: qsTr("%1 %").arg(control.percent.toFixed(2)*100);
            }
        }
    
        //用定时器刷新
        Timer {
            running: visible
            repeat: true
            interval: 35
            onTriggered:{
                //波浪移动
                offset += speed;
                offset %= Math.PI*2;
                canvas.requestPaint();
            }
        }
        /*NumberAnimation {
            target: control
            running: visible
            loops: -1
            property: "offset"
            from: 0
            duration: 2000
            to: Math.PI*2
        }
        onOffsetChanged: canvas.requestPaint();*/
    }
    

    波浪进度球代码:

    import QtQuick 2.12
    import QtQml 2.12
    import QtQuick.Controls 2.12
    
    //canvas绘制波浪进度球
    //龚建波 2022-02-10
    Item {
        id: control
    
        implicitHeight: 160
        implicitWidth: 160
    
        property real from: 0 //最小值
        property real to: 100 //最大值
        property real value: 0 //当前值
        property real percent: (value-from)/(to-from) //百分比[0,1]
        //
        property int canvasMargin: 3
        property int canvasWidth: width < height ? width : height
        //
        property int fontPx: 34
        property string fontFamily: "Arial"
        property color waveColor: "#0486FF"
        property int waveBorder: 6
        property int waveRadius: canvasWidth / 2 - canvasMargin - waveBorder
        //
        property real waveWidth: 0.05   //波浪宽度,数越小越宽
        property real waveHeight: 5     //波浪高度,数越大越高
        property real speed: 0.1        //波浪速度,数越大速度越快
        property real offset: 0         //波浪x偏移量,用于动画
    
        Canvas {
            id: canvas
            width: canvasWidth
            height: canvasWidth
            onPaint: {
                var ctx = getContext("2d");
                ctx.clearRect(0, 0, canvasWidth, canvasWidth);
                ctx.lineCap = "round";
    
                ctx.save();
                ctx.beginPath();
                ctx.arc(canvasWidth/2, canvasWidth/2, waveRadius+waveBorder-canvasMargin, 0, 2*Math.PI);
                ctx.lineWidth = waveBorder;
                var color_offset = offset>Math.PI?(1-(offset-Math.PI)/Math.PI):(offset/Math.PI);
                ctx.strokeStyle = Qt.lighter(waveColor, 1.5+0.3*color_offset);
                ctx.stroke();
    
                var progress_text = qsTr("%1 %").arg(parseInt(percent*100));
                ctx.font = fontPx + "px '" + fontFamily + "'";
                ctx.textAlign = "center";
                ctx.fillStyle = waveColor;
                //canvas字体高度和居中还有点问题
                ctx.fillText(progress_text, canvasWidth/2, canvasWidth/2+fontPx/2-5);
    
                ctx.save();
                ctx.beginPath();
                ctx.lineWidth = 0;
                ctx.arc(canvasWidth/2, canvasWidth/2, waveRadius, 0, 2*Math.PI);
                ctx.clip();
                drawWave(ctx, waveColor, 0, 0, false);
                ctx.clip();
                ctx.font = fontPx + "px '" + fontFamily + "'";
                ctx.textAlign = "center";
                ctx.fillStyle = "white";
                ctx.fillText(progress_text, canvasWidth/2, canvasWidth/2+fontPx/2-5);
                ctx.restore();
            }
    
            //画笔,颜色,x偏移,y偏移,角度值取反
            function drawWave(ctx, w_color, x_offset, y_offset, reverse)
            {
                ctx.beginPath();
                var x_base = canvasWidth/2-waveRadius;
                var y_base = canvasWidth/2+waveRadius-waveRadius*2*percent;
                //正弦波浪,横坐标步进为5px,右侧多加5是为了填补不足步进的部分
                for(var x_value = 0; x_value <= waveRadius*2 + 5; x_value += 5){
                    var y_value = waveHeight*Math.sin((reverse?-1:1)*(x_value)*waveWidth+offset+x_offset)+y_offset;
                    ctx.lineTo(x_base+x_value, y_base+y_value);
                }
                //左右底部围城实心
                ctx.lineTo(canvasWidth/2+waveRadius, canvasWidth/2+waveRadius);
                ctx.lineTo(canvasWidth/2-waveRadius, canvasWidth/2+waveRadius);
                ctx.closePath();
                ctx.fillStyle = w_color;
                ctx.fill();
            }
        }
    
        //用定时器刷新
        Timer {
            running: visible
            repeat: true
            interval: 35
            onTriggered:{
                //波浪移动
                offset += speed;
                offset %= Math.PI*2;
                canvas.requestPaint();
            }
        }
        /*NumberAnimation {
            target: control
            running: visible
            loops: -1
            property: "offset"
            from: 0
            duration: 2000
            to: Math.PI*2
        }
        onOffsetChanged: canvas.requestPaint();*/
    }
    

    更多相关内容
  • QML与C++交互

    万次阅读 多人点赞 2019-02-27 15:21:09
    import QtQuick 2.9 import QtQuick.Window 2.9 //引入我们注册的模块 import MyCppObject 1.0 Window { id: root visible: true width: 500 height: 300 title: qsTr("QML调用Cpp对象:by 龚建波1992") color:...

    (本文发布于2019-2-27,2020-6-25进行了更新)

    前言

    文档如是说,QML旨在通过C ++代码轻松扩展。Qt QML模块中的类使QML对象能够从C ++加载和操作,QML引擎与Qt元对象系统集成的本质使得C ++功能可以直接从QML调用。这允许开发混合应用程序,这些应用程序是通过混合使用QML,JavaScript和C ++代码实现的。

    QML is designed to be easily extensible through C++ code. The classes in the Qt QML module enable QML objects to be loaded and manipulated from C++, and the nature of QML engine's integration with Qt's meta object system enables C++ functionality to be invoked directly from QML. This allows the development of hybrid applications which are implemented with a mixture of QML, JavaScript and C++ code.

    除了从QML访问C ++功能的能力之外,Qt QML模块还提供了从C ++代码执行反向和操作QML对象的方法。

    下面会通过示例来讲解QML与C++的交互是如何实现的(内容有点长)。

    第一个例子:QML中创建C++对象

    文档如是说,使用C ++代码中定义的功能可以轻松扩展QML。由于QML引擎与Qt元对象系统的紧密集成,可以从QML代码访问由QObject派生的类适当公开的任何功能。这使得C ++类的属性和方法可以直接从QML访问,通常很少或无需修改。

    QML引擎能够通过元对象系统内省QObject实例。这意味着,任何QML代码都可以访问QObject派生类实例的以下成员:

    • 属性(使用Q_PROPERTY注册的属性)
    • 方法(需注册为public slots或是标记为Q_INVOKABLE)
    • 信号

    (此外,如果已使用Q_ENUMS声明枚举,则可以使用枚举。)

    通常,无论是否已向QML类型系统注册了QObject派生类,都可以从QML访问它们。但是,如果QML引擎要访问其他类型信息(例如,如果要将类本身用作方法参数或属性,或者要将其中一个枚举类型用于以这种方式使用),那么该类可能需要注册。

    代码示例有四个文件,QtQuick Empty工程的两个加自定义的Cpp类h和cpp文件,因为我把几种常用的方法都写出来了,所以看起来有点乱(完整代码链接见文末)。

    #ifndef CPPOBJECT_H
    #define CPPOBJECT_H
    
    #include <QObject>
    
    //派生自QObject
    //使用qmlRegisterType注册到QML中
    class CppObject : public QObject
    {
        Q_OBJECT
        //注册属性,使之可以在QML中访问--具体语法百度Q_PROPERTY
        Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
        Q_PROPERTY(int year READ getYear WRITE setYear NOTIFY yearChanged)
    
    public:
        explicit CppObject(QObject *parent = nullptr);
        //通过Q_INVOKABLE宏标记的public函数可以在QML中访问
        Q_INVOKABLE void sendSignal();//功能为发送信号
    
        //给类属性添加访问方法--myName
        void setName(const QString &name);
        QString getName() const;
        //给类属性添加访问方法--myYear
        void setYear(int year);
        int getYear() const;
    
    signals:
        //信号可以在QML中访问
        void cppSignalA();//一个无参信号
        void cppSignalB(const QString &str,int value);//一个带参数信号
        void nameChanged(const QString name);
        void yearChanged(int year);
    
    public slots:
        //public槽函数可以在QML中访问
        void cppSlotA();//一个无参槽函数
        void cppSlotB(const QString &str,int value);//一个带参数槽函数
    
    private:
        //类的属性
        QString myName;
        int myYear;
    };
    
    #endif // CPPOBJECT_H
    

    在头文件中,我定义了信号和public槽函数,以及Q_INVOKABLE宏标记的public函数,还通过Q_PROPERTY注册了两个属性,这些方法和属性之后都可以在QML中进行访问。

    #include "CppObject.h"
    
    #include <QDebug>
    
    CppObject::CppObject(QObject *parent)
        : QObject(parent),
          myName("none"),
          myYear(0)
    {
    
    }
    
    void CppObject::sendSignal()
    {
        //测试用,调用该函数后发送信号
        qDebug()<<"CppObject::sendSignal";
        emit cppSignalA();
        emit cppSignalB(myName,myYear);
    }
    
    void CppObject::setName(const QString &name)
    {
        qDebug()<<"CppObject::setName"<<name;
        if(myName!=name){
            qDebug()<<"emit nameChanged";
            myName=name;
            emit nameChanged(name);
        }
    }
    
    QString CppObject::getName() const
    {
        qDebug()<<"CppObject::getName";
        return myName;
    }
    
    void CppObject::setYear(int year)
    {
        qDebug()<<"CppObject::setYear"<<year;
        if(year!=myYear){
            qDebug()<<"emit yearChanged";
            myYear=year;
            emit yearChanged(myYear);
        }
    }
    
    int CppObject::getYear() const
    {
        qDebug()<<"CppObject::getYear";
        return myYear;
    }
    
    void CppObject::cppSlotA()
    {
        qDebug()<<"CppObject::cppSlotA";
    }
    
    void CppObject::cppSlotB(const QString &str, int value)
    {
        qDebug()<<"CppObject::cppSlotB"<<str<<value;
    }
    

    为了测试方便,我给每个函数都加了一个打印语句,当调用sendSignal函数时将会emit两个信号,稍后会在QML中调用该函数。

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    #include "CppObject.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
    
        //qmlRegisterType注册C++类型至QML
        //arg1:import时模块名
        //arg2:主版本号
        //arg3:次版本号
        //arg4:QML类型名
        qmlRegisterType<CppObject>("MyCppObject",1,0,"CppObject");
    
        QQmlApplicationEngine engine;
    
        //也可以注册为qml全局对象
        //engine.rootContext()->setContextProperty("cppObj",new CppObject(qApp));
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
    
        return app.exec();
    }
    

    通过使用qmlRegisterType,将刚才定义的QObject派生类注册到QML中(Qt5.15增加了新的注册方式)。

    import QtQuick 2.9
    import QtQuick.Window 2.9
    //引入我们注册的模块
    import MyCppObject 1.0
    
    Window {
        id: root
        visible: true
        width: 500
        height: 300
        title: qsTr("QML调用Cpp对象:by 龚建波1992")
        color:"green"
    
        signal qmlSignalA
        signal qmlSignalB(string str,int value)
    
        //鼠标点击区域
        MouseArea{
            anchors.fill: parent
            acceptedButtons: Qt.LeftButton | Qt.RightButton
            //测试时点击左键或右键
            onClicked: {
                if(mouse.button===Qt.LeftButton){
                    console.log('----qml 点击左键:Cpp发射信号')
                    cpp_obj.name="gongjianbo"  //修改属性会触发set函数,获取值会触发get函数
                    cpp_obj.year=1992
                    cpp_obj.sendSignal() //调用Q_INVOKABLE宏标记的函数
                }else{
                    console.log('----qml 点击右键:QML发射信号')
                    root.qmlSignalA()
                    root.qmlSignalB('gongjianbo',1992)
                }
            }
        }
    
        //作为一个QML对象
        CppObject{
            id:cpp_obj
            //也可以像原生QML对象一样操作,增加属性之类的
            property int counts: 0
    
            onYearChanged: {
                counts++
                console.log('qml onYearChanged',counts)
            }
            onCountsChanged: {
                console.log('qml onCountsChanged',counts)
            }
        }
    
        //组件加载完成执行
        Component.onCompleted: {
            //关联信号与信号处理函数的方式同QML中的类型
            //Cpp对象的信号关联到Qml
            //cpp_obj.onCppSignalA.connect(function(){console.log('qml signalA process')})
            cpp_obj.onCppSignalA.connect(()=>console.log('qml signalA process')) //js的lambda
            cpp_obj.onCppSignalB.connect(processB)
            //Qml对象的信号关联到Cpp
            root.onQmlSignalA.connect(cpp_obj.cppSlotA)
            root.onQmlSignalB.connect(cpp_obj.cppSlotB)
        }
    
        //定义的函数可以作为槽函数
        function processB(str,value){
            console.log('qml function processB',str,value)
        }
    }
    

    注册之后就能直接在QML中使用刚才定义的C++类型了,并且可以像QML定义的类型一样进行操作,如信号槽关联、属性绑定等。

    这个示例很简单,点击鼠标左键调用CppObj的sendSignal函数来发送信号,QML处理;点击鼠标右键QML发送信号,CppObj处理,下面是操作结果:

    可以看到QML成功的访问了CppObj的属性和方法,并能进行信号槽的关联。

    第二个例子:C++中加载QML对象

    文档如是说,所有QML对象类型都是源自QObject类型,无论它们是由引擎内部实现还是第三方定义。这意味着QML引擎可以使用Qt元对象系统动态实例化任何QML对象类型并检查创建的对象。

    这对于从C ++代码创建QML对象非常有用,无论是显示可以直观呈现的QML对象,还是将非可视QML对象数据集成到C ++应用程序中。一旦创建了QML对象,就可以从C ++中检查它,以便读取和写入属性,调用方法和接收信号通知。

    可以使用QQmlComponentQQuickView来加载QML文档。QQmlComponent将QML文档作为为一个C++对象加载,然后可以从C++ 代码进行修改。QQuickView也可以这样做,但由于QQuickView是一个基于QWindow的派生类,加载的对象也将可视化显示,QQuickView通常用于将一个可视化的QML对象集成到应用程序的用户界面中。参见文档Qt/Qt5.9.7/Docs/Qt-5.9.7/qtqml/qtqml-cppintegration-interactqmlfromcpp.html

    下面通过代码来演示(完整代码链接见文末)。

    import QtQuick 2.9
    
    Item{
        id: root
        width: 250
        height: 250
        //自定义属性  --cpp可以访问
        property string msg: "GongJianBo1992"
        //自定义信号  --可以触发cpp槽函数
        signal qmlSendMsg(string arg1,string arg2)
    
        Rectangle {
            anchors.fill: parent
            color: "green"
            objectName: "rect" //用于cpp查找对象
        }
    
        MouseArea {
            anchors.fill: parent
            onClicked: {
                console.log("qml 点击鼠标, 发送信号 qmlSendMsg")
                root.qmlSendMsg(root.msg,"myarg2")
            }
        }
    
        onHeightChanged: console.log("qml height changed")
        onWidthChanged: console.log("qml width changed")
    
        //QML中的方法可以被cpp调用,也可以作为槽函数
        function qml_method(val_arg){
            console.log("qml method runing",val_arg,"return ok")
            return "ok"
        }
        //注意槽函数参数为var类型
        function qmlRecvMsg(arg1,arg2){
            console.log("qml slot runing",arg1,arg2)
        }
    }
    

    在QML中我定义了一些属性和方法等,用于测试。

    #ifndef CPPOBJECT_H
    #define CPPOBJECT_H
    
    #include <QObject>
    #include <QDebug>
    
    class CppObject : public QObject
    {
        Q_OBJECT
    public:
        explicit CppObject(QObject *parent = Q_NULLPTR)
            :QObject(parent){}
    
    signals:
        //信号 --用来触发qml的函数
        //注意参数为var类型,对应qml中js函数的参数类型
        void cppSendMsg(const QVariant &arg1,const QVariant &arg2);
    
    public slots:
        //槽函数 --用来接收qml的信号
        void cppRecvMsg(const QString &arg1,const QString &arg2){
            qDebug()<<"CppObject::cppRecvMsg"<<arg1<<arg2;
            qDebug()<<"emit cppSendMsg";
            emit cppSendMsg(arg1,arg2);
        }
    };
    
    #endif // CPPOBJECT_H
    

    Cpp中定义了一个槽函数,用来接收QML对象的信号。 

    #include <QGuiApplication>
    #include <QQmlProperty>
    #include <QQuickView>
    #include <QQuickItem>
    #include <QMetaObject>
    #include <QDebug>
    
    #include "CppObject.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
    
        //可以用QQmlComponent\QQuickView\QQuickWidget的C++代码加载QML文档
        //QQuickView不能用Window做根元素
        QQuickView view(QUrl("qrc:/main.qml"));
        view.show();
    
        //获取到qml根对象的指针
        QObject *qmlObj=view.rootObject();
    
        /*文档如是说:
        应该始终使用QObject::setProperty()、QQmlProperty
        或QMetaProperty::write()来改变QML的属性值,以确保QML引擎感知属性的变化。*/
    
        //【1】
        //通过QObject设置属性值
        qDebug()<<"Cpp set qml property height";
        //qmlObj->setProperty("height",300);
        QQmlProperty(qmlObj,"height").write(300);
        //通过QObject获取属性值
        qDebug()<<"Cpp get qml property height"<<qmlObj->property("height");
        //任何属性都可以通过C++访问
        qDebug()<<"Cpp get qml property msg"<<qmlObj->property("msg");
    
        //【2】
        QQuickItem *item=qobject_cast<QQuickItem*>(qmlObj);
        //通过QQuickItem设置属性值
        qDebug()<<"Cpp set qml property width";
        item->setWidth(300);
        //通过QQuickItem获取属性值
        qDebug()<<"Cpp get qml property width"<<item->width();
    
        //【3】
        //通过objectName访问加载的QML对象
        //QObject::findChildren()可用于查找具有匹配objectName属性的子项
        QObject *qmlRect=qmlObj->findChild<QObject*>("rect");
        if(qmlRect){
            qDebug()<<"Cpp get rect color"<<qmlRect->property("color");
        }
    
        //【4】
        //调用QML方法
        QVariant val_return;  //返回值
        QVariant val_arg="GongJianBo";  //参数值
        //Q_RETURN_ARG()和Q_Arg()参数必须制定为QVariant类型
        QMetaObject::invokeMethod(qmlObj,
                                  "qml_method",
                                  Q_RETURN_ARG(QVariant,val_return),
                                  Q_ARG(QVariant,val_arg));
        qDebug()<<"QMetaObject::invokeMethod result"<<val_return; //qml函数中返回“ok”
    
        //【5】
        //关联信号槽
        CppObject cppObj;
        //关联qml信号与cpp槽
        //如果信号参数为QML对象类型,信号用var参数类型,槽用QVariant类型接收
        QObject::connect(qmlObj,SIGNAL(qmlSendMsg(QString,QString)),
                         &cppObj,SLOT(cppRecvMsg(QString,QString)));
        //关联cpp信号与qml槽
        //qml中js函数参数为var类型,信号也用QVariant类型
        QObject::connect(&cppObj,SIGNAL(cppSendMsg(QVariant,QVariant)),
                         qmlObj,SLOT(qmlRecvMsg(QVariant,QVariant)));
        //此外,cpp信号也可以关联qml信号
    
        return app.exec();
    }
    

    然后就把文档中的东西测试了下,操作起来很简单。不过相对于QML中使用C++对象来说,感觉作用没那么大,因为一般把QML嵌入到Widgets中才会做这些操作,但是混合两个框架很多坑。下面是我的测试输出结果:

     以上两种方式应该就是最简单的QML与C++交互应用了,对照文档或是博客敲一遍代码可以很容易地理解。

    (完结)

    代码完整链接(GitHub)如下:

    Qml中创建Cpp对象(Cpp注册给Qml):https://github.com/gongjianbo/MyTestCode/tree/master/Qml/QmlCallCpp2020

    Cpp中加载Qml对象(Cpp操作Qml):https://github.com/gongjianbo/MyTestCode/tree/master/Qml/CppCallQml2020

    代码的CSDN下载链接:https://download.csdn.net/download/gongjianbo1992/12552274

    参考

    文档:Qt/Qt5.9.7/Docs/Qt-5.9.7/qtqml/qtqml-cppintegration-overview.html

    文档:Qt/Qt5.9.7/Docs/Qt-5.9.7/qtqml/qtqml-cppintegration-interactqmlfromcpp.html

    文档:Qt/Qt5.9.7/Docs/Qt-5.9.7/qtqml/qtqml-cppintegration-topic.html

    (注:文档中有很多相关链接,此处忽略)

    博客:https://blog.csdn.net/u011012932/column/info/14318

    博客:https://blog.csdn.net/baidu_33850454/article/details/81907857

    博客:https://blog.csdn.net/baidu_33850454/article/details/81914821

     

    展开全文
  • 实现代码 实现效果图片: 实现代码: //main.qml import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.12 Window { id: root visible: true width: 640 height: 480 title: qsTr("龚建波 ...

    0.前言

    之前用 Qt Widgets 的 QOpenGLWidget 类配合着色器写了下环形进度条(https://blog.csdn.net/gongjianbo1992/article/details/106043023),现在用 QML 的 ShaderEffect 重新写下,用的 Qt 默认的 OpenGL 版本,貌似是 OpenGL ES2.0 。

    ShaderEffect 是 QML 中用来对 Item 做着色器效果的组件,允许直接在 QML 中编写诸如阴影,模糊,着色和页面卷曲之类的效果。传递参数时,只需要将 ShaderEffect 的 property 声明在 shader 中,该组件也预定了一些属性,详情参考文档:https://doc.qt.io/qt-5/qml-qtquick-shadereffect.html   

    (也可以参照 QML Book 进行使用 http://qmlbook.github.io/

    1.实现思路

    1.1 圆环我是通过计算距离圆心的距离来渲染的圆环,( glsl 本来有 distance 内建函数,之前没看到)

    void main() {
        float len = abs(sqrt(thePos.x*thePos.x+thePos.y*thePos.y));
        float alpha = 1.0-smoothstep(0.15,0.20,abs(len-0.75));
        gl_FragColor = vec4(0.4,0.1,0.6,alpha);
    }

    1.2 圆环的抗锯齿我使用的 smoothstep  函数:

    (这里还没有去做斜线部分的抗锯齿)

    1.3 修改值时的动画效果用的 QML 的属性行为(https://blog.csdn.net/gongjianbo1992/article/details/102135779)。

    1.4(2021-6-6补充)参照https://bugreports.qt.io/browse/QTBUG-21550

    ShaderEffect的输出颜色需要乘上透明度,即

    gl_FragColor.rgb *= gl_FragColor.a;

    2.实现代码

    实现效果图片:

    实现代码:

    //main.qml
    import QtQuick 2.12
    import QtQuick.Window 2.12
    import QtQuick.Controls 2.12
    
    Window {
        id: root
        visible: true
        width: 640
        height: 480
        title: qsTr("龚建波 1992")
    
        //进度条
        MyProgressBar{
            id: progress
            width: root.width>root.height?root.height/2:root.width/2
            height: progress.width
            anchors.centerIn: parent
        }
    
        Row{
            x:10
            y:10
            spacing: 10
    
            //数字框
            SpinBox{
                id: spin
                from: 0
                to: 100
                value: 45
                editable: true
            }
            //按钮
            Button{
                id: btn
                text: "set data"
                onClicked: {
                    progress.value=spin.value
                }
            }
        }
        Component.onCompleted: {
            //初始值
            progress.value=spin.value
        }
    }
    
    import QtQuick 2.12
    
    //自定义的环形进度条组件
    //没设置OpenGL版本,可能默认时OpenGL ES2.0吧
    ShaderEffect{
        id: control
    
        property double min: 0
        property double max: 100
        property double value: 0
        // 给着色器的
        property real aValue: (value-min)/(max-min)
        // 修改值后触发动画效果
        Behavior on aValue{
            NumberAnimation{
                duration: 1000
            }
        }
    
        // 上面的文字
        Text{
            anchors.centerIn: parent
            color: "black"
            font.pixelSize: 20
            text: Number(aValue*100).toFixed(2)+" %"
        }
    
        //mat4 qt_Matrix组合的转换矩阵,从根项到此ShaderEffect的矩阵与正交投影的乘积
        //vec4 qt_Vertex顶点位置,左上角顶点的位置为(0,0),右下角顶点的位置为(width,height)
        //vec2 qt_MultiTexCoord0纹理坐标,左上角坐标为(0,0),右下角坐标为(1,1)
        //thePos用来指定位置,标准化设备坐标
        //aSmoothWidth用来指定smoothstep的宽度
        vertexShader: "
                uniform mat4 qt_Matrix;
                attribute vec4 qt_Vertex;
                attribute vec2 qt_MultiTexCoord0;
                varying vec2 thePos;
                varying float aSmoothWidth;
                uniform float width;
    
                void main() {
                    thePos = vec2(qt_MultiTexCoord0.x*2.0-1.0,-qt_MultiTexCoord0.y*2.0+1.0);
                    aSmoothWidth = 3.0/float(width);
    
                    gl_Position = qt_Matrix * qt_Vertex;
                }"
    
        //float qt_Opacity从根项到此ShaderEffect的不透明度的乘积。
        //es2里字面量也得写成和运算变量一样的类型,如浮点数
        //myatan2把角度归一化为[0,1]
        fragmentShader: "
                #define PI 3.14159265
                varying vec2 thePos;
                varying float aSmoothWidth;
                uniform float qt_Opacity;
                uniform float aValue;
    
                float myatan2(float y,float x)
                {
                    float ret_val=0.0;
                    if(x!=0.0){
                        ret_val=atan(y,x);
                        if(ret_val<0.0){
                            ret_val+=2.0*PI;
                        }
                    }
                    return ret_val/(2.0*PI);
                }
    
                void main() {
                    float len = abs(sqrt(pow(thePos.x,2.0)+pow(thePos.y,2.0)));
                    float alpha = 1.0-smoothstep(0.15,0.15+aSmoothWidth,abs(len-0.75));
                    float angle = myatan2(thePos.y,thePos.x);
                    if(angle<aValue){
                        gl_FragColor = vec4(1.0,0.1,(1.0-angle),alpha);
                    }else{
                        gl_FragColor = vec4(0.4,0.1,0.6,alpha);
                    }
                    gl_FragColor.rgb *= gl_FragColor.a;
                }"
    }
    

    (2020-5-14修复bug及改进)

    主要是片段着色器部分,一是之前的圆环顶点 x 为 0 时没进行处理,上下会有竖线;二是圆环色条的抗锯齿现在也处理了下:

        fragmentShader: "
                #define PI 3.14159265
                varying vec2 thePos;
                varying float aSmoothWidth;
                uniform float qt_Opacity;
                uniform float aValue;
    
                float myatan2(float y,float x)
                {
                    float ret_val = 0.0;
                    if(x != 0.0){
                        ret_val = atan(y,x);
                        if(ret_val < 0.0){
                            ret_val += 2.0*PI;
                        }
                    }else{
                        ret_val = y>0.0 ? PI*0.5 : PI*1.5;
                    }
                    return ret_val/(2.0*PI);
                }
    
                void main() {
                    float len = abs(sqrt(pow(thePos.x,2.0)+pow(thePos.y,2.0)));
                    float alpha = 1.0-smoothstep(0.15,0.15+aSmoothWidth,abs(len-0.75));
                    float angle = myatan2(thePos.y,thePos.x);
                    float angle_smooth=1.0-smoothstep(aValue,aValue+aSmoothWidth/3.0,angle);
    
                    if(angle_smooth>0.0){
                    if(angle_smooth>=1.0){
                    gl_FragColor = vec4(1.0,0.1,(1.0-angle),alpha);
                    }else{
                    gl_FragColor = vec4(mix(vec3(1.0,0.1,(1.0-angle)),vec3(0.4,0.1,0.6),1.0-angle_smooth),alpha);
                    }
                    }else{
                    gl_FragColor = vec4(0.4,0.1,0.6,alpha);
                    }
                    gl_FragColor.rgb *= gl_FragColor.a;
                }"

     

    展开全文
  • 绘制圆后对该圆路径clip会有一圈虚线,绘制前后加上save、restore解决了;小球的实现思路比较简单,就是填充一个带正弦路径的区域,然后clip截取出圆球部分。clip对抗锯齿有一点影响,可以计算下水球圆弧的起止角度...

    小球的实现思路比较简单,就是填充一个带正弦路径的区域,然后 clip 截取出圆球部分。

    最终效果图:

    遇到的问题:

    绘制圆后对该圆路径 clip 会有一圈虚线,绘制前后加上 save、restore 解决了;

    阴影等特效很占资源不建议使用;

    clip 对抗锯齿有一点影响,可以计算下水球圆弧的起止角度直接绘制圆弧;

    要对文本差异性着色在有多个波浪时比较麻烦,待完成。

    代码地址:https://github.com/gongjianbo/MyTestCode/tree/master/Qml/TestQml_20210310_Wave

    完整代码:

    import QtQuick 2.12
    import QtQuick.Controls 2.12
    
    //波浪小球
    //参照:https://blog.csdn.net/zhengtianzuo06/article/details/78687838
    //参照:https://blog.csdn.net/lecepin/article/details/53536445
    //参照:https://blog.csdn.net/yyy269954107/article/details/43449087
    Canvas {
        id: control
    
        implicitWidth: 200
        implicitHeight: 200
    
        //数值范围
        property real maxValue: 100
        property real minValue: 0
        property real curValue: 0
    
        //显示进度
        property real curProgress: (curValue-minValue)/(maxValue-minValue)*100
        property string curText: (curProgress<0?0:curProgress).toFixed(1)+" %"
    
        //画布
        //取最短边定义正方形区域进行绘制,一般组件已经是设置为正方形的
        property int canvasWidth: width < height ? width : height
        property int canvasMargin: 5
        property int waveRadius: canvasWidth/2-canvasMargin //水球半径
    
        property int fontPx: 34
        property color textColor: Qt.lighter(waveColor) //文本颜色
        property color waveColor: "#0486FF" //波浪颜色
        //波浪参数
        property real waveWidth: 0.05   //波浪宽度,数越小越宽
        property real waveHeight: 5     //波浪高度,数越大越高
        property real speed: 0.1        //波浪速度,数越大速度越快
        property real offset: 0         //波浪x偏移量,用于动画
    
        onPaint: {
            var ctx = getContext("2d");
            ctx.clearRect(0, 0, canvasWidth, canvasWidth);
            //保存默认设置
            ctx.save();
    
            //【1】截取波浪圆形区域
            //截取圆圈范围进行绘制
            ctx.lineWidth = 1;
            ctx.beginPath();
            ctx.arc(canvasWidth/2, canvasWidth/2, waveRadius, 0, 2*Math.PI);
            ctx.closePath();
            ctx.clip();
    
            //【2】画波浪
            //画波浪,可以导出波浪个数属性
            drawWave(ctx, Qt.darker(waveColor), 1.5, -1, true);
            drawWave(ctx, waveColor, 0, 0, false);
    
            //【3】文本
            ctx.font = fontPx + 'px Arial';
            ctx.textAlign = 'center';
            ctx.fillStyle = textColor;
            ctx.fillText(curText, canvasWidth/2, canvasWidth/2+fontPx/2);
    
            //恢复默认设置,防止污染下一次刷新
            ctx.restore();
    
            //【4】画外圈,restore之后再画,避免被clip
            ctx.lineWidth = 1;
            ctx.strokeStyle = waveColor;
            ctx.beginPath();
            ctx.arc(canvasWidth/2, canvasWidth/2, waveRadius, 0, 2*Math.PI);
            ctx.stroke();
        }
    
        //画笔,颜色,x偏移,y偏移,角度值取反
        function drawWave(ctx,w_color,x_offset,y_offset,reverse=false)
        {
            //sin曲线
            ctx.beginPath();
            var x_base = canvasWidth/2-waveRadius;
            var y_base = canvasWidth/2+waveRadius-waveRadius*2*(curProgress/100);
            //波浪曲线部分,横坐标步进为5px
            for(var x_value = 0; x_value <= waveRadius*2 + 5; x_value += 5){
                var y_value = waveHeight*Math.sin((reverse?-1:1)*(x_value)*waveWidth+offset+x_offset)+y_offset;
                ctx.lineTo(x_base+x_value, y_base+y_value);
            }
            //底部两端围成实心
            ctx.lineTo(canvasWidth/2+waveRadius, canvasWidth/2+waveRadius);
            ctx.lineTo(canvasWidth/2-waveRadius, canvasWidth/2+waveRadius);
            ctx.closePath();
            ctx.fillStyle = w_color;
            ctx.fill();
        }
    
        //用定时器刷新,动画的刷新率更高CPU占用率也高了
        Timer {
            running: visible
            repeat: true
            interval: 30
            onTriggered:{
                //波浪移动
                offset += speed;
                offset %= Math.PI*2;
                control.requestPaint();
            }
        }
    }
    
    import QtQuick 2.12
    import QtQuick.Window 2.12
    import QtQuick.Controls 2.12
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Qml Wave")
    
        Row {
            spacing: 20
    
            //勾选递增后定时器增加数值
            CheckBox {
                id: auto_increment
                text: "递增"
                checked: true
            }
        }
    
        CanvasWave {
            id: wave
            anchors.centerIn: parent
            //waveColor: "darkCyan"
    
            //用来测试边界
            Rectangle {
                anchors.fill: parent
                color: "transparent"
                border.color: "black"
            }
        }
    
        Timer {
            id: timer
            running: auto_increment.checked
            repeat: true
            interval: 30
            onTriggered:{
                //测试用逻辑,递增进度值
                if(wave.curValue <= wave.maxValue){
                    wave.curValue += 0.5;
                }
    
                if(wave.curValue > wave.maxValue){
                    wave.curValue = wave.minValue;
                }
            }
        }
    }
    

    展开全文
  • 《QDebug 2020年4月》

    2020-04-10 11:01:32
    一、Qt Widgets 问题交流 1.信号槽使用宏的方式连接,可以访问私有槽函数 宏的方式可以访问 private slots: private slots: void test(); // ...btn,SIGNAL(clicked(bool)),&...connect(u...
  • spdlog 日志库学习,自定义 sink

    千次阅读 2021-01-19 00:13:38
    //龚建波 2021-1-18 //参照rotating_file_sink和daily_file_sink自定义 //(生成的时间也可以修改为任意间隔) //(还有个需求没完成,就是清理旧文件,按照个数或者时间,即max_files参数目前相关逻辑是没用的) ...
  • 1.准备服务端测试代码 只需要上传和下载两个接口,实际应用时可能还需要 token 验证之类的,而且也没有对文件传输结果进行校验。 #using flask 2.0.1 import os,sys from flask import Flask,request,jsonify,...
  • (本文是LearnOpenGL的学习笔记,教程中文翻译地址https://learnopengl-cn.github.io/(备用地址https://learnopengl-cn.readthedocs.io/zh/latest/),写于 2022-04-04) 0.前言 在之前的图形绘制操作中,都是将...
  • 1. 实现的效果 没有使用外部导入时, 外部功能数据库如下: id name rule notes 1 外部规则1 ( 3 * x + 2 * y ) / 5 加权平均 2 外部规则2 sqrt( x * x + y * y ) ...void Widget::on_pushButton_2_cl...
  • 转载请务必注明出处及原始链接,谢谢! 要在QComboBox下拉列表项中添加复选框,并进行消息处理,在网上搜索了很久没有找到太多有用的信息和实际的例子, 但从中还是找到了一些提示性的资料,根据这些简短的介绍,...
  • * @brief 一个简易的无边框辅助类 * @author 龚建波 * @date 2020-11-15 * @details * 之前看有网友用的Window+本地事件来做的 * Qt5 QML 中的 Window类型 * 可能是QQuickWindow或者QQuickWindowQmlImpl(子类),后...
  • control.requestPage(page_num,control.__itemPerPage) } } import QtQuick 2.12 import QtQuick.Controls 2.12 //分页组件上的页码按钮 PageButton //龚建波 2020-12-20 Button { id: control //该按钮代表的页码 ...
  • /** * @brief 使用QLineEdit+QPushButton+弹出框组合的下拉框 * @author 龚建波 * @date 2020-7-6 * @history * [2020-7-7] * 重构弹出框部分,增加可扩展性 * 基础组件:Box+Popup,继承Container实现接口后设置给...
  • spdlog 日志库学习,简易封装

    千次阅读 2021-01-27 22:55:30
    file_sink.h" //#include "spdlog/sinks/daily_file_sink.h" //spdlog 简易封装 //龚建波 2021-1-27 修改 //''' 示例 // Qt使用时 CONFIG += utf8_source // INITLOG(QString("日志.log").toLocal8Bit().toStdString...
  • Qt实现桌面右下角弹窗

    千次阅读 2020-06-21 14:57:59
    } /** * @brief 桌面右下角弹框 * @author 龚建波 * @date 2020-6-21 * @note 可以增加show的parent参数,使模态有父子关系 * @details 只有单个实例对象显示弹框, * 后面设置的信息会覆盖之前的。 * 也可以修改为...
  • /** * @brief 管理音频上下文,也可用来获取音频格式等信息 * @author 龚建波 * @date 2020-11-20 * @details * 去掉了拷贝和赋值,需要作为参数传递时使用智能指针管理 * (为什么用 NULL 不用 nullptr,为了和 C ...
  • 使用 OpacityMask 遮罩 效果图: import QtQuick 2.12 import QtQuick.Window 2.12 import QtGraphicalEffects 1.0 Window { width: 640 height: 480 visible: true title: qsTr("龚建波 1992") Rectangle{ anchors...
  • /** * @brief 用于读写数据时进行回调 * QAudioInput/Output需要QIODevice类来读写数据 * @author 龚建波 * @date 2021-12-10 * @history * 2021-12-29 修复播放游标的偏移问题,修复播放reset问题 */ class ...
  • title: qsTr("龚建波 1992") color: "#021B39" Column{ anchors.centerIn: parent spacing: 10 CircleView{ width: 100 height: 50 model:["red","green","blue","orange"] delegate: Rectangle { ...
  • true width: 640 height: 480 title: qsTr("龚建波 1992") header: TabBar { id: bar width: parent.width TabButton { text: qsTr("Hollow") } TabButton { text: qsTr("Solid") } TabButton { text: qsTr("Circle...
  • QML Canvas绘制一个简单的仪表盘

    千次阅读 2020-02-23 00:44:54
    title: qsTr("龚建波 1992") Column{ anchors.centerIn: parent spacing: 20 MyDial{ id: dial width: root.width-40 height: width/2 //测试用,查看canvas范围 Rectangle{ anchors.fill: parent ...
  • loading 效果 //龚建波 2021-1-17 Item { id: control //item圆圈个数 property int itemCount: 10 //item圆圈直径大小 property int itemSize: 16 //item变大范围 property int itemExpand: 10 //item圆圈颜色 ...
  • https://github.com/gongjianbo/MyTestCode/tree/master/Qt/TestQt_20210425_ini 实现代码: #pragma once #include #include #include namespace Tool { /** * @brief 简易的ini读写类 * @author 龚建波 ...
  • -_rotate : 360 moveToStart: true } } } } import QtQuick 2.12 import QtQuick.Controls 2.12 //自定义loading效果 //龚建波 2022-02-08 Item { id: control //item的颜色 property color itemColor: "#0486FF" /...
  • https://github.com/gongjianbo/EasyOpenGL2D) 实现效果(GIF): 主要实现代码: #ifndef CIRCLEPROGRESSBAR_H #define CIRCLEPROGRESSBAR_H #include #include #include #include #include #include //龚建波:...
  • https://doc.qt.io/qt-5.12/qtlocation-index.html Window { visible: true width: 640 height: 480 title: qsTr("龚建波 1992") Slider{ id: bearing_slider height: 20 width: parent.width from:...
  • outer.next if __name__ == "__main__": print("龚建波 1992") mylist = LinkedList() print("尾部增删") mylist.pushBack(11) mylist.pushBack(22) mylist.pushBack(33) print(len(mylist)) print(str(mylist)) ...
  • 0.前言 QML中QtQuick.Controls 1里有Calendar这个控件,官方的文档以及示例都很简单,网上很多人也是自己重新实现的,我比较懒,照着QtQuick.Controls.Styles里的源码改了下。 1.展示 效果如图: ...
  • 实现效果(GIF): 主要实现代码: #ifndef WAVEPROGRESSBAR_H #define WAVEPROGRESSBAR_H #include #include #include #include #include #include #include //龚建波:进度球 class WaveProgressBar : public ...
  • QML自定义环形菜单/环形选择框

    千次阅读 2022-03-13 20:45:33
    radiusY: pop.spread moveToStart: true startAngle: 0 sweepAngle: pop.angle } } } } } import QtQuick 2.12 import QtQuick.Controls 2.12 //环形菜单 //龚建波 2022-03-13 //note:弹框为pop会被限制在window内...

空空如也

空空如也

1 2 3 4 5 ... 19
收藏数 361
精华内容 144
关键字:

龚建波