Я хочу выполнить длинную задачу при нажатии кнопки. Я хочу, чтобы эта задача блокировала пользовательский интерфейс, потому что приложение не может работать до тех пор, пока задача не будет выполнена. Тем не менее, я хочу указать пользователю, что что-то происходит, поэтому у меня есть BusyIndicator
(который работает в потоке визуализации) и установлен для отображения до начала операции. Однако это никогда не проявляется. Зачем?Операция блокировки в Qt Quick
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDateTime>
#include <QDebug>
class Task : public QObject
{
Q_OBJECT
Q_PROPERTY(bool running READ running NOTIFY runningChanged)
public:
Task() : mRunning(false) {}
Q_INVOKABLE void run() {
qDebug() << "setting running property to true";
mRunning = true;
emit runningChanged();
// Try to ensure that the scene graph has time to begin the busy indicator
// animation on the render thread.
Q_ASSERT(QMetaObject::invokeMethod(this, "doRun", Qt::QueuedConnection));
}
bool running() const {
return mRunning;
}
signals:
void runningChanged();
private:
Q_INVOKABLE void doRun() {
qDebug() << "beginning long, blocking operation";
QDateTime start = QDateTime::currentDateTime();
while (start.secsTo(QDateTime::currentDateTime()) < 2) {
// Wait...
}
qDebug() << "finished long, blocking operation";
qDebug() << "setting running property to false";
mRunning = false;
emit runningChanged();
}
bool mRunning;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Task task;
engine.rootContext()->setContextProperty("task", &task);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.6
import QtQuick.Window 2.2
import Qt.labs.controls 1.0
Window {
width: 600
height: 400
visible: true
Shortcut {
sequence: "Ctrl+Q"
onActivated: Qt.quit()
}
Column {
anchors.centerIn: parent
spacing: 20
Button {
text: task.running ? "Running task" : "Run task"
onClicked: task.run()
}
BusyIndicator {
anchors.horizontalCenter: parent.horizontalCenter
running: task.running
onRunningChanged: print("BusyIndicator running =", running)
}
}
}
Выход отладки выглядит корректным с точки зрения порядка событий:
setting running property to true
qml: BusyIndicator running = true
beginning long, blocking operation
finished long, blocking operation
setting running property to false
qml: BusyIndicator running = false
Я просто не понимаю, почему вы настаиваете на блокировке основной нити? Просто используйте рабочий поток и блокируйте события в пользовательском интерфейсе, когда он выполняется. Большинство ОС даст вам «приложение не отвечает», если вы блокируете основной поток, обычно просите пользователя немедленно прекратить или прекратить действие. – dtech
Я использую поток GUI, потому что мне еще не нужно переключаться на отдельный поток. Я не слышал сообщение «приложение не отвечающее». – Mitch
Если это не оп, который включает элементы пользовательского интерфейса, вы можете и определенно должны сделать это асинхронно через рабочий поток. Правило большого пальца - это любая операция, которая занимает дольше 25 мс. Вы не получаете «не отвечаете», потому что знаете, что происходит, и вы не можете касаться приложения, как только произойдет событие ввода, которое ОС не сможет передать в цикл событий приложения, вы получите его. Определенно не то, что вы хотели бы передать клиенту. – dtech