Qt 之 QWidget嵌入Qml文件以及如何交互

项目场景:

工作中,有的老版本代码是用QtWidgets来实现的,在后期的OTA实现中,由于种种原因,比如用qml来实现更方便等原因,某些功能的开发确用qml语言来实现,这就涉及到了QWidget与Qml文件的相互嵌入,以及交互问题。


问题描述:

笔者遇到的需求是,做一个语音助手,窗口会实时显示自己说的话,以及一些对话内容,比如听故事界面,要实现一种无边框,圆角的对话框:
Qt 之 QWidget嵌入Qml文件以及如何交互_第1张图片

按照常规做法,因为代码是QtWidget框架写的,所以我应该也用QWidgets来实现就好了,但是考虑到也写效果的实现和最近一直在用qml开发,就尝试用qml语言来实现了。

加载qml文件,有几种方式:

第一种 QQmlApplicationEngine

QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

其中main.qml 可以以Window作为根元素,这个时候QML就完全拥有了控制权,可以直接设置窗体的标题、尺寸等信息;

第二种 QQuickView

QQuickView最常见的用法如下:

QQuickView view;
view.setResizeMode (QQuickView::SizeRootObjectToView);
view.setSource (QUrl("qrc:/main.qml"));
view.show ();

注意:其中qml文件不能以Window作为根元素,最佳选择是使用Item为根元素,否则会警告:

QQuickView does not support using windows as a root item. 
If you wish to create your root window from QML, consider using QQmlApplicationEngine instead.

第三种 QQuickWidget

官网自带的说明例子.一般用来在QWidget界面上加载QML界面

	QQuickWidget *view = new QQuickWidget;
    view->setSource(QUrl::fromLocalFile("myqmlfile.qml"));
    view->show();

代码示例以及解析:

最主要的是qml的嵌入以及与QWidget的交互
1 通过信号槽的方式创建连接
2 通过QMetaObject::invokeMethod(方法调用

这里直接把代码贴在这里,供大家参考,效果如下
Qt 之 QWidget嵌入Qml文件以及如何交互_第2张图片


代码结构如下:
Qt 之 QWidget嵌入Qml文件以及如何交互_第3张图片

main.cpp

#include 
#include "dialog.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Dialog w;
    w.show();
    return a.exec();
}

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include 
QT_BEGIN_NAMESPACE
namespace Ui { class Dialog; }
QT_END_NAMESPACE

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = nullptr);
    ~Dialog();

    void setTitle(const QString &title);
    void setTotal(const long v);
    void setCurValue(const long v);
signals:
    void sigPalyProgress(qreal v);
public slots:
    void onSliderMoved(qreal v);
    void onPlay();
    void onPause();
private:
    Ui::Dialog *ui;
};
#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"
#include
#include
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::Dialog)
{
    setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
    setAttribute(Qt::WA_TranslucentBackground);

    ui->setupUi(this);
    QUrl source("qrc:/listen.qml");
    ui->quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView );
    ui->quickWidget->setSource(source);
    ui->quickWidget->setClearColor(QColor(Qt::transparent));
    QQuickItem *item=ui->quickWidget->rootObject();
    connect(item,SIGNAL(sliderMoved(qreal)),this,SLOT(onSliderMoved(qreal)));
    connect(item,SIGNAL(playClicked()),this,SLOT(onPlay()));
    connect(item,SIGNAL(pauseClicked()),this,SLOT(onPause()));
    connect(this,SIGNAL(sigPalyProgress(qreal)),item,SIGNAL(updatePlayProgress(qreal)));
    if(item)
        QMetaObject::invokeMethod(item,"setPlayState");

    int inum=0;
    QTimer *p = new QTimer;
    p->setInterval(10);
    connect(p,&QTimer::timeout,[=]()mutable{
        inum+=10;
        sigPalyProgress(inum);
    });
    p->start();
}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::setTitle(const QString &title)
{
    QQuickItem *item=ui->quickWidget->rootObject();
    if(item)
        QMetaObject::invokeMethod(item,"setTitle",Q_ARG(QVariant,title));

}

void Dialog::setTotal(const long v)
{
    QQuickItem *item=ui->quickWidget->rootObject();
    if(item)
        QMetaObject::invokeMethod(item,"setTotalTime",Q_ARG(long,v));
}

void Dialog::setCurValue(const long v)
{
    QQuickItem *item=ui->quickWidget->rootObject();
    if(item)
        QMetaObject::invokeMethod(item,"setCurTime",Q_ARG(long,v));
}

void Dialog::onSliderMoved(qreal v)
{
    qDebug() << "onslidermoved:" << v;
}

void Dialog::onPlay()
{
    qDebug() << "onPlay";
}

void Dialog::onPause()
{
    qDebug() << "onPause";
}

listen.qml

import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.3
import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.3

