Как сделать объект QML видимым перед «тяжелой» операцией в коде C ++ - PullRequest
1 голос
/ 23 апреля 2019

У меня есть следующая логика в 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, так что, возможно, есть какое-то решение.

Спасибо

Ответы [ 2 ]

2 голосов
/ 25 апреля 2019

Поместите свою большую задачу в 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

0 голосов
/ 24 апреля 2019

Одним из возможных «простых» решений, не связанных с использованием потоков, является задержка вызова вашего метода c ++ с использованием таймеров qml:

обработчик нажатия кнопки в QML:

  1. rectangle.visible = true
  2. вызов таймера с задержкой "d"
  3. после "d" таймер сработает и вызовет ваш метод C ++ (к тому времени прямоугольник должен был стать видимым

Код:

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

Обратите внимание, что это не препятствует тому, чтобы ваше приложение перестало отвечать на запросы во время выполнения метода c ++. Лучший способ - переместить ваш метод cpp в другой поток ивызовите его из основного потока, используя сигналы и слоты.

...