Commit d2892026 authored by Joerg Bornemann's avatar Joerg Bornemann
Browse files

QmlProfiler: fix progress bar visibilty for loading traces



Move the main part of the loading work into a background thread
and show the standard progress indicator for the "reading file"
part of the load operation.
The load operation can be canceled now.

Change-Id: I4cb3b762072ab4a0665dcf9d4a39d6d6630d22e8
Task-number: QTCREATORBUG-11822
Reviewed-by: default avatarUlf Hermann <ulf.hermann@theqtcompany.com>
parent 5515d030
......@@ -36,6 +36,7 @@ namespace Constants {
const char ATTACH[] = "Menu.Analyzer.Attach";
const char TraceFileExtension[] = ".qtd";
const char TASK_LOAD[] = "QmlProfiler.TaskLoad";
const char TASK_SAVE[] = "QmlProfiler.TaskSave";
} // namespace Constants
......
......@@ -406,27 +406,33 @@ void QmlProfilerModelManager::setFilename(const QString &filename)
void QmlProfilerModelManager::load()
{
QString filename = d->fileName;
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
emit error(tr("Could not open %1 for reading.").arg(filename));
QFile *file = new QFile(d->fileName, this);
if (!file->open(QIODevice::ReadOnly | QIODevice::Text)) {
emit error(tr("Could not open %1 for reading.").arg(d->fileName));
delete file;
emit loadFinished();
return;
}
// erase current
clear();
setState(QmlProfilerDataState::AcquiringData);
QmlProfilerFileReader reader;
connect(&reader, SIGNAL(error(QString)), this, SIGNAL(error(QString)));
reader.setV8DataModel(d->v8Model);
reader.setQmlDataModel(d->model);
reader.load(&file);
QFuture<void> result = QtConcurrent::run<void>([this, file] (QFutureInterface<void> &future) {
QmlProfilerFileReader reader;
reader.setFuture(&future);
connect(&reader, &QmlProfilerFileReader::error, this, &QmlProfilerModelManager::error);
reader.setV8DataModel(d->v8Model);
reader.setQmlDataModel(d->model);
reader.load(file);
file->close();
file->deleteLater();
// The completion step uses the old progress display widget for now.
complete();
QMetaObject::invokeMethod(this, "loadFinished", Qt::QueuedConnection);
});
complete();
Core::ProgressManager::addTask(result, tr("Loading Trace Data"), Constants::TASK_LOAD);
}
......
......@@ -137,6 +137,7 @@ signals:
void stateChanged();
void progressChanged();
void dataAvailable();
void loadFinished();
void saveFinished();
void requestDetailsForLocation(int eventType, const QmlDebug::QmlEventLocation &location);
......@@ -162,6 +163,7 @@ public slots:
void load();
void newTimeEstimation(qint64 estimation);
private:
void setState(QmlProfilerDataState::State state);
......
......@@ -151,7 +151,9 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent)
connect(d->m_profilerModelManager, SIGNAL(availableFeaturesChanged(quint64)),
this, SLOT(setAvailableFeatures(quint64)));
connect(d->m_profilerModelManager, &QmlProfilerModelManager::saveFinished,
this, &QmlProfilerTool::onSaveFinished);
this, &QmlProfilerTool::onLoadSaveFinished);
connect(d->m_profilerModelManager, &QmlProfilerModelManager::loadFinished,
this, &QmlProfilerTool::onLoadSaveFinished);
d->m_profilerConnections->setModelManager(d->m_profilerModelManager);
Command *command = 0;
......@@ -597,11 +599,6 @@ void QmlProfilerTool::showSaveDialog()
}
}
void QmlProfilerTool::onSaveFinished()
{
AnalyzerManager::mainWindow()->setEnabled(true);
}
void QmlProfilerTool::showLoadDialog()
{
if (!checkForUnsavedNotes())
......@@ -616,12 +613,16 @@ void QmlProfilerTool::showLoadDialog()
tr("QML traces (*%1)").arg(QLatin1String(TraceFileExtension)));
if (!filename.isEmpty()) {
// delayed load (prevent graphical artifacts due to long load time)
d->m_profilerModelManager->setFilename(filename);
QTimer::singleShot(100, d->m_profilerModelManager, SLOT(load()));
AnalyzerManager::mainWindow()->setEnabled(false);
d->m_profilerModelManager->load(filename);
}
}
void QmlProfilerTool::onLoadSaveFinished()
{
AnalyzerManager::mainWindow()->setEnabled(true);
}
/*!
Checks if we have unsaved notes. If so, shows a warning dialog. Returns true if we can continue
with a potentially destructive operation and discard the warnings, or false if not. We don't
......
......@@ -95,8 +95,8 @@ private slots:
void showSaveOption();
void showLoadOption();
void showSaveDialog();
void onSaveFinished();
void showLoadDialog();
void onLoadSaveFinished();
void toggleRecordingFeature(QAction *action);
......
......@@ -122,7 +122,8 @@ static QString qmlTypeAsString(Message message, RangeType rangeType)
QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) :
QObject(parent),
m_v8Model(0)
m_v8Model(0),
m_future(0)
{
}
......@@ -136,8 +137,18 @@ void QmlProfilerFileReader::setQmlDataModel(QmlProfilerDataModel *dataModel)
m_qmlModel = dataModel;
}
void QmlProfilerFileReader::setFuture(QFutureInterface<void> *future)
{
m_future = future;
}
bool QmlProfilerFileReader::load(QIODevice *device)
{
if (m_future) {
m_future->setProgressRange(0, qMin(device->size(), qint64(INT_MAX)));
m_future->setProgressValue(0);
}
QXmlStreamReader stream(device);
bool validVersion = true;
......@@ -145,6 +156,8 @@ bool QmlProfilerFileReader::load(QIODevice *device)
qint64 traceEnd = -1;
while (validVersion && !stream.atEnd() && !stream.hasError()) {
if (isCanceled())
return false;
QXmlStreamReader::TokenType token = stream.readNext();
const QStringRef elementName = stream.name();
switch (token) {
......@@ -179,7 +192,7 @@ bool QmlProfilerFileReader::load(QIODevice *device)
if (elementName == _("v8profile")) {
if (m_v8Model)
m_v8Model->load(stream);
m_v8Model->load(stream, m_future);
break;
}
......@@ -217,12 +230,16 @@ void QmlProfilerFileReader::loadEventData(QXmlStreamReader &stream)
const QmlProfilerDataModel::QmlEventTypeData defaultEvent = event;
while (!stream.atEnd() && !stream.hasError()) {
if (isCanceled())
return;
QXmlStreamReader::TokenType token = stream.readNext();
const QStringRef elementName = stream.name();
switch (token) {
case QXmlStreamReader::StartElement: {
if (elementName == _("event")) {
progress(stream.device());
event = defaultEvent;
const QXmlStreamAttributes attributes = stream.attributes();
......@@ -322,12 +339,16 @@ void QmlProfilerFileReader::loadProfilerDataModel(QXmlStreamReader &stream)
QTC_ASSERT(stream.name() == _("profilerDataModel"), return);
while (!stream.atEnd() && !stream.hasError()) {
if (isCanceled())
return;
QXmlStreamReader::TokenType token = stream.readNext();
const QStringRef elementName = stream.name();
switch (token) {
case QXmlStreamReader::StartElement: {
if (elementName == _("range")) {
progress(stream.device());
QmlProfilerDataModel::QmlEventData range = { -1, 0, 0, 0, 0, 0, 0, 0 };
const QXmlStreamAttributes attributes = stream.attributes();
......@@ -389,12 +410,16 @@ void QmlProfilerFileReader::loadNoteData(QXmlStreamReader &stream)
{
QmlProfilerDataModel::QmlEventNoteData currentNote;
while (!stream.atEnd() && !stream.hasError()) {
if (isCanceled())
return;
QXmlStreamReader::TokenType token = stream.readNext();
const QStringRef elementName = stream.name();
switch (token) {
case QXmlStreamReader::StartElement: {
if (elementName == _("note")) {
progress(stream.device());
QXmlStreamAttributes attrs = stream.attributes();
currentNote.startTime = attrs.value(_("startTime")).toString().toLongLong();
currentNote.duration = attrs.value(_("duration")).toString().toLongLong();
......@@ -420,6 +445,19 @@ void QmlProfilerFileReader::loadNoteData(QXmlStreamReader &stream)
}
}
void QmlProfilerFileReader::progress(QIODevice *device)
{
if (!m_future)
return;
m_future->setProgressValue(qMin(device->pos(), qint64(INT_MAX)));
}
bool QmlProfilerFileReader::isCanceled() const
{
return m_future && m_future->isCanceled();
}
QmlProfilerFileWriter::QmlProfilerFileWriter(QObject *parent) :
QObject(parent),
m_startTime(0),
......
......@@ -58,6 +58,7 @@ public:
void setV8DataModel(QV8ProfilerDataModel *dataModel);
void setQmlDataModel(QmlProfilerDataModel *dataModel);
void setFuture(QFutureInterface<void> *future);
bool load(QIODevice *device);
......@@ -68,9 +69,12 @@ private:
void loadEventData(QXmlStreamReader &reader);
void loadProfilerDataModel(QXmlStreamReader &reader);
void loadNoteData(QXmlStreamReader &reader);
void progress(QIODevice *device);
bool isCanceled() const;
QV8ProfilerDataModel *m_v8Model;
QmlProfilerDataModel *m_qmlModel;
QFutureInterface<void> *m_future;
QVector<QmlProfilerDataModel::QmlEventTypeData> m_qmlEvents;
QVector<QmlProfilerDataModel::QmlEventData> m_ranges;
QVector<QmlProfilerDataModel::QmlEventNoteData> m_notes;
......
......@@ -372,7 +372,7 @@ void QV8ProfilerDataModel::save(QXmlStreamWriter &stream, QFutureInterface<void>
stream.writeEndElement(); // v8 profiler output
}
void QV8ProfilerDataModel::load(QXmlStreamReader &stream)
void QV8ProfilerDataModel::load(QXmlStreamReader &stream, QFutureInterface<void> *future)
{
Q_D(QV8ProfilerDataModel);
QHash <int, QV8EventData *> v8eventBuffer;
......@@ -392,12 +392,17 @@ void QV8ProfilerDataModel::load(QXmlStreamReader &stream)
bool finishedReading = false;
while (!stream.atEnd() && !stream.hasError() && !finishedReading) {
if (future && future->isCanceled())
return;
QXmlStreamReader::TokenType token = stream.readNext();
const QStringRef elementName = stream.name();
switch (token) {
case QXmlStreamReader::StartDocument : continue;
case QXmlStreamReader::StartElement : {
if (elementName == QLatin1String("event")) {
if (future)
future->setProgressValue(qMin(stream.device()->pos(), qint64(INT_MAX)));
QXmlStreamAttributes attributes = stream.attributes();
if (attributes.hasAttribute(QLatin1String("index"))) {
int ndx = attributes.value(QLatin1String("index")).toString().toInt();
......
......@@ -89,7 +89,7 @@ public:
qint64 v8MeasuredTime() const;
void save(QXmlStreamWriter &stream, QFutureInterface<void> *future = 0);
void load(QXmlStreamReader &stream);
void load(QXmlStreamReader &stream, QFutureInterface<void> *future = 0);
void complete();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment