如何在 C++ 代码中的某些 "heavy" 操作之前使 QML 对象可见

How to make QML object visible before some "heavy" operation in C++ code

我在 QML 中有以下逻辑:

button click handler in QML:

1) rectangle.visible=true

2) call some C++ method

在 C++ 方法中,我调用 QFile::copy,它从 USB 存储复制文件,并将日志打印到上方的矩形(必须已经可见)。但据我所知,QML 仅在执行按钮单击处理程序后才使元素可见,但 QFile::copy 太慢,因此我仅在复制所有文件后才看到日志(矩形变得可见)。所以我的问题是,如何在调用 QFile::copy 之前使包含日志的矩形可见(实际上是 "visible")。我当然可以实现异步复制,但我是 Qt 的新手,所以可能有一些解决方案。

谢谢

一种不涉及使用线程的 "simple" 解决方案是使用 qml 计时器延迟调用您的 c++ 方法:

QML 中的按钮单击处理程序:

  1. rectangle.visible=真
  2. 延迟调用计时器"d"
  3. 在 "d" 之后,计时器将触发并调用您的 C++ 方法(届时, 矩形应该已经可见

代码:

Rectangle {
   id: rectangle
}
Button {
   onClicked: {
      rectangle.visible = true
      timer.start()
   }
}
Timer {
   id: timer
   interval: 100
   onTriggered: myCppMethod()
}

请注意,这不会阻止您的应用程序在 c++ 方法执行期间变得无响应。更好的方法是将您的 cpp 方法移动到另一个线程,并使用信号和槽从主线程调用它。

把你的大任务放在QThread:

thetask.hpp

#ifndef THETASK_HPP
#define THETASK_HPP

#include <QThread>

class TheTask: public QThread
{
    Q_OBJECT

    public:
        TheTask(/* Put operation args here, or through some setters. */);

    protected:
        void run() override;

        // Put stuff for storing operation's arguments and results here.
};

#endif  // THETASK_HPP

thetask.cpp

#include "thetask.hpp"

TheTask::TheTask() :
    QThread()
    // Init stuff for storing operation's arguments and results.
{}

void TheTask::run() {
    // Put your heavy C++ operation here
}

heavy.hpp

#ifndef HEAVY_HPP
#define HEAVY_HPP

#include <QObject>
#include <QList>
class TheTask;

class Heavy: public QObject
{
    Q_OBJECT

    public:
        Heavy();
        virtual ~Heavy();

        /// @brief QML registration
        static void declareQML();

        /// @brief Your heavy operation
        Q_INVOKABLE void doTheBigOperation(/* Operation args */);

    protected:
        /// @brief Storing the running threads for objects lifecycle reasons.
        /// 
        /// The bigOp object would be destroyed at the end of the
        /// Heavy::doTheBigOperation() method. In order to keep it alive,
        /// let's store it in a list.
        QList<TheTask *> bigOps;

    signals:
        /// @brief Emitted when everything is finished.
        void afterBigOp(/* Put operation results here */);

    protected slots:
        /// @brief Treatments to do when the operation is finished.
        void bigOpFinished();
};

#endif  // HEAVY_HPP

heavy.cpp

#include "anchor.hpp"
#include <QQmlEngine>
#include "thetask.hpp"

Heavy::Heavy() :
    QObject(),
    bigOps()
{}

Heavy::~Heavy() {
    // Delete threads pointers properly.
    while (!bigOps.isEmpty()) {
        TheTask * thread = bigOps.takeLast();

        // Stopping threads. Be careful on consequences.
        thread->quit();
        thread->wait();

        thread->deleteLater();
    }
}

void Heavy::declareQML() {
    qmlRegisterType<Heavy>("CppGates", 13, 37, "Heavy");
}

void Heavy::doTheBigOperation(/* Operation args */) {
    TheTask * bigOp = new TheTask(/* Operation args */);

    // A thread emits the QThread::finised() signal when its task is finished.
    connect(bigOp, &TheTask::finished,
            this,  &Heavy::bigOpFinished);

    // Keeping the thread alive (cf. bigOps documentation).
    bigOps << bigOp;

    // Start executing the heavy operation.
    bigOp->start();
}

void Heavy::bigOpFinished() {
    // Retrieving the thread, which is the signal sender.
    TheTask * thread = qobject_cast<TheTask *>(sender());

    // The treatment is over: let's broke communication with its thread.
    disconnect(thread, &TheTask::finished,
               this,   &Heavy::bigOpFinished);

    // Extract operation results from the thread.

    // Removing the thread from the running threads list.
    int threadIndex = bigOps.indexOf(thread);
    bigOps.removeAt(threadIndex);

    thread->deleteLater();

    // Telling QML that the heavy operation is over.
    emit afterBigOp(/* Operation results */);
}

RectComp.qml

import QtQuick 2.12
import CppGates 13.37

Item {
    id: qml_comp

    Heavy { id: controller }

    Rectangle {
        id: rectangle

        // ...
    }

    function doItHeavy() {
        rectangle.visible = false
        controller.doTheBigOperation(/* Operation arguments */)
    }

    function afterTheBigOp(/* Operation results */) {
        // Put here things you would like to do after controller.doTheBigOperation()
    }

    Component.onCompleted: {
        // Connecting a callback to the signal emitted after the heavy operation.
        controller.afterBigOp.connect(qml_comp.afterTheBigOp)
    }
}

main.cpp

#include <QApplication>
#include <QQmlApplicationEngine>
#include "heavy.hpp"

int main(int argc, char ** argv() {
    QApplication app(argc, argv);

    // ...

    Heavy::declareQML();

    // ...

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
    int res = engine.rootObjects().isEmpty() ? -1 : app.exec();

    // ...

    return res;
}

更多信息,请查看QThread的参考:https://doc.qt.io/qt-5/qthread.html