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: Ulf Hermann <ulf.hermann@theqtcompany.com>
This commit is contained in:
Joerg Bornemann
2015-02-17 14:25:24 +01:00
parent 5515d0308e
commit d2892026c8
9 changed files with 85 additions and 28 deletions

View File

@@ -36,6 +36,7 @@ namespace Constants {
const char ATTACH[] = "Menu.Analyzer.Attach"; const char ATTACH[] = "Menu.Analyzer.Attach";
const char TraceFileExtension[] = ".qtd"; const char TraceFileExtension[] = ".qtd";
const char TASK_LOAD[] = "QmlProfiler.TaskLoad";
const char TASK_SAVE[] = "QmlProfiler.TaskSave"; const char TASK_SAVE[] = "QmlProfiler.TaskSave";
} // namespace Constants } // namespace Constants

View File

@@ -406,27 +406,33 @@ void QmlProfilerModelManager::setFilename(const QString &filename)
void QmlProfilerModelManager::load() void QmlProfilerModelManager::load()
{ {
QString filename = d->fileName; QFile *file = new QFile(d->fileName, this);
if (!file->open(QIODevice::ReadOnly | QIODevice::Text)) {
QFile file(filename); emit error(tr("Could not open %1 for reading.").arg(d->fileName));
delete file;
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { emit loadFinished();
emit error(tr("Could not open %1 for reading.").arg(filename));
return; return;
} }
// erase current
clear(); clear();
setState(QmlProfilerDataState::AcquiringData); setState(QmlProfilerDataState::AcquiringData);
QFuture<void> result = QtConcurrent::run<void>([this, file] (QFutureInterface<void> &future) {
QmlProfilerFileReader reader; QmlProfilerFileReader reader;
connect(&reader, SIGNAL(error(QString)), this, SIGNAL(error(QString))); reader.setFuture(&future);
connect(&reader, &QmlProfilerFileReader::error, this, &QmlProfilerModelManager::error);
reader.setV8DataModel(d->v8Model); reader.setV8DataModel(d->v8Model);
reader.setQmlDataModel(d->model); reader.setQmlDataModel(d->model);
reader.load(&file); reader.load(file);
file->close();
file->deleteLater();
// The completion step uses the old progress display widget for now.
complete(); complete();
QMetaObject::invokeMethod(this, "loadFinished", Qt::QueuedConnection);
});
Core::ProgressManager::addTask(result, tr("Loading Trace Data"), Constants::TASK_LOAD);
} }

View File

@@ -137,6 +137,7 @@ signals:
void stateChanged(); void stateChanged();
void progressChanged(); void progressChanged();
void dataAvailable(); void dataAvailable();
void loadFinished();
void saveFinished(); void saveFinished();
void requestDetailsForLocation(int eventType, const QmlDebug::QmlEventLocation &location); void requestDetailsForLocation(int eventType, const QmlDebug::QmlEventLocation &location);
@@ -162,6 +163,7 @@ public slots:
void load(); void load();
void newTimeEstimation(qint64 estimation); void newTimeEstimation(qint64 estimation);
private: private:
void setState(QmlProfilerDataState::State state); void setState(QmlProfilerDataState::State state);

View File

@@ -151,7 +151,9 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent)
connect(d->m_profilerModelManager, SIGNAL(availableFeaturesChanged(quint64)), connect(d->m_profilerModelManager, SIGNAL(availableFeaturesChanged(quint64)),
this, SLOT(setAvailableFeatures(quint64))); this, SLOT(setAvailableFeatures(quint64)));
connect(d->m_profilerModelManager, &QmlProfilerModelManager::saveFinished, 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); d->m_profilerConnections->setModelManager(d->m_profilerModelManager);
Command *command = 0; Command *command = 0;
@@ -597,11 +599,6 @@ void QmlProfilerTool::showSaveDialog()
} }
} }
void QmlProfilerTool::onSaveFinished()
{
AnalyzerManager::mainWindow()->setEnabled(true);
}
void QmlProfilerTool::showLoadDialog() void QmlProfilerTool::showLoadDialog()
{ {
if (!checkForUnsavedNotes()) if (!checkForUnsavedNotes())
@@ -616,12 +613,16 @@ void QmlProfilerTool::showLoadDialog()
tr("QML traces (*%1)").arg(QLatin1String(TraceFileExtension))); tr("QML traces (*%1)").arg(QLatin1String(TraceFileExtension)));
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
// delayed load (prevent graphical artifacts due to long load time) AnalyzerManager::mainWindow()->setEnabled(false);
d->m_profilerModelManager->setFilename(filename); d->m_profilerModelManager->load(filename);
QTimer::singleShot(100, d->m_profilerModelManager, SLOT(load()));
} }
} }
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 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 with a potentially destructive operation and discard the warnings, or false if not. We don't

