如何在 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 中的按钮单击处理程序:
- rectangle.visible=真
- 延迟调用计时器"d"
- 在 "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
我在 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 中的按钮单击处理程序:
- rectangle.visible=真
- 延迟调用计时器"d"
- 在 "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