0


QT c++和qml交互实例

文章目录

一、demo效果图

该实例,主要是在已有的QWidget工程中,加入qml工程,方便qml项目的开发与维护,让qml开发者更好的上手qml。

(1)展示了c++和qml常见的交互方式。
(2)qwidget工程如何加载qml工程,如何加载自己实现的qml tool库。
(3)创建无边框qml界面,支持拖拽,窗口的缩小与展开,界面的分段实现。
(4)展示一个简单的堆栈窗口(SwipeView),相当于QStackedWidget,管理多个子窗口页面。
(5)实现一个简单的列表框(ListView),相当于QListWidget,定时请求网络数据,展示学生信息,将view和model进行分离,降低界面与数据的耦合。
(6)点击学号、年龄,实现了列表数据的排序。

在这里插入图片描述

二、c++和qml交互的基本方式

交互有很多种方式,有的比较繁杂,不易理解,此处只使用了最方便使用的方法,满足基本的交互。

1、qml访问C++类对象

需要先将C++类(MyWindow)注册到QQuickView中。

和qml相关的C++类,最好都进行注册。
classMainQuickView:publicQQuickView{
    Q_OBJECT
public:MainQuickView(QQuickView *parent =nullptr);~MainQuickView()override;voidinitialzeUI();protected:};voidMainQuickView::initialzeUI(){// 注册,为了qml中可以直接访问C++对象
    MyWindow *w =newMyWindow();this->rootContext()->setContextProperty("myWindow", w);...}

这样C++的

信号

public槽函数

Q_INVOKABLE 修饰的类成员函数
classMyWindow:publicQWidget{
      Q_OBJECT
  public:MyWindow();// Q_INVOKABLE可以将C++函数暴漏给qml引擎,注册到元对象系统
      Q_INVOKABLE voidinvokableMethod();
  signals:voiddataChanged();public slots:voidrefresh();};

就可以在qml中调用

// main.qml
Rectangle {
    id: root
    width:1200
    height:800
    onClicked:{
        myWindow.showNormal();// showNormal是QWidget父类的槽函数}}

三、关键代码

1、工程结构图

在这里插入图片描述

2、c++代码
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent):QWidget(parent),ui(new Ui::MainWindow),m_pQuickVew(nullptr){
    ui->setupUi(this);}MainWindow::~MainWindow(){delete ui;}voidMainWindow::on_pushButton_clicked(){if(!m_pQuickVew){// 加载qml包含两种方式,一种是QQuickView,一种是QQmlApplicationEngine// 当前使用第一种方式,MainQuickView继承自QQuickView
        m_pQuickVew =newMainQuickView();
        m_pQuickVew->setObjectName("quickView");}

    m_pQuickVew->show();}
MainQuickView.cpp
staticconstchar* s_mainPath ="qrc:/qml/main.qml";MainQuickView::MainQuickView(QQuickView *parent):QQuickView(parent),m_bMin(false){this->setFlags(Qt::Window | Qt::FramelessWindowHint);this->setTitle(QString::fromLocal8Bit("图书馆"));initialize();setMoveable();setTitleData();}MainQuickView::~MainQuickView(){}voidMainQuickView::initialize(){// 初始化view 和 model,降低耦合,提高可维护性if(!m_pStudentInfoView)
        m_pStudentInfoView =newStudentInfoView();if(!m_pStudentInfoModel)
        m_pStudentInfoModel = m_pStudentInfoView->getStudentInfoMode();// ...其他功能initialzeUI();}voidMainQuickView::initialzeUI(){// 注册,qml中可以直接访问C++对象this->rootContext()->setContextProperty("mainQuickView",this);// qml可以通过"studentInfoView",去使用其实现的函数this->rootContext()->setContextProperty("studentInfoView", m_pStudentInfoView);this->rootContext()->setContextProperty("studentInfoModel", m_pStudentInfoModel);// qml根对象大小随窗口大小改变而改变this->setResizeMode(QQuickView::SizeRootObjectToView);// 导入自定义模块工具this->engine()->addImportPath(":/qmlTools");// 设置准备加载得qml文件,相当于c++的main函数入口this->setSource(QUrl(s_mainPath));}voidMainQuickView::minMaxQmlWindow(){
    m_bMin =!m_bMin;
    emit minQmlWindow(m_bMin);}voidMainQuickView::onSendTopRectPos(QVariant pX, QVariant pY){this->setX(this->x()+ pX.toInt());this->setY(this->y()+ pY.toInt());}voidMainQuickView::onCloseQuickView(){close();}voidMainQuickView::onShowMinimized(){showMinimized();}voidMainQuickView::setMoveable(){// 当移动qml窗口时,将移动坐标告诉C++// 找到对象名叫topRect的qml,C++绑定qml的信号
    QQuickItem* topRect =this->rootObject()->findChild<QQuickItem*>("topRect");if(topRect){connect(topRect,SIGNAL(sendTopRectPos(QVariant, QVariant)),this,SLOT(onSendTopRectPos(QVariant, QVariant)));}}voidMainQuickView::setTitleData(){// 找到对象名叫topRect的qml,C++向qml发送消息,触发qml中实现的setTitleText函数
    QQuickItem* topRect =this->rootObject()->findChild<QQuickItem*>("topRect");QMetaObject::invokeMethod(topRect,"setTitleText",Q_ARG(QVariant,QString::fromLocal8Bit("Qml窗口标题")));}
StudentInfoView.cpp
StudentInfoView::StudentInfoView(QObject *parent):QObject(parent),m_sortType(StudentInfoSort::sortNone){if(!m_pStudentInfoModel)
        m_pStudentInfoModel =newStudentInfoModel(this);// 如果数据来自网络,就需要定时请求接口,并更新界面
    m_timer =newQTimer(this);
    m_timer->setInterval(10*1000);QObject::connect(m_timer,&QTimer::timeout,this,&StudentInfoView::onUpdateInfoData);onUpdateInfoData();
    m_timer->start();}

StudentInfoModel *StudentInfoView::getStudentInfoMode(){return m_pStudentInfoModel;}// qSort()比较函数不能定义为类成员函数(参数包含隐藏的this指针),会导致参数不符// 可以定义为static类成员函数、static函数、普通函数boolStudentInfoView::idAscend(const StudentInfoItem &stu1,const StudentInfoItem &stu2){return stu1.stuId < stu2.stuId;//学号升序}// 普通函数boolidDescend(const StudentInfoItem &stu1,const StudentInfoItem &stu2){return stu1.stuId > stu2.stuId;//学号降序}boolageAscend(const StudentInfoItem &stu1,const StudentInfoItem &stu2){return stu1.age < stu2.age;//年龄升序}voidStudentInfoView::setSort(StudentInfoSort sortType){
    m_sortType = sortType;if(m_sortType == StudentInfoSort::sortIdAscend){qSort(m_allDatas.begin(), m_allDatas.end(), idAscend);}elseif(m_sortType == StudentInfoSort::sortIdDescend){qSort(m_allDatas.begin(), m_allDatas.end(), idDescend);}elseif(m_sortType == StudentInfoSort::sortAgeAscend){// 比较函数也可以写为lambda表达式qSort(m_allDatas.begin(), m_allDatas.end(),[](const StudentInfoItem& stu1,const StudentInfoItem& stu2){return stu1.age < stu2.age;});}}voidStudentInfoView::updateSort(int sortType){// qml调用,执行哪一种排序方式,并刷新数据setSort((StudentInfoSort)sortType);if(m_pStudentInfoModel)
        m_pStudentInfoModel->clear();for(auto studentInfo : m_allDatas){if(m_pStudentInfoModel){
            m_pStudentInfoModel->AddModel(studentInfo);}}}voidStudentInfoView::onUpdateInfoData(){// 每隔十秒请求网络接口,根据返回的数据刷新model,该实例模拟网络数据
    m_allDatas.clear();if(m_pStudentInfoModel)
        m_pStudentInfoModel->clear();

    QVector<StudentInfoItem> studentInfos;

    StudentInfoItem item1;
    item1.stuId =9704;
    item1.stuName =QString::fromLocal8Bit("百里");
    item1.sex =1;
    item1.age =14;

    StudentInfoItem item2;
    item2.stuId =9207;
    item2.stuName =QString::fromLocal8Bit("黄忠");
    item2.sex =1;
    item2.age =26;

    StudentInfoItem item3;
    item3.stuId =9206;
    item3.stuName =QString::fromLocal8Bit("鲁班");
    item3.sex =1;
    item3.age =17;

    StudentInfoItem item4;
    item4.stuId =9787;
    item4.stuName =QString::fromLocal8Bit("女娲");
    item4.sex =0;
    item4.age =33;

    studentInfos << item1 << item2 << item3 << item4;

    m_allDatas = studentInfos;setSort(m_sortType);//每一次网络数据刷新后,执行保存的排序方式for(auto studentInfo : m_allDatas){if(m_pStudentInfoModel){
            m_pStudentInfoModel->AddModel(studentInfo);}}}