Item {
    id:control
    visible: true
    width: 736
    height: 290
    //    color: "#00000000"
    //    modality: Qt.WindowModal
    //    flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowSystemMenuHint

    signal sliderMoved(real v);
    signal playClicked();
    signal pauseClicked();
    signal updatePlayProgress(real v);
    property string  title: qsTr("《一起来听红楼梦》")
    property var totalTimeValue: 20000
    property var curTimeValue : 100

    function setPlayState()
    {
        playbtn.source ="qrc:/pause96.png";
    }

    function setTitle(str)
    {
        title = str;
    }

    function setTotalTime(v)
    {
        totalTimeValue = v;
    }

    function setCurTime(v)
    {
        curTimeValue = v;
    }

    function currentTimeMMSS(time)
    {
        var sec= Math.floor(time/1000);
        var hours=Math.floor(sec/3600);
        var minutes=Math.floor((sec-hours*3600)/60);
        var seconds=sec-hours*3600-minutes*60;
        var hh,mm,ss;
        if(hours.toString().length<2)
            hh="0"+hours.toString();
        else
            hh=hours.toString();
        if(minutes.toString().length<2)
            mm="0"+minutes.toString();
        else
            mm=minutes.toString();
        if(seconds.toString().length<2)
            ss="0"+seconds.toString();
        else
            ss=seconds.toString();

        if(hh !== "00")
        {
            return hh + ":" + mm+":"+ss;
        }
        else
        {
            return mm+":"+ss;
        }

    }

    Rectangle {
        id:root
        anchors.fill: parent
        color: "#101A24"
        radius: 40
        clip:true


        Text {
            id: name
            text: title
            anchors.verticalCenter: playbtn.verticalCenter
            anchors.left: parent.left
            anchors.leftMargin: 48
            width: 520
            elide: Text.ElideRight

            font.pixelSize: 52
            font.family: "PingFang SC"
            font.weight: Font.Medium;
            color: "#FFFFFF";
            //lineHeight: 64
        }

        ParallelAnimation{ //点击动画
            id: clickedAnimation
            PropertyAnimation{
                target: playbtn
                property: "opacity"
                to: 0.5
                //from: 0
                duration: 300
            }
            PropertyAnimation{
                target: playbtn
                property: "scale"
                to: 1.3
                //from: 1
                duration: 300
            }
        }

        ParallelAnimation{ //取消点击动画
            id: releaseAnimation
            PropertyAnimation{
                target: playbtn
                property: "opacity"
                //from: 0.5
                to: 1
                duration: 300
            }
            PropertyAnimation{
                target: playbtn
                property: "scale"
                //from: 1.3
                to: 1
                duration: 300
            }
        }

        Image {
            id:playbtn
            source: "qrc:/play96.png"
            anchors.right: parent.right
            anchors.rightMargin: 48
            anchors.top: parent.top
            anchors.topMargin: 48
            width:96
            height: 96

            MouseArea
            {
                id:mouse
                anchors.fill: parent
                anchors.margins: -16

                onClicked:
                {
                    console.log("clicked")
                    if(playbtn.source == "qrc:/pause96.png")
                    {
                        playbtn.source = "qrc:/play96.png";
                        pauseClicked();

                    }
                    else
                    {
                        playbtn.source = "qrc:/pause96.png"
                        playClicked();
                    }
                }

                onPressed:
                {
                    clickedAnimation.start();
                }

                onReleased: {
                    releaseAnimation.start()
                }
            }
        }


        Slider{

            id: slider
            anchors.left: parent.left
            anchors.leftMargin: 48
            anchors.bottom: curTime.top
            anchors.bottomMargin: 16

            implicitWidth: 640;
            implicitHeight: 16;
            orientation:Qt.Horizontal
            padding: 0

            from: 0
            value: curTimeValue
            to:totalTimeValue
            stepSize: 1

            onMoved: {
                sliderMoved(slider.value)
            }

            background: Rectangle {
                id:bg
                implicitWidth: slider.implicitWidth
                implicitHeight: slider.implicitHeight
                color: "#2D4247"
                radius: 16
                layer.enabled: true
                layer.effect: OpacityMask {
                    maskSource: Rectangle {
                        width: bg.width
                        height: bg.height
                        radius: bg.radius
                    }
                }

                Rectangle {
                    y: slider.horizontal ? 0 : slider.visualPosition * parent.height
                    width: slider.horizontal ? slider.position * parent.width : parent.width
                    height: slider.horizontal ? parent.height : slider.position * parent.height
                    //                    color: "#12CFA7"
                    opacity: 1
                    radius:16
                    LinearGradient {            ///--[Mark]
                        anchors.fill: parent
                        start: Qt.point(0, 0)
                        end: Qt.point(parent.width, 0)      ///1.横向渐变
                        //end: Qt.point(0, height)     ///2.竖向渐变
                        //end: Qt.point(width, height) ///3.斜向渐变
                        gradient: Gradient {
                            GradientStop {  position: 0.0;    color: "#618CFF" }
                            GradientStop {  position: 1.0;    color: "#FF7A6A" }
                        }
                    }
                }
            }

            handle: Item {
                x: slider.leftPadding + (slider.horizontal ? slider.visualPosition * (slider.availableWidth - width) : (slider.availableWidth - width) / 2)
                y: slider.topPadding + (slider.horizontal ? (slider.availableHeight - height) / 2 : slider.visualPosition * (slider.availableHeight - height))
                implicitWidth: 28
                implicitHeight: 28
                Rectangle{
                    id:rect_out
                    anchors.fill: parent
                    radius: width/2
                    color:"#ffffff"
                }

            }
        }


        Text {
            id: curTime
            text: currentTimeMMSS(curTimeValue)
            color: "#ffffff"
            font.pixelSize: 28
            anchors.left: parent.left
            anchors.leftMargin: 48
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 48
        }


        Text {
            id: totalTime
            text: currentTimeMMSS(totalTimeValue)
            color: "#ffffff"
            font.pixelSize: 28
            anchors.right: parent.right
            anchors.rightMargin: 48
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 48
        }
    }

    Component.onCompleted: {
        control.updatePlayProgress.connect(control.setCurTime)
    }

    Component.onDestruction:{
        control.updatePlayProgress.disconnect(control.setCurTime)
    }
}

你可能感兴趣的:(《Qt,项目实战经历全记录》,qt,qml,嵌入调用)