forked from qt-creator/qt-creator
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:
@@ -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);
|
||||
|
||||
QFuture<void> result = QtConcurrent::run<void>([this, file] (QFutureInterface<void> &future) {
|
||||
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.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();
|
||||
QMetaObject::invokeMethod(this, "loadFinished", Qt::QueuedConnection);
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user