From 62dbc5d0bcae849af1dff393bb045a3ad8174ae7 Mon Sep 17 00:00:00 2001 From: Bea Lam Date: Tue, 3 Nov 2009 08:51:14 +1000 Subject: Remember host and port settings. --- tools/qmldebugger/standalone/main.cpp | 3 +++ tools/qmldebugger/standalone/qmldebugger.cpp | 29 +++++++++++++++++++--------- tools/qmldebugger/standalone/qmldebugger.h | 3 +++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/tools/qmldebugger/standalone/main.cpp b/tools/qmldebugger/standalone/main.cpp index 2f2a30e..715837e 100644 --- a/tools/qmldebugger/standalone/main.cpp +++ b/tools/qmldebugger/standalone/main.cpp @@ -45,6 +45,9 @@ int main(int argc, char ** argv) { QApplication app(argc, argv); + app.setApplicationName("QtQmlDebugger"); + app.setOrganizationName("Nokia"); + app.setOrganizationDomain("nokia.com"); QStringList args = app.arguments(); diff --git a/tools/qmldebugger/standalone/qmldebugger.cpp b/tools/qmldebugger/standalone/qmldebugger.cpp index afcf84c..4d86377 100644 --- a/tools/qmldebugger/standalone/qmldebugger.cpp +++ b/tools/qmldebugger/standalone/qmldebugger.cpp @@ -40,12 +40,14 @@ ****************************************************************************/ #include #include -#include -#include -#include -#include -#include -#include +#include + +#include +#include +#include +#include +#include +#include #include "canvasframerate.h" #include "engine.h" @@ -64,12 +66,10 @@ QmlDebugger::QmlDebugger(QWidget *parent) m_connectionState = new QLabel(this); connectLayout->addWidget(m_connectionState); m_host = new QLineEdit(this); - m_host->setText("127.0.0.1"); connectLayout->addWidget(m_host); m_port = new QSpinBox(this); m_port->setMinimum(1024); m_port->setMaximum(20000); - m_port->setValue(3768); connectLayout->addWidget(m_port); m_connectButton = new QPushButton(tr("Connect"), this); QObject::connect(m_connectButton, SIGNAL(clicked()), @@ -99,7 +99,9 @@ QmlDebugger::QmlDebugger(QWidget *parent) QObject::connect(&client, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(connectionError(QAbstractSocket::SocketError))); - m_tabs->setCurrentIndex(1); + QSettings settings; + m_host->setText(settings.value("Host", "127.0.0.1").toString()); + m_port->setValue(settings.value("Port", 3768).toInt()); connectToHost(); } @@ -119,6 +121,15 @@ void QmlDebugger::showEngineTab() m_tabs->setCurrentWidget(m_enginePane); } +void QmlDebugger::closeEvent(QCloseEvent *event) +{ + QSettings settings; + settings.setValue("Host", m_host->text()); + settings.setValue("Port", m_port->value()); + + QWidget::closeEvent(event); +} + void QmlDebugger::connectionStateChanged() { switch (client.state()) { diff --git a/tools/qmldebugger/standalone/qmldebugger.h b/tools/qmldebugger/standalone/qmldebugger.h index 7bacce7..da95ef9 100644 --- a/tools/qmldebugger/standalone/qmldebugger.h +++ b/tools/qmldebugger/standalone/qmldebugger.h @@ -67,6 +67,9 @@ public slots: void connectToHost(); void disconnectFromHost(); +protected: + void closeEvent(QCloseEvent *); + private slots: void connectionStateChanged(); void connectionError(QAbstractSocket::SocketError socketError); -- cgit v0.12 From 2eb395fc1bce47599ed9de453a84d102a4920727 Mon Sep 17 00:00:00 2001 From: Bea Lam Date: Tue, 3 Nov 2009 08:51:55 +1000 Subject: Various UI changes: added graph labels and 'clear' button, changed 'enabled' checkbox to group box and changed embedded scrollbar to external slider. --- tools/qmldebugger/standalone/canvasframerate.cpp | 213 ++++++++++++++++------- tools/qmldebugger/standalone/canvasframerate.h | 13 +- 2 files changed, 155 insertions(+), 71 deletions(-) diff --git a/tools/qmldebugger/standalone/canvasframerate.cpp b/tools/qmldebugger/standalone/canvasframerate.cpp index 408e8d0..0921be0 100644 --- a/tools/qmldebugger/standalone/canvasframerate.cpp +++ b/tools/qmldebugger/standalone/canvasframerate.cpp @@ -38,23 +38,26 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include "canvasframerate.h" -#include -#include -#include -#include #include #include #include + +#include +#include +#include +#include +#include #include +#include +#include +#include +#include + #include #include -#include -#include -#include -#include -#include -#include + +#include +#include "canvasframerate.h" QT_BEGIN_NAMESPACE @@ -62,22 +65,23 @@ class QLineGraph : public QWidget { Q_OBJECT public: - QLineGraph(QWidget * = 0); + QLineGraph(QAbstractSlider *slider, QWidget * = 0); void setPosition(int); public slots: void addSample(int, int, int, bool); void setResolutionForHeight(int); + void clear(); protected: virtual void paintEvent(QPaintEvent *); private slots: - void scrollbarChanged(int); + void sliderChanged(int); private: - void updateScrollbar(); + void updateSlider(); void drawSample(QPainter *, int, const QRect &); void drawTime(QPainter *, const QRect &); struct Sample { @@ -86,50 +90,53 @@ private: }; QList _samples; - QScrollBar sb; + QAbstractSlider *slider; int position; int samplesPerWidth; int resolutionForHeight; bool ignoreScroll; }; -QLineGraph::QLineGraph(QWidget *parent) -: QWidget(parent), sb(Qt::Horizontal, this), position(-1), samplesPerWidth(99), resolutionForHeight(50), ignoreScroll(false) +QLineGraph::QLineGraph(QAbstractSlider *slider, QWidget *parent) +: QWidget(parent), slider(slider), position(-1), samplesPerWidth(99), resolutionForHeight(50), ignoreScroll(false) { setMinimumHeight(200); - sb.setMaximum(0); - sb.setMinimum(0); - sb.setSingleStep(1); - - QVBoxLayout *layout = new QVBoxLayout; - setLayout(layout); - layout->addStretch(2); - layout->addWidget(&sb); - QObject::connect(&sb, SIGNAL(valueChanged(int)), this, SLOT(scrollbarChanged(int))); + slider->setMaximum(0); + slider->setMinimum(0); + slider->setSingleStep(1); + + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged(int))); } -void QLineGraph::scrollbarChanged(int v) +void QLineGraph::sliderChanged(int v) { if(ignoreScroll) return; - if (v == sb.maximum()) + if (v == slider->maximum()) position = -1; else position = v; update(); } -void QLineGraph::updateScrollbar() +void QLineGraph::clear() +{ + _samples.clear(); + updateSlider(); + update(); +} + +void QLineGraph::updateSlider() { ignoreScroll = true; - sb.setMaximum(qMax(0, _samples.count() - samplesPerWidth - 1)); + slider->setMaximum(qMax(0, _samples.count() - samplesPerWidth - 1)); if(position == -1) { - sb.setValue(sb.maximum()); + slider->setValue(slider->maximum()); } else { - sb.setValue(position); + slider->setValue(position); } ignoreScroll = false; } @@ -142,13 +149,13 @@ void QLineGraph::addSample(int a, int b, int d, bool isBreak) s.sample[1] = b; s.sample[2] = d; _samples << s; - updateScrollbar(); + updateSlider(); update(); } void QLineGraph::setPosition(int p) { - scrollbarChanged(p); + sliderChanged(p); } void QLineGraph::drawTime(QPainter *p, const QRect &rect) @@ -218,7 +225,14 @@ void QLineGraph::paintEvent(QPaintEvent *) QPainter p(this); p.setRenderHint(QPainter::Antialiasing); - QRect r(50, 10, width() - 60, height() - 60); + QRect r(65, 10, width() - 71, height() - 40); + + p.save(); + p.rotate(-90); + p.translate(-r.height()/2 - r.width()/2 - 71, -r.height()/2); + p.drawText(r, Qt::AlignCenter, tr("Time per frame (ms)")); + p.restore(); + p.setBrush(QColor("lightsteelblue")); drawSample(&p, 0, r); @@ -228,6 +242,8 @@ void QLineGraph::paintEvent(QPaintEvent *) p.setBrush(Qt::NoBrush); p.drawRect(r); + slider->setGeometry(x() + r.x(), slider->y(), r.width(), slider->height()); + for(int ii = 0; ii <= resolutionForHeight; ++ii) { int y = 1 + r.bottom() - ii * r.height() / resolutionForHeight; @@ -249,9 +265,58 @@ void QLineGraph::setResolutionForHeight(int resolution) update(); } + +class GraphWindow : public QWidget +{ + Q_OBJECT +public: + GraphWindow(QWidget *parent = 0); + +public slots: + void addSample(int, int, int, bool); + void setResolutionForHeight(int); + void clear(); + +private: + QLineGraph *m_graph; +}; + +GraphWindow::GraphWindow(QWidget *parent) + : QWidget(parent) +{ + QSlider *scroll = new QSlider(Qt::Horizontal); + m_graph = new QLineGraph(scroll); + + QLabel *label = new QLabel(tr("Total time elapsed (ms)")); + label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 5, 5); + layout->setSpacing(0); + layout->addWidget(m_graph); + layout->addWidget(label, 0, Qt::AlignHCenter); + layout->addWidget(scroll); +} + +void GraphWindow::addSample(int a, int b, int d, bool isBreak) +{ + m_graph->addSample(a, b, d, isBreak); +} + +void GraphWindow::setResolutionForHeight(int res) +{ + m_graph->setResolutionForHeight(res); +} + +void GraphWindow::clear() +{ + m_graph->clear(); +} + + class CanvasFrameRatePlugin : public QmlDebugClient { -Q_OBJECT + Q_OBJECT public: CanvasFrameRatePlugin(QmlDebugConnection *client); @@ -290,38 +355,44 @@ CanvasFrameRate::CanvasFrameRate(QWidget *parent) : QWidget(parent), m_plugin(0) { - QVBoxLayout *layout = new QVBoxLayout; - layout->setContentsMargins(0,0,0,0); - layout->setSpacing(0); - setLayout(layout); - m_tabs = new QTabWidget(this); - layout->addWidget(m_tabs); QHBoxLayout *bottom = new QHBoxLayout; - bottom->setContentsMargins(5, 0, 5, 0); + bottom->setMargin(0); bottom->setSpacing(10); - layout->addLayout(bottom); - - QLabel *label = new QLabel("Resolution", this); - bottom->addWidget(label); - m_spin = new QSpinBox(this); - m_spin->setRange(50,200); - m_spin->setValue(50); - m_spin->setSuffix("ms"); - bottom->addWidget(m_spin); + m_res = new QSpinBox; + m_res->setRange(50, 200); + m_res->setValue(m_res->minimum()); + m_res->setSingleStep(10); + m_res->setSuffix(QLatin1String("ms")); + bottom->addWidget(new QLabel(tr("Resolution:"))); + bottom->addWidget(m_res); - bottom->addStretch(2); + bottom->addStretch(); - m_enabledCheckBox = new QCheckBox("Enable", this); - bottom->addWidget(m_enabledCheckBox); - QObject::connect(m_enabledCheckBox, SIGNAL(stateChanged(int)), - this, SLOT(enabledStateChanged(int))); + m_clearButton = new QPushButton(tr("Clear")); + connect(m_clearButton, SIGNAL(clicked()), SLOT(clearGraph())); + bottom->addWidget(m_clearButton); - QPushButton *pb = new QPushButton(tr("New Tab"), this); - QObject::connect(pb, SIGNAL(clicked()), this, SLOT(newTab())); + QPushButton *pb = new QPushButton(tr("New Graph"), this); + connect(pb, SIGNAL(clicked()), this, SLOT(newTab())); bottom->addWidget(pb); + + m_group = new QGroupBox(tr("Enabled")); + m_group->setCheckable(true); + m_group->setChecked(false); + connect(m_group, SIGNAL(toggled(bool)), SLOT(enabledToggled(bool))); + + QVBoxLayout *groupLayout = new QVBoxLayout(m_group); + groupLayout->addWidget(m_tabs); + groupLayout->addLayout(bottom); + + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(m_group); + setLayout(layout); } void CanvasFrameRate::reset(QmlDebugConnection *conn) @@ -358,7 +429,7 @@ void CanvasFrameRate::handleConnected(QmlDebugConnection *conn) { delete m_plugin; m_plugin = new CanvasFrameRatePlugin(conn); - enabledStateChanged(m_enabledCheckBox->checkState()); + enabledToggled(m_group->isChecked()); newTab(); } @@ -372,6 +443,14 @@ QSize CanvasFrameRate::sizeHint() const return m_sizeHint; } +void CanvasFrameRate::clearGraph() +{ + if (m_tabs->count()) { + GraphWindow *w = qobject_cast(m_tabs->widget(m_tabs->count() - 1)); + w->clear(); + } +} + void CanvasFrameRate::newTab() { if (!m_plugin) @@ -385,20 +464,20 @@ void CanvasFrameRate::newTab() int id = m_tabs->count(); - QLineGraph *graph = new QLineGraph(this); - QObject::connect(m_plugin, SIGNAL(sample(int,int,int,bool)), - graph, SLOT(addSample(int,int,int,bool))); - QObject::connect(m_spin, SIGNAL(valueChanged(int)), graph, SLOT(setResolutionForHeight(int))); + GraphWindow *graph = new GraphWindow; + graph->setResolutionForHeight(m_res->value()); + connect(m_plugin, SIGNAL(sample(int,int,int,bool)), + graph, SLOT(addSample(int,int,int,bool))); + connect(m_res, SIGNAL(valueChanged(int)), + graph, SLOT(setResolutionForHeight(int))); QString name = QLatin1String("Graph ") + QString::number(id); m_tabs->addTab(graph, name); m_tabs->setCurrentIndex(id); } -void CanvasFrameRate::enabledStateChanged(int s) +void CanvasFrameRate::enabledToggled(bool checked) { - bool checked = s != 0; - if (m_plugin) static_cast(m_plugin)->setEnabled(checked); } diff --git a/tools/qmldebugger/standalone/canvasframerate.h b/tools/qmldebugger/standalone/canvasframerate.h index be6bbc6..f8eec59 100644 --- a/tools/qmldebugger/standalone/canvasframerate.h +++ b/tools/qmldebugger/standalone/canvasframerate.h @@ -49,8 +49,11 @@ QT_BEGIN_NAMESPACE class QTabWidget; +class QSlider; +class QGroupBox; +class QLabel; class QSpinBox; -class QCheckBox; +class QPushButton; class CanvasFrameRatePlugin; @@ -66,18 +69,20 @@ public: virtual QSize sizeHint() const; private slots: + void clearGraph(); void newTab(); - void enabledStateChanged(int); + void enabledToggled(bool); void connectionStateChanged(QAbstractSocket::SocketState state); private: void handleConnected(QmlDebugConnection *conn); + QGroupBox *m_group; QTabWidget *m_tabs; - QSpinBox *m_spin; + QSpinBox *m_res; + QPushButton *m_clearButton; CanvasFrameRatePlugin *m_plugin; QSize m_sizeHint; - QCheckBox *m_enabledCheckBox; }; QT_END_NAMESPACE -- cgit v0.12 From e0688cfd4d9a31d53b456d0c211ab39565305064 Mon Sep 17 00:00:00 2001 From: Bea Lam Date: Tue, 3 Nov 2009 16:02:49 +1000 Subject: Add tooltip and bar highlight when cursor is hovered over a graph bar. --- tools/qmldebugger/standalone/canvasframerate.cpp | 115 ++++++++++++++++++----- 1 file changed, 94 insertions(+), 21 deletions(-) diff --git a/tools/qmldebugger/standalone/canvasframerate.cpp b/tools/qmldebugger/standalone/canvasframerate.cpp index 0921be0..63e04ed 100644 --- a/tools/qmldebugger/standalone/canvasframerate.cpp +++ b/tools/qmldebugger/standalone/canvasframerate.cpp @@ -41,8 +41,10 @@ #include #include #include +#include #include +#include #include #include #include @@ -76,14 +78,17 @@ public slots: protected: virtual void paintEvent(QPaintEvent *); + virtual void mouseMoveEvent(QMouseEvent *); + virtual void leaveEvent(QEvent *); private slots: void sliderChanged(int); private: void updateSlider(); - void drawSample(QPainter *, int, const QRect &); + void drawSample(QPainter *, int, const QRect &, QList *); void drawTime(QPainter *, const QRect &); + QRect findContainingRect(const QList &rects, const QPoint &pos) const; struct Sample { int sample[3]; bool isBreak; @@ -95,12 +100,18 @@ private: int samplesPerWidth; int resolutionForHeight; bool ignoreScroll; + QMargins graphMargins; + + QList rectsPaintTime; // time to do a paintEvent() + QList rectsTimeBetween; // time between frames + QRect highlightedBar; }; QLineGraph::QLineGraph(QAbstractSlider *slider, QWidget *parent) -: QWidget(parent), slider(slider), position(-1), samplesPerWidth(99), resolutionForHeight(50), ignoreScroll(false) +: QWidget(parent), slider(slider), position(-1), samplesPerWidth(99), resolutionForHeight(50), + ignoreScroll(false), graphMargins(65, 10, 71, 40) { - setMinimumHeight(200); + setMouseTracking(true); slider->setMaximum(0); slider->setMinimum(0); @@ -124,6 +135,11 @@ void QLineGraph::sliderChanged(int v) void QLineGraph::clear() { _samples.clear(); + rectsPaintTime.clear(); + rectsTimeBetween.clear(); + highlightedBar = QRect(); + position = -1; + updateSlider(); update(); } @@ -189,7 +205,7 @@ void QLineGraph::drawTime(QPainter *p, const QRect &rect) } -void QLineGraph::drawSample(QPainter *p, int s, const QRect &rect) +void QLineGraph::drawSample(QPainter *p, int s, const QRect &rect, QList *record) { if(_samples.isEmpty()) return; @@ -212,8 +228,11 @@ void QLineGraph::drawSample(QPainter *p, int s, const QRect &rect) xEnd = rect.left() + scaleX * (ii - first); int yEnd = rect.bottom() - _samples.at(ii).sample[s] * scaleY; - if (!(s == 0 && _samples.at(ii).isBreak)) - p->drawRect(QRect(lastXEnd, yEnd, scaleX, _samples.at(ii).sample[s] * scaleY)); + if (!(s == 0 && _samples.at(ii).isBreak)) { + QRect bar(lastXEnd, yEnd, scaleX, _samples.at(ii).sample[s] * scaleY); + record->append(bar); + p->drawRect(bar); + } lastXEnd = xEnd; } @@ -225,19 +244,27 @@ void QLineGraph::paintEvent(QPaintEvent *) QPainter p(this); p.setRenderHint(QPainter::Antialiasing); - QRect r(65, 10, width() - 71, height() - 40); + QRect r(graphMargins.left(), graphMargins.top(), + width() - graphMargins.right(), height() - graphMargins.bottom()); p.save(); p.rotate(-90); - p.translate(-r.height()/2 - r.width()/2 - 71, -r.height()/2); + p.translate(-r.height()/2 - r.width()/2 - graphMargins.right(), -r.height()/2); p.drawText(r, Qt::AlignCenter, tr("Time per frame (ms)")); p.restore(); p.setBrush(QColor("lightsteelblue")); - drawSample(&p, 0, r); + rectsTimeBetween.clear(); + drawSample(&p, 0, r, &rectsTimeBetween); p.setBrush(QColor("pink")); - drawSample(&p, 1, r); + rectsPaintTime.clear(); + drawSample(&p, 1, r, &rectsPaintTime); + + if (!highlightedBar.isNull()) { + p.setBrush(Qt::darkGreen); + p.drawRect(highlightedBar); + } p.setBrush(Qt::NoBrush); p.drawRect(r); @@ -259,12 +286,50 @@ void QLineGraph::paintEvent(QPaintEvent *) drawTime(&p, r); } +void QLineGraph::mouseMoveEvent(QMouseEvent *event) +{ + QPoint pos = event->pos(); + + QRect rect = findContainingRect(rectsPaintTime, pos); + if (rect.isNull()) + rect = findContainingRect(rectsTimeBetween, pos); + + if (!highlightedBar.isNull()) + update(highlightedBar.adjusted(-1, -1, 1, 1)); + highlightedBar = rect; + + if (!rect.isNull()) { + QRect graph(graphMargins.left(), graphMargins.top(), + width() - graphMargins.right(), height() - graphMargins.bottom()); + qreal scaleY = qreal(graph.height()) / resolutionForHeight; + QToolTip::showText(event->globalPos(), QString::number(qRound(rect.height() / scaleY)), this, rect); + update(rect.adjusted(-1, -1, 1, 1)); + } +} + +void QLineGraph::leaveEvent(QEvent *) +{ + if (!highlightedBar.isNull()) { + highlightedBar = QRect(); + update(highlightedBar.adjusted(-1, -1, 1, 1)); + } +} + void QLineGraph::setResolutionForHeight(int resolution) { resolutionForHeight = resolution; update(); } +QRect QLineGraph::findContainingRect(const QList &rects, const QPoint &pos) const +{ + for (int i=0; isetFocusPolicy(Qt::WheelFocus); m_graph = new QLineGraph(scroll); - QLabel *label = new QLabel(tr("Total time elapsed (ms)")); - label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + setFocusPolicy(Qt::WheelFocus); + setFocusProxy(scroll); QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 5, 5); layout->setSpacing(0); - layout->addWidget(m_graph); - layout->addWidget(label, 0, Qt::AlignHCenter); + layout->addWidget(m_graph, 2); + layout->addWidget(new QLabel(tr("Total time elapsed (ms)")), 0, Qt::AlignHCenter); layout->addWidget(scroll); + + setMinimumSize(QSize(400, 200)); } void GraphWindow::addSample(int a, int b, int d, bool isBreak) @@ -362,7 +430,7 @@ CanvasFrameRate::CanvasFrameRate(QWidget *parent) bottom->setSpacing(10); m_res = new QSpinBox; - m_res->setRange(50, 200); + m_res->setRange(30, 200); m_res->setValue(m_res->minimum()); m_res->setSingleStep(10); m_res->setSuffix(QLatin1String("ms")); @@ -385,14 +453,18 @@ CanvasFrameRate::CanvasFrameRate(QWidget *parent) connect(m_group, SIGNAL(toggled(bool)), SLOT(enabledToggled(bool))); QVBoxLayout *groupLayout = new QVBoxLayout(m_group); + groupLayout->setContentsMargins(5, 0, 5, 0); + groupLayout->setSpacing(5); groupLayout->addWidget(m_tabs); groupLayout->addLayout(bottom); QVBoxLayout *layout = new QVBoxLayout; - layout->setMargin(0); + layout->setContentsMargins(0, 15, 0, 0); layout->setSpacing(0); layout->addWidget(m_group); setLayout(layout); + + setFocusPolicy(Qt::StrongFocus); } void CanvasFrameRate::reset(QmlDebugConnection *conn) @@ -446,8 +518,9 @@ QSize CanvasFrameRate::sizeHint() const void CanvasFrameRate::clearGraph() { if (m_tabs->count()) { - GraphWindow *w = qobject_cast(m_tabs->widget(m_tabs->count() - 1)); - w->clear(); + GraphWindow *w = qobject_cast(m_tabs->currentWidget()); + if (w) + w->clear(); } } @@ -462,7 +535,7 @@ void CanvasFrameRate::newTab() w, SLOT(addSample(int,int,int,bool))); } - int id = m_tabs->count(); + int count = m_tabs->count(); GraphWindow *graph = new GraphWindow; graph->setResolutionForHeight(m_res->value()); @@ -471,9 +544,9 @@ void CanvasFrameRate::newTab() connect(m_res, SIGNAL(valueChanged(int)), graph, SLOT(setResolutionForHeight(int))); - QString name = QLatin1String("Graph ") + QString::number(id); + QString name = QLatin1String("Graph ") + QString::number(count + 1); m_tabs->addTab(graph, name); - m_tabs->setCurrentIndex(id); + m_tabs->setCurrentIndex(count); } void CanvasFrameRate::enabledToggled(bool checked) -- cgit v0.12 From 7a249a72f118f5adacc7ca3af498ac6434dc8c65 Mon Sep 17 00:00:00 2001 From: Bea Lam Date: Wed, 4 Nov 2009 14:30:21 +1000 Subject: Show source file and line of an object in the text editor when it is double clicked in the object tree. --- tools/qmldebugger/creatorplugin/creatorplugin.pro | 1 + .../qmldebugger/creatorplugin/qmlinspectormode.cpp | 24 ++++++++++++++++++++++ tools/qmldebugger/creatorplugin/qmlinspectormode.h | 2 ++ tools/qmldebugger/standalone/objecttree.cpp | 20 +++++++++++++----- tools/qmldebugger/standalone/objecttree.h | 2 ++ 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/tools/qmldebugger/creatorplugin/creatorplugin.pro b/tools/qmldebugger/creatorplugin/creatorplugin.pro index d191a37..ff7f3da 100644 --- a/tools/qmldebugger/creatorplugin/creatorplugin.pro +++ b/tools/qmldebugger/creatorplugin/creatorplugin.pro @@ -25,5 +25,6 @@ IDE_BUILD_TREE=$$(CREATOR_BUILD_DIR) include($$(CREATOR_SRC_DIR)/src/qtcreatorplugin.pri) include($$(CREATOR_SRC_DIR)/src/plugins/projectexplorer/projectexplorer.pri) include($$(CREATOR_SRC_DIR)/src/plugins/coreplugin/coreplugin.pri) +include($$(CREATOR_SRC_DIR)/src/plugins/texteditor/texteditor.pri) LIBS += -L$$(CREATOR_BUILD_DIR)/lib/qtcreator diff --git a/tools/qmldebugger/creatorplugin/qmlinspectormode.cpp b/tools/qmldebugger/creatorplugin/qmlinspectormode.cpp index a1ca2fc..ce8ef30 100644 --- a/tools/qmldebugger/creatorplugin/qmlinspectormode.cpp +++ b/tools/qmldebugger/creatorplugin/qmlinspectormode.cpp @@ -65,6 +65,8 @@ #include #include +#include + #include #include #include @@ -435,8 +437,12 @@ void QmlInspectorMode::initWidgets() WatchTableHeaderView *header = new WatchTableHeaderView(m_watchTableModel); m_watchTableView->setHorizontalHeader(header); + connect(m_objectTreeWidget, SIGNAL(activated(QmlDebugObjectReference)), + this, SLOT(treeObjectActivated(QmlDebugObjectReference))); + connect(m_objectTreeWidget, SIGNAL(currentObjectChanged(QmlDebugObjectReference)), m_propertiesWidget, SLOT(reload(QmlDebugObjectReference))); + connect(m_objectTreeWidget, SIGNAL(expressionWatchRequested(QmlDebugObjectReference,QString)), m_watchTableModel, SLOT(expressionWatchRequested(QmlDebugObjectReference,QString))); @@ -536,6 +542,24 @@ void QmlInspectorMode::contextChanged() delete m_contextQuery; m_contextQuery = 0; } +void QmlInspectorMode::treeObjectActivated(const QmlDebugObjectReference &obj) +{ + QmlDebugFileReference source = obj.source(); + QString fileName = source.url().toLocalFile(); + + if (source.lineNumber() < 0 || !QFile::exists(fileName)) + return; + + Core::EditorManager *editorManager = Core::EditorManager::instance(); + TextEditor::ITextEditor *editor = qobject_cast(editorManager->openEditor(fileName)); + if (editor) { + editorManager->ensureEditorManagerVisible(); + editorManager->addCurrentPositionToNavigationHistory(); + editor->gotoLine(source.lineNumber()); + editor->widget()->setFocus(); + } +} + QT_END_NAMESPACE #include "qmlinspectormode.moc" diff --git a/tools/qmldebugger/creatorplugin/qmlinspectormode.h b/tools/qmldebugger/creatorplugin/qmlinspectormode.h index 93c2e44..b4158f1 100644 --- a/tools/qmldebugger/creatorplugin/qmlinspectormode.h +++ b/tools/qmldebugger/creatorplugin/qmlinspectormode.h @@ -57,6 +57,7 @@ class QmlEngineDebug; class QmlDebugConnection; class QmlDebugEnginesQuery; class QmlDebugRootContextQuery; +class QmlDebugObjectReference; class ObjectTree; class WatchTableModel; class WatchTableView; @@ -92,6 +93,7 @@ private slots: void enginesChanged(); void queryEngineContext(int); void contextChanged(); + void treeObjectActivated(const QmlDebugObjectReference &obj); private: struct Actions { diff --git a/tools/qmldebugger/standalone/objecttree.cpp b/tools/qmldebugger/standalone/objecttree.cpp index b06d377..4dbc1a7 100644 --- a/tools/qmldebugger/standalone/objecttree.cpp +++ b/tools/qmldebugger/standalone/objecttree.cpp @@ -59,9 +59,12 @@ ObjectTree::ObjectTree(QmlEngineDebug *client, QWidget *parent) { setHeaderHidden(true); setMinimumWidth(250); + setExpandsOnDoubleClick(false); connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), - this, SLOT(currentItemChanged(QTreeWidgetItem *))); + SLOT(currentItemChanged(QTreeWidgetItem *))); + connect(this, SIGNAL(itemActivated(QTreeWidgetItem *, int)), + SLOT(activated(QTreeWidgetItem *))); } void ObjectTree::setEngineDebug(QmlEngineDebug *client) @@ -113,11 +116,18 @@ void ObjectTree::currentItemChanged(QTreeWidgetItem *item) return; QmlDebugObjectReference obj = item->data(0, Qt::UserRole).value(); - if (obj.debugId() < 0) { - qWarning("QML Object Tree: bad object id"); + if (obj.debugId() >= 0) + emit currentObjectChanged(obj); +} + +void ObjectTree::activated(QTreeWidgetItem *item) +{ + if (!item) return; - } - emit currentObjectChanged(obj); + + QmlDebugObjectReference obj = item->data(0, Qt::UserRole).value(); + if (obj.debugId() >= 0) + emit activated(obj); } void ObjectTree::buildTree(const QmlDebugObjectReference &obj, QTreeWidgetItem *parent) diff --git a/tools/qmldebugger/standalone/objecttree.h b/tools/qmldebugger/standalone/objecttree.h index f7b3a3f..c8d625c 100644 --- a/tools/qmldebugger/standalone/objecttree.h +++ b/tools/qmldebugger/standalone/objecttree.h @@ -64,6 +64,7 @@ public: signals: void currentObjectChanged(const QmlDebugObjectReference &); + void activated(const QmlDebugObjectReference &); void expressionWatchRequested(const QmlDebugObjectReference &, const QString &); public slots: @@ -76,6 +77,7 @@ protected: private slots: void objectFetched(); void currentItemChanged(QTreeWidgetItem *); + void activated(QTreeWidgetItem *); private: QTreeWidgetItem *findItemByObjectId(int debugId) const; -- cgit v0.12 From f447e64f378fdebea3b8f2fca5624f48546a0529 Mon Sep 17 00:00:00 2001 From: Bea Lam Date: Wed, 4 Nov 2009 16:20:56 +1000 Subject: Use QML type names in object tree --- src/declarative/qml/qmlenginedebug.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/declarative/qml/qmlenginedebug.cpp b/src/declarative/qml/qmlenginedebug.cpp index 2d8acf7..52d54b9 100644 --- a/src/declarative/qml/qmlenginedebug.cpp +++ b/src/declarative/qml/qmlenginedebug.cpp @@ -271,10 +271,18 @@ QmlEngineDebugServer::objectData(QObject *object) } rv.objectName = object->objectName(); - rv.objectType = QString::fromUtf8(object->metaObject()->className()); rv.objectId = QmlDebugService::idForObject(object); rv.contextId = QmlDebugService::idForObject(qmlContext(object)); + QmlType *type = QmlMetaType::qmlType(object->metaObject()); + if (type) { + QString typeName = type->qmlTypeName(); + int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); + rv.objectType = lastSlash < 0 ? typeName : typeName.mid(lastSlash+1); + } else { + rv.objectType = QString::fromUtf8(object->metaObject()->className()); + } + return rv; } -- cgit v0.12 From 3babe49011d363aaaabafb70bfb0626217ec8959 Mon Sep 17 00:00:00 2001 From: Bea Lam Date: Thu, 5 Nov 2009 13:54:25 +1000 Subject: Move graph slider when graph gets wheel event. --- tools/qmldebugger/standalone/canvasframerate.cpp | 43 ++++++++++++++++++------ 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/tools/qmldebugger/standalone/canvasframerate.cpp b/tools/qmldebugger/standalone/canvasframerate.cpp index 63e04ed..d956029 100644 --- a/tools/qmldebugger/standalone/canvasframerate.cpp +++ b/tools/qmldebugger/standalone/canvasframerate.cpp @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -80,6 +81,7 @@ protected: virtual void paintEvent(QPaintEvent *); virtual void mouseMoveEvent(QMouseEvent *); virtual void leaveEvent(QEvent *); + virtual void wheelEvent(QWheelEvent *event); private slots: void sliderChanged(int); @@ -109,7 +111,7 @@ private: QLineGraph::QLineGraph(QAbstractSlider *slider, QWidget *parent) : QWidget(parent), slider(slider), position(-1), samplesPerWidth(99), resolutionForHeight(50), - ignoreScroll(false), graphMargins(65, 10, 71, 40) + ignoreScroll(false), graphMargins(65, 10, 71, 35) { setMouseTracking(true); @@ -129,7 +131,16 @@ void QLineGraph::sliderChanged(int v) position = -1; else position = v; + update(); + + // update highlightedRect + QPoint pos = mapFromGlobal(QCursor::pos()); + if (geometry().contains(pos)) { + QMouseEvent *me = new QMouseEvent(QEvent::MouseMove, pos, + Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QApplication::postEvent(this, me); + } } void QLineGraph::clear() @@ -153,7 +164,7 @@ void QLineGraph::updateSlider() slider->setValue(slider->maximum()); } else { slider->setValue(position); - } + } ignoreScroll = false; } @@ -250,7 +261,7 @@ void QLineGraph::paintEvent(QPaintEvent *) p.save(); p.rotate(-90); p.translate(-r.height()/2 - r.width()/2 - graphMargins.right(), -r.height()/2); - p.drawText(r, Qt::AlignCenter, tr("Time per frame (ms)")); + p.drawText(r, Qt::AlignCenter, tr("Frame rate")); p.restore(); p.setBrush(QColor("lightsteelblue")); @@ -310,11 +321,18 @@ void QLineGraph::mouseMoveEvent(QMouseEvent *event) void QLineGraph::leaveEvent(QEvent *) { if (!highlightedBar.isNull()) { + QRect bar = highlightedBar.adjusted(-1, -1, 1, 1); highlightedBar = QRect(); - update(highlightedBar.adjusted(-1, -1, 1, 1)); + update(bar); } } +void QLineGraph::wheelEvent(QWheelEvent *event) +{ + QWheelEvent we(QPoint(0,0), event->delta(), event->buttons(), event->modifiers(), event->orientation()); + QApplication::sendEvent(slider, &we); +} + void QLineGraph::setResolutionForHeight(int resolution) { resolutionForHeight = resolution; @@ -337,6 +355,8 @@ class GraphWindow : public QWidget public: GraphWindow(QWidget *parent = 0); + virtual QSize sizeHint() const; + public slots: void addSample(int, int, int, bool); void setResolutionForHeight(int); @@ -357,13 +377,11 @@ GraphWindow::GraphWindow(QWidget *parent) setFocusProxy(scroll); QVBoxLayout *layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 5, 5); + layout->setContentsMargins(0, 0, 5, 0); layout->setSpacing(0); layout->addWidget(m_graph, 2); layout->addWidget(new QLabel(tr("Total time elapsed (ms)")), 0, Qt::AlignHCenter); layout->addWidget(scroll); - - setMinimumSize(QSize(400, 200)); } void GraphWindow::addSample(int a, int b, int d, bool isBreak) @@ -381,6 +399,11 @@ void GraphWindow::clear() m_graph->clear(); } +QSize GraphWindow::sizeHint() const +{ + return QSize(400, 220); +} + class CanvasFrameRatePlugin : public QmlDebugClient { @@ -454,17 +477,15 @@ CanvasFrameRate::CanvasFrameRate(QWidget *parent) QVBoxLayout *groupLayout = new QVBoxLayout(m_group); groupLayout->setContentsMargins(5, 0, 5, 0); - groupLayout->setSpacing(5); + groupLayout->setSpacing(2); groupLayout->addWidget(m_tabs); groupLayout->addLayout(bottom); QVBoxLayout *layout = new QVBoxLayout; - layout->setContentsMargins(0, 15, 0, 0); + layout->setContentsMargins(0, 10, 0, 0); layout->setSpacing(0); layout->addWidget(m_group); setLayout(layout); - - setFocusPolicy(Qt::StrongFocus); } void CanvasFrameRate::reset(QmlDebugConnection *conn) -- cgit v0.12 From 2fb5889ea55209528fd19b7a34faac7c5be2b1b4 Mon Sep 17 00:00:00 2001 From: Bea Lam Date: Mon, 9 Nov 2009 08:55:59 +1000 Subject: Fix crash when watch is deleted before calling removeWatch(). --- src/declarative/debugger/qmldebug.cpp | 12 +++++++++++- src/declarative/debugger/qmldebug_p.h | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/declarative/debugger/qmldebug.cpp b/src/declarative/debugger/qmldebug.cpp index b06a250..1403ffa 100644 --- a/src/declarative/debugger/qmldebug.cpp +++ b/src/declarative/debugger/qmldebug.cpp @@ -69,6 +69,7 @@ public: void decode(QDataStream &, QmlDebugContextReference &); void decode(QDataStream &, QmlDebugObjectReference &, bool simple); + static void remove(QmlEngineDebug *, QmlDebugEnginesQuery *); static void remove(QmlEngineDebug *, QmlDebugRootContextQuery *); static void remove(QmlEngineDebug *, QmlDebugObjectQuery *); @@ -133,6 +134,7 @@ void QmlEngineDebugPrivate::remove(QmlEngineDebug *c, QmlDebugExpressionQuery *q p->expressionQuery.remove(q->m_queryId); } + Q_DECLARE_METATYPE(QmlDebugObjectReference); void QmlEngineDebugPrivate::decode(QDataStream &ds, QmlDebugObjectReference &o, bool simple) @@ -345,6 +347,7 @@ QmlDebugPropertyWatch *QmlEngineDebug::addWatch(const QmlDebugPropertyReference if (d->client->isConnected()) { int queryId = d->getId(); watch->m_queryId = queryId; + watch->m_client = this; watch->m_objectDebugId = property.objectDebugId(); watch->m_name = property.name(); d->watched.insert(queryId, watch); @@ -373,6 +376,7 @@ QmlDebugObjectExpressionWatch *QmlEngineDebug::addWatch(const QmlDebugObjectRefe if (d->client->isConnected()) { int queryId = d->getId(); watch->m_queryId = queryId; + watch->m_client = this; watch->m_objectDebugId = object.debugId(); watch->m_expr = expr; d->watched.insert(queryId, watch); @@ -395,6 +399,7 @@ QmlDebugWatch *QmlEngineDebug::addWatch(const QmlDebugObjectReference &object, Q if (d->client->isConnected()) { int queryId = d->getId(); watch->m_queryId = queryId; + watch->m_client = this; watch->m_objectDebugId = object.debugId(); d->watched.insert(queryId, watch); @@ -544,8 +549,13 @@ QmlDebugExpressionQuery *QmlEngineDebug::queryExpressionResult(int objectDebugId } QmlDebugWatch::QmlDebugWatch(QObject *parent) -: QObject(parent), m_state(Waiting), m_queryId(-1), m_objectDebugId(-1) +: QObject(parent), m_state(Waiting), m_queryId(-1), m_client(0), m_objectDebugId(-1) +{ +} + +QmlDebugWatch::~QmlDebugWatch() { + m_client->removeWatch(this); } int QmlDebugWatch::queryId() const diff --git a/src/declarative/debugger/qmldebug_p.h b/src/declarative/debugger/qmldebug_p.h index d7e4f5a..4bc54e8 100644 --- a/src/declarative/debugger/qmldebug_p.h +++ b/src/declarative/debugger/qmldebug_p.h @@ -106,6 +106,7 @@ public: enum State { Waiting, Active, Inactive, Dead }; QmlDebugWatch(QObject *); + ~QmlDebugWatch(); int queryId() const; int objectDebugId() const; @@ -125,6 +126,7 @@ private: void setState(State); State m_state; int m_queryId; + QmlEngineDebug *m_client; int m_objectDebugId; }; -- cgit v0.12 From 11bbed0a7cfd4b5295109341c3dc6df339b4df05 Mon Sep 17 00:00:00 2001 From: Bea Lam Date: Mon, 9 Nov 2009 13:07:05 +1000 Subject: QmlContext objects shouldn't be added to object tree. --- src/declarative/qml/qmlenginedebug.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/declarative/qml/qmlenginedebug.cpp b/src/declarative/qml/qmlenginedebug.cpp index 52d54b9..20d6fc1 100644 --- a/src/declarative/qml/qmlenginedebug.cpp +++ b/src/declarative/qml/qmlenginedebug.cpp @@ -99,6 +99,7 @@ QmlEngineDebugServer::QmlObjectProperty QmlEngineDebugServer::propertyData(QObject *obj, int propIdx) { QmlObjectProperty rv; +; QMetaProperty prop = obj->metaObject()->property(propIdx); @@ -165,7 +166,7 @@ void QmlEngineDebugServer::buildObjectDump(QDataStream &message, int childrenCount = children.count(); for (int ii = 0; ii < children.count(); ++ii) { - if (QmlBoundSignal::cast(children[ii])) + if (qobject_cast(children[ii]) || QmlBoundSignal::cast(children[ii])) --childrenCount; } @@ -175,6 +176,8 @@ void QmlEngineDebugServer::buildObjectDump(QDataStream &message, for (int ii = 0; ii < children.count(); ++ii) { QObject *child = children.at(ii); + if (qobject_cast(child)) + continue; QmlBoundSignal *signal = QmlBoundSignal::cast(child); if (signal) { QmlObjectProperty prop; -- cgit v0.12 From 034c9f1b81cfa2d854f87b08e945296d3c1fac85 Mon Sep 17 00:00:00 2001 From: Bea Lam Date: Mon, 9 Nov 2009 13:08:49 +1000 Subject: Initial qmldebug tests. --- tests/auto/declarative/declarative.pro | 1 + tests/auto/declarative/qmldebug/qmldebug.pro | 5 + tests/auto/declarative/qmldebug/tst_qmldebug.cpp | 594 +++++++++++++++++++++++ 3 files changed, 600 insertions(+) create mode 100644 tests/auto/declarative/qmldebug/qmldebug.pro create mode 100644 tests/auto/declarative/qmldebug/tst_qmldebug.cpp diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro index b9f59d6..a31be0b 100644 --- a/tests/auto/declarative/declarative.pro +++ b/tests/auto/declarative/declarative.pro @@ -16,6 +16,7 @@ SUBDIRS += \ qfxwebview \ # Cover qmetaobjectbuilder \ # Cover qmlcontext \ # Cover + qmldebug \ # Cover qmldom \ # Cover qmlecmascript \ # Cover qmlfontloader \ # Cover diff --git a/tests/auto/declarative/qmldebug/qmldebug.pro b/tests/auto/declarative/qmldebug/qmldebug.pro new file mode 100644 index 0000000..61f821e --- /dev/null +++ b/tests/auto/declarative/qmldebug/qmldebug.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +contains(QT_CONFIG,declarative): QT += network declarative +macx:CONFIG -= app_bundle + +SOURCES += tst_qmldebug.cpp diff --git a/tests/auto/declarative/qmldebug/tst_qmldebug.cpp b/tests/auto/declarative/qmldebug/tst_qmldebug.cpp new file mode 100644 index 0000000..a7573da --- /dev/null +++ b/tests/auto/declarative/qmldebug/tst_qmldebug.cpp @@ -0,0 +1,594 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +class tst_QmlDebug : public QObject +{ + Q_OBJECT + +public: + tst_QmlDebug(QmlDebugConnection *conn, QmlEngine *engine, QmlGraphicsItem *rootItem) + : m_conn(conn), m_dbg(0), m_engine(engine), m_rootItem(rootItem) {} + +protected slots: + void saveValueChange(const QByteArray &ba, const QVariant &v) + { + m_savedValueChanges[ba] = v; + } + +private: + QmlDebugConnection *m_conn; + QmlEngineDebug *m_dbg; + QmlEngine *m_engine; + QmlGraphicsItem *m_rootItem; + + QHash m_savedValueChanges; + + void waitForQuery(QmlDebugQuery *query) + { + QCOMPARE(query->parent(), this); + QEventLoop loop; + QTimer timer; + QVERIFY(query->state() == QmlDebugQuery::Waiting); + connect(query, SIGNAL(stateChanged(State)), &loop, SLOT(quit())); + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + timer.start(5000); + loop.exec(); + if (!timer.isActive()) + QFAIL("query timed out"); + } + + void verifyRootObject(const QmlDebugObjectReference &obj) + { + // verifies object according to component definition in main() + + QCOMPARE(obj.debugId(), QmlDebugService::idForObject(m_rootItem)); + QCOMPARE(obj.className(), QLatin1String("Item")); + QCOMPARE(obj.name(), m_rootItem->objectName()); + QCOMPARE(obj.contextDebugId(), QmlDebugService::idForObject(qmlContext(m_rootItem))); + + QmlDebugFileReference source = obj.source(); + QCOMPARE(source.url(), QUrl("file://")); + QCOMPARE(source.lineNumber(), 3); + QCOMPARE(source.columnNumber(), 3); + + QList props = obj.properties(); + QHash expected; + + expected["width"] = 10; + expected["height"] = 20; + verifyProperties(props, expected); + } + + void verifyProperties(const QList &actual, const QHash &expected) + { + foreach(const QmlDebugPropertyReference &p, actual) { + if (expected.contains(p.name())) + QCOMPARE(p.value(), expected[p.name()]); + } + } + + QmlDebugObjectReference findRootObject() + { + QmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + + if (q_engines->engines().count() == 0) + return QmlDebugObjectReference(); + QmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); + waitForQuery(q_context); + + if (q_context->rootContext().objects().count() == 0) + return QmlDebugObjectReference(); + QmlDebugObjectQuery *q_obj = m_dbg->queryObject(q_context->rootContext().objects()[0], this); + waitForQuery(q_obj); + + QmlDebugObjectReference result = q_obj->object(); + + delete q_engines; + delete q_context; + delete q_obj; + + return result; + } + + QmlDebugPropertyReference findProperty(const QList &props, const QString &name) + { + foreach(const QmlDebugPropertyReference &p, props) { + if (p.name() == name) + return p; + } + return QmlDebugPropertyReference(); + } + + int countNotifiableProperties(const QObject *obj) + { + int count = 0; + for (int i=0; imetaObject()->propertyCount(); i++) { + QMetaProperty p = obj->metaObject()->property(i); + if (p.hasNotifySignal()) + count++; + } + return count; + } + +private slots: + void initTestCase(); + + void watch_property(); + void watch_object(); + void watch_expression(); + void watch_expression_data(); + + void queryAvailableEngines(); + void queryRootContexts(); + void queryObject(); + void queryObjectRecursive(); + void queryExpressionResult(); + void queryExpressionResult_data(); +}; + +void tst_QmlDebug::initTestCase() +{ + m_dbg = new QmlEngineDebug(m_conn, this); +} + +void tst_QmlDebug::watch_property() +{ + QmlDebugObjectReference obj = findRootObject(); + QmlDebugPropertyReference prop = findProperty(obj.properties(), "width"); + + QmlDebugPropertyWatch *watch = m_dbg->addWatch(prop, this); + QCOMPARE(watch->state(), QmlDebugWatch::Waiting); + QCOMPARE(watch->objectDebugId(), obj.debugId()); + QCOMPARE(watch->name(), prop.name()); + + QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant))); + QEventLoop loop; + QTimer timer; + connect(watch, SIGNAL(valueChanged(QByteArray,QVariant)), &loop, SLOT(quit())); + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + + int origWidth = m_rootItem->property("width").toInt(); + timer.start(5000); + m_rootItem->setProperty("width", origWidth*2); + loop.exec(); + + if (!timer.isActive()) + QFAIL("Did not receive valueChanged() for property"); + + m_dbg->removeWatch(watch); + delete watch; + + // restore original value and verify spy doesn't get a signal since watch has been removed + m_rootItem->setProperty("width", origWidth); + QTest::qWait(500); + QCOMPARE(spy.count(), 1); + + QCOMPARE(spy.at(0).at(0).value(), prop.name().toUtf8()); + QCOMPARE(spy.at(0).at(1).value(), qVariantFromValue(origWidth*2)); +} + +void tst_QmlDebug::watch_object() +{ + QmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + + QmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); + waitForQuery(q_context); + + QmlDebugObjectQuery *q_obj = m_dbg->queryObject(q_context->rootContext().objects()[0], this); + waitForQuery(q_obj); + + QmlDebugObjectReference obj = q_obj->object(); + + delete q_engines; + delete q_context; + delete q_obj; + + QmlDebugWatch *watch = m_dbg->addWatch(obj, this); + QCOMPARE(watch->state(), QmlDebugWatch::Waiting); + QCOMPARE(watch->objectDebugId(), obj.debugId()); + + m_savedValueChanges.clear(); + connect(watch, SIGNAL(valueChanged(QByteArray,QVariant)), + SLOT(saveValueChange(QByteArray,QVariant))); + + int origWidth = m_rootItem->property("width").toInt(); + int origHeight = m_rootItem->property("height").toInt(); + m_rootItem->setProperty("width", origWidth*2); + m_rootItem->setProperty("height", origHeight*2); + + QEventLoop loop; + QTimer timer; + timer.start(5000); + while (timer.isActive() && + (!m_savedValueChanges.contains("width") || !m_savedValueChanges.contains("height"))) { + loop.processEvents(QEventLoop::AllEvents, 50); + } + + QVariant newWidth = m_savedValueChanges["width"]; + QVariant newHeight = m_savedValueChanges["height"]; + + m_dbg->removeWatch(watch); + delete watch; + + // since watch has been removed, restoring the original values should not trigger a valueChanged() + m_savedValueChanges.clear(); + m_rootItem->setProperty("width", origWidth); + m_rootItem->setProperty("height", origHeight); + QTest::qWait(500); + QCOMPARE(m_savedValueChanges.count(), 0); + + if (newWidth.isNull() || newHeight.isNull()) { + QString s = QString("Did not receive both width and height changes (width=%1, height=%2)") + .arg(newWidth.toString()).arg(newHeight.toString()); + QFAIL(qPrintable(s)); + } + + QCOMPARE(newWidth, qVariantFromValue(origWidth*2)); + QCOMPARE(newHeight, qVariantFromValue(origHeight*2)); +} + +void tst_QmlDebug::watch_expression() +{ + QFETCH(QString, expr); + QFETCH(int, increment); + QFETCH(int, incrementCount); + + int origWidth = m_rootItem->property("width").toInt(); + + QmlDebugObjectReference obj = findRootObject(); + QmlDebugPropertyReference prop; + + QmlDebugObjectExpressionWatch *watch = m_dbg->addWatch(obj, expr, this); + QCOMPARE(watch->state(), QmlDebugWatch::Waiting); + QCOMPARE(watch->objectDebugId(), obj.debugId()); + QCOMPARE(watch->expression(), expr); + + QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant))); + int expectedSpyCount = incrementCount + 1; // should also get signal with expression's initial value + + int width = origWidth; + for (int i=0; i 0) { + width += increment; + m_rootItem->setProperty("width", width); + } + QEventLoop loop; + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + connect(watch, SIGNAL(valueChanged(QByteArray,QVariant)), &loop, SLOT(quit())); + loop.exec(); + if (!timer.isActive()) + QFAIL("Did not receive valueChanged() signal for expression"); + } + + m_dbg->removeWatch(watch); + delete watch; + + // restore original value and verify spy doesn't get a signal since watch has been removed + m_rootItem->setProperty("width", origWidth); // may increase spy count before QCOMPARE() + QTest::qWait(500); + QCOMPARE(spy.count(), expectedSpyCount); + + width = origWidth + increment; + for (int i=0; i().toInt(), width); + width += increment; + } +} + +void tst_QmlDebug::watch_expression_data() +{ + QTest::addColumn("expr"); + QTest::addColumn("increment"); + QTest::addColumn("incrementCount"); + + QTest::newRow("width") << "width" << 0 << 0; + QTest::newRow("width+10") << "width + 10" << 10 << 5; +} + +void tst_QmlDebug::queryAvailableEngines() +{ + QmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + + // TODO have multiple engines + QList engines = q_engines->engines(); + QCOMPARE(engines.count(), 1); + + foreach(const QmlDebugEngineReference &e, engines) { + QCOMPARE(e.debugId(), QmlDebugService::idForObject(m_engine)); + QCOMPARE(e.name(), m_engine->objectName()); + } + + delete q_engines; +} + +void tst_QmlDebug::queryRootContexts() +{ + QmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + + QmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); + waitForQuery(q_context); + + QmlContext *actualContext = m_engine->rootContext(); + QmlDebugContextReference context = q_context->rootContext(); + QCOMPARE(context.debugId(), QmlDebugService::idForObject(actualContext)); + QCOMPARE(context.name(), actualContext->objectName()); + + QCOMPARE(context.objects().count(), 1); + + // root context query sends only root object data - it doesn't fill in + // the children or property info + QCOMPARE(context.objects()[0].properties().count(), 0); + QCOMPARE(context.objects()[0].children().count(), 0); + + // TODO have multiple contexts + QCOMPARE(context.contexts().count(), 0); + + delete q_engines; + delete q_context; +} + +void tst_QmlDebug::queryObject() +{ + QmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + + QmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); + waitForQuery(q_context); + + QmlDebugObjectQuery *q_obj = m_dbg->queryObject(q_context->rootContext().objects()[0], this); + waitForQuery(q_obj); + + QmlDebugObjectReference obj = q_obj->object(); + verifyRootObject(obj); + + QVERIFY(obj.children().count() >= 2); + + // non-recursive query, children data not available + foreach(const QmlDebugObjectReference &child, obj.children()) + QCOMPARE(child.properties().count(), 0); + + delete q_engines; + delete q_context; + delete q_obj; +} + +void tst_QmlDebug::queryObjectRecursive() +{ + QmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + + QmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); + waitForQuery(q_context); + + QmlDebugObjectQuery *q_obj = m_dbg->queryObjectRecursive(q_context->rootContext().objects()[0], this); + waitForQuery(q_obj); + + QmlDebugObjectReference obj = q_obj->object(); + verifyRootObject(obj); + + delete q_engines; + delete q_context; + delete q_obj; + + QList children = obj.children(); + QVERIFY(children.count() >= 2); // there may be additional properties e.g. StateGroup + + QList props; + QHash expected; + + QmlDebugObjectReference rectRef; + QmlDebugObjectReference textRef; + foreach (const QmlDebugObjectReference &child, children) { + if (child.className() == "Rectangle") { + props = child.properties(); + QVERIFY(props.count() > 0); + expected.clear(); + expected["width"] = 500; + expected["height"] = 600; + expected["color"] = "blue"; + verifyProperties(props, expected); + rectRef = child; + } else if (child.className() == "Text") { + props = child.properties(); + QVERIFY(props.count() > 0); + expected.clear(); + expected["color"] = "red"; + verifyProperties(props, expected); + textRef = child; + } + } + + QVERIFY(!rectRef.className().isEmpty()); + QVERIFY(!textRef.className().isEmpty()); + + QObject *rectObj = 0; + QObject *textObj = 0; + foreach (QObject *o, m_rootItem->children()) { + if (o->metaObject()->className() == QmlGraphicsRectangle::staticMetaObject.className()) + rectObj = o; + else if (o->metaObject()->className() == QmlGraphicsText::staticMetaObject.className()) + textObj = o; + } + + QVERIFY(rectObj); + QVERIFY(textObj); + + for (int i=0; imetaObject()->propertyCount(); i++) { + QMetaProperty p = rectObj->metaObject()->property(i); + QmlDebugPropertyReference pd = findProperty(rectRef.properties(), p.name()); + if (!pd.name().isEmpty()) { + QCOMPARE(pd.name(), QString::fromUtf8(p.name())); + if (p.type() < QVariant::UserType) + QCOMPARE(pd.value(), p.read(rectObj)); + if (pd.name() != "parent") + QCOMPARE(pd.valueTypeName(), QString::fromUtf8(p.typeName())); + QCOMPARE(pd.hasNotifySignal(), p.hasNotifySignal()); + } + } +} + +void tst_QmlDebug::queryExpressionResult() +{ + QFETCH(QString, expr); + QFETCH(QVariant, result); + + QmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + + QmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); + waitForQuery(q_context); + + QmlDebugExpressionQuery *q_expr = m_dbg->queryExpressionResult(q_context->rootContext().objects()[0].debugId(), expr, this); + QCOMPARE(q_expr->expression(), expr); + waitForQuery(q_expr); + + QCOMPARE(q_expr->result(), result); + + delete q_engines; + delete q_context; + delete q_expr; +} + +void tst_QmlDebug::queryExpressionResult_data() +{ + QTest::addColumn("expr"); + QTest::addColumn("result"); + + QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60); + QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500); + QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("")); +} + + +class TestRunnerThread : public QThread +{ + Q_OBJECT +public: + void run() { + QTest::qWait(1000); + connectToEngine(); + } + + QPointer m_engine; + QPointer m_item; + +signals: + void testsFinished(); + +public slots: + + void connectToEngine() + { + QmlDebugConnection conn; + conn.connectToHost("127.0.0.1", 3768); + bool ok = conn.waitForConnected(5000); + Q_ASSERT(ok); + while (!m_engine && !m_item) + QTest::qWait(50); + + tst_QmlDebug test(&conn, m_engine, m_item); + QTest::qExec(&test); + emit testsFinished(); + } +}; + + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + qputenv("QML_DEBUG_SERVER_PORT", "3768"); + + TestRunnerThread thread; + QObject::connect(&thread, SIGNAL(testsFinished()), qApp, SLOT(quit())); + thread.start(); + + QmlEngine engine; // blocks until client connects + + QmlComponent component(&engine, + "import Qt 4.6\n" + "\n" // don't remove, line number is tested + " Item {\n" // don't remove spaces, column number is tested + "width: 10; height: 20;\n" + "Rectangle { id: blueRect; width: 500; height: 600; color: \"blue\"; }" + "Text { color: \"red\"; }" + "}\n", + QUrl("file://")); + Q_ASSERT(component.isReady()); + QObject *o = component.create(); + QObject::connect(&thread, SIGNAL(testsFinished()), o, SLOT(deleteLater())); + + // start the test + thread.m_engine = &engine; + thread.m_item = qobject_cast(o); + + return app.exec(); + +} + +//QTEST_MAIN(tst_QmlDebug) + +#include "tst_qmldebug.moc" -- cgit v0.12