StudentInfoModel.cpp
#include"StudentInfoModel.h"StudentInfoModel::StudentInfoModel(QObject *parent):QAbstractListModel(parent){roleNames();}StudentInfoModel::~StudentInfoModel(){}voidStudentInfoModel::clear(){if(m_allDatas.size()<=0)return;beginRemoveRows(QModelIndex(),0, m_allDatas.size()-1);
    m_allDatas.clear();endRemoveRows();}voidStudentInfoModel::mremove(int index){beginRemoveRows(QModelIndex(), index, index);
    m_allDatas.erase(m_allDatas.begin()+ index);endRemoveRows();}voidStudentInfoModel::update(int index,const StudentInfoItem &infoModel){if(index <0|| m_allDatas.size()< index)return;

    StudentInfoItem &srcModel = m_allDatas[index];
    srcModel = infoModel;}voidStudentInfoModel::AddModel(const StudentInfoItem &md){beginInsertRows(QModelIndex(),rowCount(),rowCount());
    m_allDatas.push_back(md);endInsertRows();}

QVariant StudentInfoModel::data(const QModelIndex &index,int role)const{if(index.row()<0|| index.row()>= m_allDatas.size()){returnQVariant();}const StudentInfoItem &itemInfo = m_allDatas.at(index.row());

    StudentInfoRoles infoRole =static_cast<StudentInfoRoles>(role);switch(infoRole){case StudentInfoRoles::stuIdRole :return itemInfo.stuId;break;case StudentInfoRoles::stuNameRole :return itemInfo.stuName;break;case StudentInfoRoles::stuSexRole :return itemInfo.sex;break;case StudentInfoRoles::stuAgeRole :return itemInfo.age;break;default:returnQVariant();break;}}intStudentInfoModel::rowCount(const QModelIndex &parent)const{return m_allDatas.size();}

QHash<int, QByteArray>StudentInfoModel::roleNames()const{// 映射C++端的枚举与QML端的字符串
    QHash<int, QByteArray> data;

    data[int(StudentInfoRoles::stuIdRole)]="stuId";
    data[int(StudentInfoRoles::stuNameRole)]="stuName";
    data[int(StudentInfoRoles::stuSexRole)]="sex";
    data[int(StudentInfoRoles::stuAgeRole)]="age";return data;}
3、qml代码
main.qml
importQtQuick2.0importQtQuick.Layouts1.0

Item {
    id: root
    width:1200
    height:800

    Rectangle {
        anchors.fill: parent
        color:"red"
        ColumnLayout {
            spacing:0
            MainQuickTopRect {
                id: topRect
                objectName:"topRect"
                width: root.width
                height:50}

            MainQuickMiddleRect {
                id: middleRect
                objectName:"middleRect"
                width: root.width
                height: root.height - topRect.height
            }}}}
MainQuickTopRect.qml
importQtQuick2.12importQtQuick.Layouts1.0importQtQuick.Controls2.12import"../qmlTools/ButtonTools"

Rectangle {
    id: root
    color:"#181D33"

    signal sendTopRectPos(var x, var y)

    function setTitleText(text){
        titleText.text = text
    }

    function onMinQmlWindow(bMin){if(bMin)
            narrowBtn.clicked()// narrowBtn是最小化按钮,当然也可以直接mainQuickView.onShowMinimized()else
            mainQuickView.showNormal()}

    Component.onCompleted:{
        mainQuickView.minQmlWindow.connect(onMinQmlWindow)}

    RowLayout {
        id: rowLayout
        anchors.fill: root
        anchors.leftMargin:16
        anchors.rightMargin:16
        Text {
            id: titleText
            anchors.fill: root
            anchors.left: rowLayout.left
            verticalAlignment: Text.AlignVCenter
            horizontalAlignment: Text.AlignHCenter
            color:"white"
            font.pixelSize:18
            font.family:"Microsoft YaHei UI"
            font.bold:true
            text:""// 设置文本框背景颜色
            Rectangle {
                id: titleTextBack
                anchors.fill: titleText
                color:"#000000"
                z:-1}}// 最小化
        ImageBtn {
            id: narrowBtn
            width:24
            height:24
            anchors.right: closeBtn.left

            normalUrl:"qrc:/resource/minNormal.png"
            hoveredUrl:"qrc:/resource/minHover.png"
            pressedUrl:"qrc:/resource/minHover.png"

            onClicked:{
                mainQuickView.onShowMinimized();
                console.log("min");}}// 关闭
        ImageBtn {
            id: closeBtn
            width:24
            height:24
            anchors.right: rowLayout.right

            normalUrl:"qrc:/resource/closeNormal.png"
            hoveredUrl:"qrc:/resource/closeHover.png"
            pressedUrl:"qrc:/resource/closeHover.png"

            onClicked:{
                mainQuickView.onCloseQuickView();}}}

    MouseArea {
        anchors.fill: parent
        acceptedButtons: Qt.LeftButton
        propagateComposedEvents:true//传递鼠标事件
        property point clickPos:"0, 0"

        onPressed:{
            clickPos = Qt.point(mouse.x, mouse.y)
            console.log("onPressed "+ clickPos);}

        onPositionChanged:{
            var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y)sendTopRectPos(delta.x, delta.y)}}}
MainQuickMiddleRect.qml
importQtQuick2.0importQtQuick2.12importQtQuick.Controls1.2importQtQuick.Controls2.5importQtQuick.Layouts1.1importQtQuick.Controls.Styles1.2import"../qmlTools/ButtonTools"

Rectangle {
    id: root
    color:"#2E3449"
    property int oneCount:50
    property int twoCount:50
    property int threeCount:50
    readonly property int pageWidth  :400

    Rectangle {
        id: titleSwitchBtn
        width: parent.width /2
        anchors.top: parent.top
        anchors.topMargin:30
        anchors.left: parent.left
        anchors.leftMargin: width - width /2
        height:46
        color:"#2C5CA5"

        RowLayout {
            spacing:20
            anchors.bottom: parent.bottom
            anchors.bottomMargin:0
            anchors.left: parent.left
            anchors.leftMargin:40

            DownLineBtn {// qmlTool中自定义实现的下划线按钮
                id: cover_one_btn
                btn_width:100
                btn_string:qsTr("一班")+ oneCount +qsTr("人")
                onMyClicked:{coverBtnStat(cover_one_btn,true)coverBtnStat(cover_two_btn,false)coverBtnStat(cover_three_btn,false)
                    swipeView.currentIndex =0}}

            DownLineBtn {
                id: cover_two_btn
                btn_width:100
                btn_string:qsTr("二班")+ twoCount +qsTr("人")
                onMyClicked:{coverBtnStat(cover_one_btn,false)coverBtnStat(cover_two_btn,true)coverBtnStat(cover_three_btn,false)
                    swipeView.currentIndex =1}}

            DownLineBtn {
                id: cover_three_btn
                btn_width:100
                btn_string:qsTr("三班")+ threeCount +qsTr("人")
                onMyClicked:{coverBtnStat(cover_one_btn,false)coverBtnStat(cover_two_btn,false)coverBtnStat(cover_three_btn,true)
                    swipeView.currentIndex =2}}}}

    Rectangle {
        id: view
        anchors.top: titleSwitchBtn.bottom
        anchors.topMargin:4
        anchors.left: titleSwitchBtn.left
        width: titleSwitchBtn.width
        height: pageWidth
        color:"white"
        radius:4

        SwipeView {
            id: swipeView
            objectName:"outSideSwipView"
            anchors.fill: parent
            currentIndex:0
            clip:true//隐藏未选中的界面
            interactive:false//鼠标能否滑动

            Item {//St: 0,第一页
                id: onePage
                Rectangle {
                    id: oneView
                    anchors.fill: parent
                    color:"#D7B9A1"}}

            Item {//St: 1,第二页
                id: twoPage
                MainQuickMiddleTableRect {
                    id: tableView
                    color:"green"
                    width: titleSwitchBtn.width
                    height: pageWidth
                }}

            Item {//St: 2,第三页
                id: defaultPage

                Rectangle {
                    anchors.fill: parent
                    color:"#E6EC12"
                    radius:4
                    Text {
                        text:qsTr("没有数据")
                        font.pixelSize:16
                        color:"red"
                        font.family:"Microsoft YaHei"
                        verticalAlignment: Text.AlignVCenter
                        horizontalAlignment: Text.AlignHCenter
                        anchors.verticalCenter: parent.verticalCenter
                        anchors.horizontalCenter: parent.horizontalCenter
                    }}}}}

    Component.onCompleted:{// 初始化按钮状态coverBtnStat(cover_one_btn,true)coverBtnStat(cover_two_btn,false)coverBtnStat(cover_three_btn,false)}/*  btn: object
     *  st : modify state
    */
    function coverBtnStat(btn, st){
        btn.bold_st = st
        btn.show_line = st
    }}
MainQuickMiddleTableRect.qml
importQtQuick2.0import"../qmlTools/ButtonTools"importQtQuick.Controls1.4importQtQuick.Controls2.5

Rectangle {
    id: win
    anchors.fill: parent
    color:"green"

    property var current_numbers      :0
    property var stu_id_sort_state    :1
    property var stu_id_sort_open     :false// 绘制表头
    Rectangle {
        id: table_Head
        height:48
        anchors.top: titleSwitchBtn.bottom

        Rectangle {
            id: table_Text
            height:47// 比整个表头高度小1,为了容纳下划线

            TextShow {
                id: name_text
                text:qsTr("姓名")
                anchors.left: table_Text.left
                anchors.leftMargin:68
                anchors.verticalCenter: table_Text.verticalCenter
            }

            TextShow {
                id: id_text
                // 学号未点击时,显示"↕",表示不排序,点击后再判断时升序还是降序
                text:qsTr("学号")+(!stu_id_sort_open ?qsTr("↕"):(stu_id_sort_state ?qsTr("↓"):qsTr("↑")))
                anchors.left: table_Text.left
                anchors.leftMargin:150
                anchors.verticalCenter: table_Text.verticalCenter

                MouseArea {
                    anchors.fill: parent
                    propagateComposedEvents:true
                    onPressed:{
                        console.log("student id clicked")
                        studentInfoView.updateSort(stu_id_sort_state ?1:2)// 通知C++修改排序方式
                        stu_id_sort_state =!stu_id_sort_state
                        stu_id_sort_open =true}}}

            TextShow {
                id: sex_text
                text:qsTr("性别")
                anchors.left: table_Text.left
                anchors.leftMargin:220
                anchors.verticalCenter: table_Text.verticalCenter
            }

            TextShow {
                id: age_text
                text:qsTr("年龄")
                anchors.left: table_Text.left
                anchors.leftMargin:290
                anchors.verticalCenter: table_Text.verticalCenter

                MouseArea {
                    anchors.fill: parent
                    propagateComposedEvents:true
                    onPressed:{
                        console.log("student age clicked")
                        studentInfoView.updateSort(3)
                        stu_id_sort_open =false}}}

            LongLine {
                anchors.top: table_Text.bottom
            }}}//listView
    Rectangle {
        width: parent.width
        height: parent.height - table_Head.height
        color:"green"
        anchors.top: table_Head.bottom

        ListView {
            id: list_view
            anchors.rightMargin:10
            anchors.bottomMargin:50
            anchors.leftMargin:0
            anchors.topMargin:0
            anchors.fill: parent
            maximumFlickVelocity:800
            clip:true
            delegate: studentInfoDelegate
            model: studentInfoModel
            boundsBehavior: Flickable.StopAtBounds
            highlightMoveDuration:0

            ScrollBar.vertical: ScrollBar {
                id: scrollbar
                //visible: (current_numbers > 8)
                visible:true
                anchors.right: list_view.right
                width:8
                active:true
                background: Item {
                    Rectangle {
                        anchors.right: parent.right
                        height: parent.height
                        color:"yellow"
                        radius:4}}
                contentItem: Rectangle {
                    radius:4
                    color:"red"}}}//studentInfoDelegate 渲染每一个item
        Component {
            id: studentInfoDelegate
            Rectangle {
                id: list_item
                width: parent.width
                height:46
                color:"green"

                Rectangle {
                    width: parent.width
                    height: parent.height
                    color:"green"
                    anchors.verticalCenter: parent.verticalCenter

                    Rectangle {
                        id: rect_item
                        color:"#333333"
                        height:45

                        TextShow {
                            id: stu_name
                            text: model.stuName
                            anchors.verticalCenter: rect_item.verticalCenter
                            anchors.left: rect_item.left
                            anchors.leftMargin:68}

                        TextShow {
                            id: id_type
                            text: model.stuId
                            anchors.verticalCenter: rect_item.verticalCenter
                            anchors.left: rect_item.left
                            anchors.leftMargin:150}

                        TextShow {
                            id: sex_type
                            text:(model.sex ==1)?qsTr("男"):qsTr("女")
                            anchors.verticalCenter: rect_item.verticalCenter
                            anchors.left: rect_item.left
                            anchors.leftMargin:220}

                        TextShow {
                            id: age_type
                            text: model.age +qsTr(" 岁")
                            anchors.verticalCenter: rect_item.verticalCenter
                            anchors.left: rect_item.left
                            anchors.leftMargin:290}}

                    LongLine {
                        anchors.top: rect_item.bottom
                    }}}}}

    Component.onCompleted:{// qml加载完成后,可以获取C++中数据的个数,用来标识是否需要展示滚动条
        current_numbers =4}}
标签: qt c++ qml

本文转载自: https://blog.csdn.net/guorong520/article/details/135424825
版权归原作者 daboluo520 所有, 如有侵权,请联系我们删除。

“QT c++和qml交互实例”的评论:

还没有评论