View File

@@ -95,8 +95,8 @@ private slots:
void showSaveOption(); void showSaveOption();
void showLoadOption(); void showLoadOption();
void showSaveDialog(); void showSaveDialog();
void onSaveFinished();
void showLoadDialog(); void showLoadDialog();
void onLoadSaveFinished();
void toggleRecordingFeature(QAction *action); void toggleRecordingFeature(QAction *action);

View File

@@ -122,7 +122,8 @@ static QString qmlTypeAsString(Message message, RangeType rangeType)
QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) : QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) :
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; m_qmlModel = dataModel;
} }
void QmlProfilerFileReader::setFuture(QFutureInterface<void> *future)
{
m_future = future;
}
bool QmlProfilerFileReader::load(QIODevice *device) 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); QXmlStreamReader stream(device);
bool validVersion = true; bool validVersion = true;
@@ -145,6 +156,8 @@ bool QmlProfilerFileReader::load(QIODevice *device)
qint64 traceEnd = -1; qint64 traceEnd = -1;
while (validVersion && !stream.atEnd() && !stream.hasError()) { while (validVersion && !stream.atEnd() && !stream.hasError()) {
if (isCanceled())
return false;
QXmlStreamReader::TokenType token = stream.readNext(); QXmlStreamReader::TokenType token = stream.readNext();
const QStringRef elementName = stream.name(); const QStringRef elementName = stream.name();
switch (token) { switch (token) {
@@ -179,7 +192,7 @@ bool QmlProfilerFileReader::load(QIODevice *device)
if (elementName == _("v8profile")) { if (elementName == _("v8profile")) {
if (m_v8Model) if (m_v8Model)
m_v8Model->load(stream); m_v8Model->load(stream, m_future);
break; break;
} }
@@ -217,12 +230,16 @@ void QmlProfilerFileReader::loadEventData(QXmlStreamReader &stream)
const QmlProfilerDataModel::QmlEventTypeData defaultEvent = event; const QmlProfilerDataModel::QmlEventTypeData defaultEvent = event;
while (!stream.atEnd() && !stream.hasError()) { while (!stream.atEnd() && !stream.hasError()) {
if (isCanceled())
return;
QXmlStreamReader::TokenType token = stream.readNext(); QXmlStreamReader::TokenType token = stream.readNext();
const QStringRef elementName = stream.name(); const QStringRef elementName = stream.name();
switch (token) { switch (token) {
case QXmlStreamReader::StartElement: { case QXmlStreamReader::StartElement: {
if (elementName == _("event")) { if (elementName == _("event")) {
progress(stream.device());
event = defaultEvent; event = defaultEvent;
const QXmlStreamAttributes attributes = stream.attributes(); const QXmlStreamAttributes attributes = stream.attributes();
@@ -322,12 +339,16 @@ void QmlProfilerFileReader::loadProfilerDataModel(QXmlStreamReader &stream)
QTC_ASSERT(stream.name() == _("profilerDataModel"), return); QTC_ASSERT(stream.name() == _("profilerDataModel"), return);
while (!stream.atEnd() && !stream.hasError()) { while (!stream.atEnd() && !stream.hasError()) {
if (isCanceled())
return;
QXmlStreamReader::TokenType token = stream.readNext(); QXmlStreamReader::TokenType token = stream.readNext();
const QStringRef elementName = stream.name(); const QStringRef elementName = stream.name();
switch (token) { switch (token) {
case QXmlStreamReader::StartElement: { case QXmlStreamReader::StartElement: {
if (elementName == _("range")) { if (elementName == _("range")) {
progress(stream.device());
QmlProfilerDataModel::QmlEventData range = { -1, 0, 0, 0, 0, 0, 0, 0 }; QmlProfilerDataModel::QmlEventData range = { -1, 0, 0, 0, 0, 0, 0, 0 };
const QXmlStreamAttributes attributes = stream.attributes(); const QXmlStreamAttributes attributes = stream.attributes();
@@ -389,12 +410,16 @@ void QmlProfilerFileReader::loadNoteData(QXmlStreamReader &stream)
{ {
QmlProfilerDataModel::QmlEventNoteData currentNote; QmlProfilerDataModel::QmlEventNoteData currentNote;
while (!stream.atEnd() && !stream.hasError()) { while (!stream.atEnd() && !stream.hasError()) {
if (isCanceled())
return;
QXmlStreamReader::TokenType token = stream.readNext(); QXmlStreamReader::TokenType token = stream.readNext();
const QStringRef elementName = stream.name(); const QStringRef elementName = stream.name();
switch (token) { switch (token) {
case QXmlStreamReader::StartElement: { case QXmlStreamReader::StartElement: {
if (elementName == _("note")) { if (elementName == _("note")) {
progress(stream.device());
QXmlStreamAttributes attrs = stream.attributes(); QXmlStreamAttributes attrs = stream.attributes();
currentNote.startTime = attrs.value(_("startTime")).toString().toLongLong(); currentNote.startTime = attrs.value(_("startTime")).toString().toLongLong();
currentNote.duration = attrs.value(_("duration")).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) : QmlProfilerFileWriter::QmlProfilerFileWriter(QObject *parent) :
QObject(parent), QObject(parent),
m_startTime(0), m_startTime(0),

View File

@@ -58,6 +58,7 @@ public:
void setV8DataModel(QV8ProfilerDataModel *dataModel); void setV8DataModel(QV8ProfilerDataModel *dataModel);
void setQmlDataModel(QmlProfilerDataModel *dataModel); void setQmlDataModel(QmlProfilerDataModel *dataModel);
void setFuture(QFutureInterface<void> *future);
bool load(QIODevice *device); bool load(QIODevice *device);
@@ -68,9 +69,12 @@ private:
void loadEventData(QXmlStreamReader &reader); void loadEventData(QXmlStreamReader &reader);
void loadProfilerDataModel(QXmlStreamReader &reader); void loadProfilerDataModel(QXmlStreamReader &reader);
void loadNoteData(QXmlStreamReader &reader); void loadNoteData(QXmlStreamReader &reader);
void progress(QIODevice *device);
bool isCanceled() const;
QV8ProfilerDataModel *m_v8Model; QV8ProfilerDataModel *m_v8Model;
QmlProfilerDataModel *m_qmlModel; QmlProfilerDataModel *m_qmlModel;
QFutureInterface<void> *m_future;
QVector<QmlProfilerDataModel::QmlEventTypeData> m_qmlEvents; QVector<QmlProfilerDataModel::QmlEventTypeData> m_qmlEvents;
QVector<QmlProfilerDataModel::QmlEventData> m_ranges; QVector<QmlProfilerDataModel::QmlEventData> m_ranges;
QVector<QmlProfilerDataModel::QmlEventNoteData> m_notes; QVector<QmlProfilerDataModel::QmlEventNoteData> m_notes;

View File

@@ -372,7 +372,7 @@ void QV8ProfilerDataModel::save(QXmlStreamWriter &stream, QFutureInterface<void>
stream.writeEndElement(); // v8 profiler output stream.writeEndElement(); // v8 profiler output
} }
void QV8ProfilerDataModel::load(QXmlStreamReader &stream) void QV8ProfilerDataModel::load(QXmlStreamReader &stream, QFutureInterface<void> *future)
{ {
Q_D(QV8ProfilerDataModel); Q_D(QV8ProfilerDataModel);
QHash <int, QV8EventData *> v8eventBuffer; QHash <int, QV8EventData *> v8eventBuffer;
@@ -392,12 +392,17 @@ void QV8ProfilerDataModel::load(QXmlStreamReader &stream)
bool finishedReading = false; bool finishedReading = false;
while (!stream.atEnd() && !stream.hasError() && !finishedReading) { while (!stream.atEnd() && !stream.hasError() && !finishedReading) {
if (future && future->isCanceled())
return;
QXmlStreamReader::TokenType token = stream.readNext(); QXmlStreamReader::TokenType token = stream.readNext();
const QStringRef elementName = stream.name(); const QStringRef elementName = stream.name();
switch (token) { switch (token) {
case QXmlStreamReader::StartDocument : continue; case QXmlStreamReader::StartDocument : continue;
case QXmlStreamReader::StartElement : { case QXmlStreamReader::StartElement : {
if (elementName == QLatin1String("event")) { if (elementName == QLatin1String("event")) {
if (future)
future->setProgressValue(qMin(stream.device()->pos(), qint64(INT_MAX)));
QXmlStreamAttributes attributes = stream.attributes(); QXmlStreamAttributes attributes = stream.attributes();
if (attributes.hasAttribute(QLatin1String("index"))) { if (attributes.hasAttribute(QLatin1String("index"))) {
int ndx = attributes.value(QLatin1String("index")).toString().toInt(); int ndx = attributes.value(QLatin1String("index")).toString().toInt();

View File

@@ -89,7 +89,7 @@ public:
qint64 v8MeasuredTime() const; qint64 v8MeasuredTime() const;
void save(QXmlStreamWriter &stream, QFutureInterface<void> *future = 0); void save(QXmlStreamWriter &stream, QFutureInterface<void> *future = 0);
void load(QXmlStreamReader &stream); void load(QXmlStreamReader &stream, QFutureInterface<void> *future = 0);
void complete(); void complete();