如何在 C++ 代码中的一些“繁重”操作之前使 QML 对象可见
Posted
技术标签:
【中文标题】如何在 C++ 代码中的一些“繁重”操作之前使 QML 对象可见【英文标题】:How to make QML object visible before some "heavy" operation in C++ code 【发布时间】:2019-04-23 18:04:55 【问题描述】:我在 QML 中有以下逻辑:
button click handler in QML:
1) rectangle.visible=true
2) call some C++ method
在 C++ 方法中,我调用 QFile::copy,它从 USB 存储复制文件,并将日志打印到上面的矩形(必须已经可见)。但据我了解,QML 仅在执行按钮单击处理程序后才使元素可见,但 QFile::copy 太慢,所以我只有在复制所有文件后才能看到日志(矩形变为可见)。所以我的问题是,如何在调用 QFile::copy 之前使包含日志的矩形可见(真正“可见”)。当然我可以实现异步复制,但我是 Qt 新手,所以可能有一些解决方案。
谢谢
【问题讨论】:
我建议您在不同的线程中运行繁重的任务,WorkerScript(QML) 或 QThread (C++),这样 GUI 线程就不会被阻塞。 将您在main.cpp
中的消息记录信号连接到您的window
(引擎上下文的根对象),因此每当发出新文本时,您都可以将其添加到您的rectangle
。还要考虑上面的注释(在你的主线程中创建一个 c++ 类并使用moveToThread
,这样你就可以将它与主线程解耦。
【参考方案1】:
把你的大任务放在QThread
:
任务.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
任务.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
【讨论】:
【参考方案2】:不涉及使用线程的一种可能的“简单”解决方案是使用 qml 计时器延迟调用您的 c++ 方法:
QML 中的按钮点击处理程序:
-
rectangle.visible=true
延迟“d”的呼叫计时器
在“d”之后,计时器将触发并调用您的 C++ 方法(届时,
矩形应该已经可见
代码:
Rectangle
id: rectangle
Button
onClicked:
rectangle.visible = true
timer.start()
Timer
id: timer
interval: 100
onTriggered: myCppMethod()
请注意,这不会阻止您的应用程序在 c++ 方法执行期间变得无响应。更好的方法是将您的 cpp 方法移动到另一个线程并使用信号和插槽从主线程调用它。
【讨论】:
以上是关于如何在 C++ 代码中的一些“繁重”操作之前使 QML 对象可见的主要内容,如果未能解决你的问题,请参考以下文章
在繁重的模拟代码中,c++ 类/结构会明显慢于 c-array [关闭]
如何在构建之前使CMake运行python脚本,以便为我的项目生成文件以在构建中使用?
我的 C++ 程序中的一些代码使程序崩溃。我正在实现 BFS 算法