diff options
author | Bea Lam <bea.lam@nokia.com> | 2009-10-27 05:43:23 (GMT) |
---|---|---|
committer | Bea Lam <bea.lam@nokia.com> | 2009-10-27 05:43:23 (GMT) |
commit | cc4bca61687f6441984ccc58ad177d24b6d3e92f (patch) | |
tree | c861eadc58164b58a92b6c4a2c67ef16ac7a412c /tools/qmldebugger/standalone | |
parent | b5b6861dd1a82ea9be9b2d2f89ba5cc9dea150ac (diff) | |
download | Qt-cc4bca61687f6441984ccc58ad177d24b6d3e92f.zip Qt-cc4bca61687f6441984ccc58ad177d24b6d3e92f.tar.gz Qt-cc4bca61687f6441984ccc58ad177d24b6d3e92f.tar.bz2 |
Move qmldebugger/ into qmldebugger/standalone dir.
Diffstat (limited to 'tools/qmldebugger/standalone')
21 files changed, 2157 insertions, 0 deletions
diff --git a/tools/qmldebugger/standalone/canvasframerate.cpp b/tools/qmldebugger/standalone/canvasframerate.cpp new file mode 100644 index 0000000..10c3dbf --- /dev/null +++ b/tools/qmldebugger/standalone/canvasframerate.cpp @@ -0,0 +1,368 @@ +#include "canvasframerate.h" +#include <QtGui/qwidget.h> +#include <QtGui/qpainter.h> +#include <QtGui/qscrollbar.h> +#include <QtDeclarative/qmldebugclient.h> +#include <QtCore/qdebug.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdatastream.h> +#include <QtGui/qboxlayout.h> +#include <QResizeEvent> +#include <QShowEvent> +#include <QTabWidget> +#include <QPushButton> +#include <QLineEdit> +#include <QCheckBox> +#include <QSpinBox> +#include <QLabel> + +QT_BEGIN_NAMESPACE + +class QLineGraph : public QWidget +{ +Q_OBJECT +public: + QLineGraph(QWidget * = 0); + + void setPosition(int); + +public slots: + void addSample(int, int, int, bool); + void setResolutionForHeight(int); + +protected: + virtual void paintEvent(QPaintEvent *); + +private slots: + void scrollbarChanged(int); + +private: + void updateScrollbar(); + void drawSample(QPainter *, int, const QRect &); + void drawTime(QPainter *, const QRect &); + struct Sample { + int sample[3]; + bool isBreak; + }; + QList<Sample> _samples; + + QScrollBar sb; + 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) +{ + 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))); +} + +void QLineGraph::scrollbarChanged(int v) +{ + if(ignoreScroll) + return; + + if (v == sb.maximum()) + position = -1; + else + position = v; + update(); +} + +void QLineGraph::updateScrollbar() +{ + ignoreScroll = true; + sb.setMaximum(qMax(0, _samples.count() - samplesPerWidth - 1)); + + if(position == -1) { + sb.setValue(sb.maximum()); + } else { + sb.setValue(position); + } + ignoreScroll = false; +} + +void QLineGraph::addSample(int a, int b, int d, bool isBreak) +{ + Sample s; + s.isBreak = isBreak; + s.sample[0] = a; + s.sample[1] = b; + s.sample[2] = d; + _samples << s; + updateScrollbar(); + update(); +} + +void QLineGraph::setPosition(int p) +{ + scrollbarChanged(p); +} + +void QLineGraph::drawTime(QPainter *p, const QRect &rect) +{ + if(_samples.isEmpty()) + return; + + int first = position; + if(first == -1) + first = qMax(0, _samples.count() - samplesPerWidth - 1); + int last = qMin(_samples.count() - 1, first + samplesPerWidth); + + qreal scaleX = qreal(rect.width()) / qreal(samplesPerWidth); + + int t = 0; + + for(int ii = first; ii <= last; ++ii) { + int sampleTime = _samples.at(ii).sample[2] / 1000; + if(sampleTime != t) { + + int xEnd = rect.left() + scaleX * (ii - first); + p->drawLine(xEnd, rect.bottom(), xEnd, rect.bottom() + 7); + + QRect text(xEnd - 30, rect.bottom() + 10, 60, 30); + + p->drawText(text, Qt::AlignHCenter | Qt::AlignTop, QString::number(_samples.at(ii).sample[2])); + + t = sampleTime; + } + } + +} + +void QLineGraph::drawSample(QPainter *p, int s, const QRect &rect) +{ + if(_samples.isEmpty()) + return; + + int first = position; + if(first == -1) + first = qMax(0, _samples.count() - samplesPerWidth - 1); + int last = qMin(_samples.count() - 1, first + samplesPerWidth); + + qreal scaleY = qreal(rect.height()) / resolutionForHeight; + qreal scaleX = qreal(rect.width()) / qreal(samplesPerWidth); + + int xEnd; + int lastXEnd = rect.left(); + + p->save(); + p->setPen(Qt::NoPen); + for(int ii = first + 1; ii <= last; ++ii) { + + 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)); + + lastXEnd = xEnd; + } + p->restore(); +} + +void QLineGraph::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + QRect r(50, 10, width() - 60, height() - 60); + p.setBrush(QColor("lightsteelblue")); + drawSample(&p, 0, r); + + p.setBrush(QColor("pink")); + drawSample(&p, 1, r); + + p.setBrush(Qt::NoBrush); + p.drawRect(r); + + for(int ii = 0; ii <= resolutionForHeight; ++ii) { + int y = 1 + r.bottom() - ii * r.height() / resolutionForHeight; + + if((ii % 10) == 0) { + p.drawLine(r.left() - 20, y, r.left(), y); + QRect text(r.left() - 20 - 53, y - 10, 50, 20); + p.drawText(text, Qt::AlignRight | Qt::AlignVCenter, QString::number(ii)); + } else { + p.drawLine(r.left() - 7, y, r.left(), y); + } + } + + drawTime(&p, r); +} + +void QLineGraph::setResolutionForHeight(int resolution) +{ + resolutionForHeight = resolution; + update(); +} + +class CanvasFrameRatePlugin : public QmlDebugClient +{ +Q_OBJECT +public: + CanvasFrameRatePlugin(QmlDebugConnection *client); + +signals: + void sample(int, int, int, bool); + +protected: + virtual void messageReceived(const QByteArray &); + +private: + int lb; + int ld; +}; + +CanvasFrameRatePlugin::CanvasFrameRatePlugin(QmlDebugConnection *client) +: QmlDebugClient(QLatin1String("CanvasFrameRate"), client), lb(-1) +{ +} + +void CanvasFrameRatePlugin::messageReceived(const QByteArray &data) +{ + QByteArray rwData = data; + QDataStream stream(&rwData, QIODevice::ReadOnly); + + int b; int c; int d; bool isBreak; + stream >> b >> c >> d >> isBreak; + + if (lb != -1) + emit sample(c, lb, ld, isBreak); + + lb = b; + ld = d; +} + +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->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); + + bottom->addStretch(2); + + m_enabledCheckBox = new QCheckBox("Enable", this); + bottom->addWidget(m_enabledCheckBox); + QObject::connect(m_enabledCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(enabledStateChanged(int))); + + QPushButton *pb = new QPushButton(tr("New Tab"), this); + QObject::connect(pb, SIGNAL(clicked()), this, SLOT(newTab())); + bottom->addWidget(pb); +} + +void CanvasFrameRate::reset(QmlDebugConnection *conn) +{ + delete m_plugin; + m_plugin = 0; + + QWidget *w; + for (int i=0; i<m_tabs->count(); i++) { + w = m_tabs->widget(i); + m_tabs->removeTab(i); + delete w; + } + + if (conn) { + connect(conn, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + SLOT(connectionStateChanged(QAbstractSocket::SocketState))); + if (conn->state() == QAbstractSocket::ConnectedState) + handleConnected(conn); + } +} + +void CanvasFrameRate::connectionStateChanged(QAbstractSocket::SocketState state) +{ + if (state == QAbstractSocket::UnconnectedState) { + delete m_plugin; + m_plugin = 0; + } else if (state == QAbstractSocket::ConnectedState) { + handleConnected(qobject_cast<QmlDebugConnection*>(sender())); + } +} + +void CanvasFrameRate::handleConnected(QmlDebugConnection *conn) +{ + delete m_plugin; + m_plugin = new CanvasFrameRatePlugin(conn); + enabledStateChanged(m_enabledCheckBox->checkState()); + newTab(); +} + +void CanvasFrameRate::setSizeHint(const QSize &size) +{ + m_sizeHint = size; +} + +QSize CanvasFrameRate::sizeHint() const +{ + return m_sizeHint; +} + +void CanvasFrameRate::newTab() +{ + if (!m_plugin) + return; + + if (m_tabs->count()) { + QWidget *w = m_tabs->widget(m_tabs->count() - 1); + QObject::disconnect(m_plugin, SIGNAL(sample(int,int,int,bool)), + w, SLOT(addSample(int,int,int,bool))); + } + + 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))); + + QString name = QLatin1String("Graph ") + QString::number(id); + m_tabs->addTab(graph, name); + m_tabs->setCurrentIndex(id); +} + +void CanvasFrameRate::enabledStateChanged(int s) +{ + bool checked = s != 0; + + if (m_plugin) + static_cast<QmlDebugClient *>(m_plugin)->setEnabled(checked); +} + +QT_END_NAMESPACE + +#include "canvasframerate.moc" diff --git a/tools/qmldebugger/standalone/canvasframerate.h b/tools/qmldebugger/standalone/canvasframerate.h new file mode 100644 index 0000000..6c1ff9f --- /dev/null +++ b/tools/qmldebugger/standalone/canvasframerate.h @@ -0,0 +1,46 @@ +#ifndef CANVASFRAMERATE_H +#define CANVASFRAMERATE_H + +#include <QtCore/qpointer.h> +#include <QtGui/qwidget.h> + +#include <QtDeclarative/qmldebugclient.h> + +QT_BEGIN_NAMESPACE + +class QTabWidget; +class QSpinBox; +class QCheckBox; + +class CanvasFrameRatePlugin; + +class CanvasFrameRate : public QWidget +{ + Q_OBJECT +public: + CanvasFrameRate(QWidget *parent = 0); + + void reset(QmlDebugConnection *conn); + + void setSizeHint(const QSize &); + virtual QSize sizeHint() const; + +private slots: + void newTab(); + void enabledStateChanged(int); + void connectionStateChanged(QAbstractSocket::SocketState state); + +private: + void handleConnected(QmlDebugConnection *conn); + + QTabWidget *m_tabs; + QSpinBox *m_spin; + CanvasFrameRatePlugin *m_plugin; + QSize m_sizeHint; + QCheckBox *m_enabledCheckBox; +}; + +QT_END_NAMESPACE + +#endif // CANVASFRAMERATE_H + diff --git a/tools/qmldebugger/standalone/engine.cpp b/tools/qmldebugger/standalone/engine.cpp new file mode 100644 index 0000000..a1fd009 --- /dev/null +++ b/tools/qmldebugger/standalone/engine.cpp @@ -0,0 +1,179 @@ +#include <QVBoxLayout> +#include <QHBoxLayout> +#include <QSplitter> +#include <QTabWidget> +#include <QFile> + +#include <private/qmlenginedebug_p.h> +#include <QtDeclarative/qmldebugclient.h> +#include <QtDeclarative/qmlcomponent.h> +#include <QtDeclarative/qfxitem.h> +#include <QtDeclarative/qmldebugservice.h> + +#include "engine.h" +#include "objectpropertiesview.h" +#include "expressionquerywidget.h" +#include "objecttree.h" +#include "watchtable.h" + +QT_BEGIN_NAMESPACE + + +class DebuggerEngineItem : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name CONSTANT); + Q_PROPERTY(int engineId READ engineId CONSTANT); + +public: + DebuggerEngineItem(const QString &name, int id) + : m_name(name), m_engineId(id) {} + + QString name() const { return m_name; } + int engineId() const { return m_engineId; } + +private: + QString m_name; + int m_engineId; +}; + +EnginePane::EnginePane(QmlDebugConnection *conn, QWidget *parent) +: QWidget(parent), m_client(new QmlEngineDebug(conn, this)), m_engines(0), m_context(0), m_watchTableModel(0), m_exprQueryWidget(0) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + + QFile enginesFile(":/engines.qml"); + enginesFile.open(QFile::ReadOnly); + Q_ASSERT(enginesFile.isOpen()); + + m_engineView = new QmlView(this); + m_engineView->rootContext()->setContextProperty("engines", qVariantFromValue(&m_engineItems)); + m_engineView->setContentResizable(true); + m_engineView->setQml(enginesFile.readAll()); + m_engineView->execute(); + m_engineView->setFixedHeight(100); + QObject::connect(m_engineView->root(), SIGNAL(engineClicked(int)), + this, SLOT(engineSelected(int))); + QObject::connect(m_engineView->root(), SIGNAL(refreshEngines()), + this, SLOT(refreshEngines())); + + m_engineView->setVisible(false); + layout->addWidget(m_engineView); + + QSplitter *splitter = new QSplitter; + + m_objTree = new ObjectTree(m_client, this); + m_propertiesView = new ObjectPropertiesView(m_client); + m_watchTableModel = new WatchTableModel(m_client, this); + + m_watchTableView = new WatchTableView(m_watchTableModel); + m_watchTableView->setModel(m_watchTableModel); + WatchTableHeaderView *header = new WatchTableHeaderView(m_watchTableModel); + m_watchTableView->setHorizontalHeader(header); + + connect(m_objTree, SIGNAL(currentObjectChanged(QmlDebugObjectReference)), + m_propertiesView, SLOT(reload(QmlDebugObjectReference))); + connect(m_objTree, SIGNAL(expressionWatchRequested(QmlDebugObjectReference,QString)), + m_watchTableModel, SLOT(expressionWatchRequested(QmlDebugObjectReference,QString))); + + connect(m_propertiesView, SIGNAL(activated(QmlDebugObjectReference,QmlDebugPropertyReference)), + m_watchTableModel, SLOT(togglePropertyWatch(QmlDebugObjectReference,QmlDebugPropertyReference))); + + connect(m_watchTableModel, SIGNAL(watchCreated(QmlDebugWatch*)), + m_propertiesView, SLOT(watchCreated(QmlDebugWatch*))); + + connect(m_watchTableView, SIGNAL(objectActivated(int)), + m_objTree, SLOT(setCurrentObject(int))); + + m_exprQueryWidget = new ExpressionQueryWidget(m_client); + connect(m_objTree, SIGNAL(currentObjectChanged(QmlDebugObjectReference)), + m_exprQueryWidget, SLOT(setCurrentObject(QmlDebugObjectReference))); + + QSplitter *propertiesTab = new QSplitter(Qt::Vertical); + propertiesTab->addWidget(m_propertiesView); + propertiesTab->addWidget(m_exprQueryWidget); + propertiesTab->setStretchFactor(0, 2); + propertiesTab->setStretchFactor(1, 1); + + m_tabs = new QTabWidget(this); + m_tabs->addTab(propertiesTab, tr("Properties")); + m_tabs->addTab(m_watchTableView, tr("Watched")); + + splitter->addWidget(m_objTree); + splitter->addWidget(m_tabs); + splitter->setStretchFactor(1, 2); + layout->addWidget(splitter); +} + +void EnginePane::engineSelected(int id) +{ + qWarning() << "Engine selected" << id; + queryContext(id); +} + +void EnginePane::queryContext(int id) +{ + if (m_context) { + delete m_context; + m_context = 0; + } + + m_context = m_client->queryRootContexts(QmlDebugEngineReference(id), this); + if (!m_context->isWaiting()) + contextChanged(); + else + QObject::connect(m_context, SIGNAL(stateChanged(State)), + this, SLOT(contextChanged())); +} + +void EnginePane::contextChanged() +{ + //dump(m_context->rootContext(), 0); + + foreach (const QmlDebugObjectReference &object, m_context->rootContext().objects()) + m_objTree->reload(object.debugId()); + + delete m_context; m_context = 0; +} + +void EnginePane::refreshEngines() +{ + if (m_engines) + return; + + m_engines = m_client->queryAvailableEngines(this); + if (!m_engines->isWaiting()) + enginesChanged(); + else + QObject::connect(m_engines, SIGNAL(stateChanged(State)), + this, SLOT(enginesChanged())); +} + +void EnginePane::enginesChanged() +{ + qDeleteAll(m_engineItems); + m_engineItems.clear(); + + QList<QmlDebugEngineReference> engines = m_engines->engines(); + delete m_engines; m_engines = 0; + + if (engines.isEmpty()) + qWarning("qmldebugger: no engines found!"); + + for (int ii = 0; ii < engines.count(); ++ii) + m_engineItems << new DebuggerEngineItem(engines.at(ii).name(), + engines.at(ii).debugId()); + + m_engineView->rootContext()->setContextProperty("engines", qVariantFromValue(&m_engineItems)); + + m_engineView->setVisible(m_engineItems.count() > 1); + if (m_engineItems.count() == 1) + engineSelected(qobject_cast<DebuggerEngineItem*>(m_engineItems.at(0))->engineId()); +} + + +#include "engine.moc" + +QT_END_NAMESPACE + diff --git a/tools/qmldebugger/standalone/engine.h b/tools/qmldebugger/standalone/engine.h new file mode 100644 index 0000000..a3ebe46 --- /dev/null +++ b/tools/qmldebugger/standalone/engine.h @@ -0,0 +1,61 @@ +#ifndef ENGINE_H +#define ENGINE_H + +#include <QWidget> +#include <QtCore/qpointer.h> +#include <QtDeclarative/qmlengine.h> +#include <QtDeclarative/qmlcontext.h> +#include <QtDeclarative/qmlview.h> +#include <QtDeclarative/qmldebug.h> + +QT_BEGIN_NAMESPACE + +class ObjectPropertiesView; +class QmlDebugConnection; +class QmlDebugPropertyReference; +class QmlDebugWatch; +class ObjectTree; +class WatchTableModel; +class WatchTableView; +class ExpressionQueryWidget; + +class QTabWidget; + +class EnginePane : public QWidget +{ +Q_OBJECT +public: + EnginePane(QmlDebugConnection *, QWidget *parent = 0); + +public slots: + void refreshEngines(); + +private slots: + void enginesChanged(); + + void queryContext(int); + void contextChanged(); + + void engineSelected(int); + +private: + QmlEngineDebug *m_client; + QmlDebugEnginesQuery *m_engines; + QmlDebugRootContextQuery *m_context; + + ObjectTree *m_objTree; + QTabWidget *m_tabs; + WatchTableView *m_watchTableView; + WatchTableModel *m_watchTableModel; + ExpressionQueryWidget *m_exprQueryWidget; + + QmlView *m_engineView; + QList<QObject *> m_engineItems; + + ObjectPropertiesView *m_propertiesView; +}; + +QT_END_NAMESPACE + +#endif // ENGINE_H + diff --git a/tools/qmldebugger/standalone/engine.png b/tools/qmldebugger/standalone/engine.png Binary files differnew file mode 100644 index 0000000..a0a8a04 --- /dev/null +++ b/tools/qmldebugger/standalone/engine.png diff --git a/tools/qmldebugger/standalone/engines.qml b/tools/qmldebugger/standalone/engines.qml new file mode 100644 index 0000000..1e9335b --- /dev/null +++ b/tools/qmldebugger/standalone/engines.qml @@ -0,0 +1,46 @@ +import Qt 4.6 + +Item { + height: 100 + id: Root + signal engineClicked(int id) + signal refreshEngines() + + Row { + anchors.fill: parent + Repeater { + model: engines + Item { + width: 100; height: 100; + Image { + id: EngineIcon; + source: "qrc:/engine.png" + anchors.horizontalCenter: parent.horizontalCenter + } + Text { + anchors.top: EngineIcon.bottom; + text: modelData.name + "(" + modelData.engineId + ")" + anchors.horizontalCenter: parent.horizontalCenter + } + MouseRegion { + anchors.fill: parent + onClicked: Root.engineClicked(modelData.engineId); + } + } + } + } + + + Image { + y: 15 + source: "qrc:/refresh.png"; + width: 75; + height: 63; + smooth: true + anchors.right: parent.right + MouseRegion { + anchors.fill: parent + onClicked: Root.refreshEngines() + } + } +} diff --git a/tools/qmldebugger/standalone/expressionquerywidget.cpp b/tools/qmldebugger/standalone/expressionquerywidget.cpp new file mode 100644 index 0000000..f8f5aef --- /dev/null +++ b/tools/qmldebugger/standalone/expressionquerywidget.cpp @@ -0,0 +1,218 @@ +#include <QtCore/qdebug.h> + +#include <QtGui/qlabel.h> +#include <QtGui/qtextedit.h> +#include <QtGui/qlineedit.h> +#include <QtGui/qpushbutton.h> +#include <QtGui/qevent.h> +#include <QtGui/qgroupbox.h> +#include <QtGui/qtextobject.h> +#include <QtGui/qlayout.h> + +#include "expressionquerywidget.h" + +ExpressionQueryWidget::ExpressionQueryWidget(QmlEngineDebug *client, QWidget *parent) + : QWidget(parent), + m_style(Compact), + m_client(client), + m_query(0), + m_textEdit(new QTextEdit), + m_lineEdit(0) +{ + m_prompt = QLatin1String(">> "); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(m_textEdit); + + updateTitle(); + + if (m_style == Compact) { + m_lineEdit = new QLineEdit; + connect(m_lineEdit, SIGNAL(returnPressed()), SLOT(executeExpression())); + QHBoxLayout *hbox = new QHBoxLayout; + hbox->setMargin(5); + hbox->setSpacing(5); + hbox->addWidget(new QLabel(tr("Expression:"))); + hbox->addWidget(m_lineEdit); + layout->addLayout(hbox); + + m_textEdit->setReadOnly(true); + m_lineEdit->installEventFilter(this); + } else { + m_textEdit->installEventFilter(this); + } +} + +void ExpressionQueryWidget::setEngineDebug(QmlEngineDebug *client) +{ + m_client = client; +} + +void ExpressionQueryWidget::clear() +{ + m_textEdit->clear(); + m_lineEdit->clear(); +} + +void ExpressionQueryWidget::updateTitle() +{ + if (m_currObject.debugId() < 0) { + m_title = tr("Expression queries"); + } else { + QString desc = QLatin1String("<") + + m_currObject.className() + QLatin1String(": ") + + (m_currObject.name().isEmpty() ? QLatin1String("<unnamed>") : m_currObject.name()) + + QLatin1String(">"); + m_title = tr("Expression queries (using context for %1)" , "Selected object").arg(desc); + } +} + +void ExpressionQueryWidget::appendPrompt() +{ + m_textEdit->moveCursor(QTextCursor::End); + + if (m_style == Compact) { + m_textEdit->insertPlainText("\n"); + } else { + m_textEdit->setTextColor(Qt::gray); + m_textEdit->append(m_prompt); + } +} + +void ExpressionQueryWidget::setCurrentObject(const QmlDebugObjectReference &obj) +{ + m_currObject = obj; + updateTitle(); +} + +void ExpressionQueryWidget::checkCurrentContext() +{ + m_textEdit->moveCursor(QTextCursor::End); + + if (m_currObject.debugId() != -1 && m_currObject.debugId() != m_objectAtLastFocus.debugId()) + showCurrentContext(); + m_objectAtLastFocus = m_currObject; +} + +void ExpressionQueryWidget::showCurrentContext() +{ + m_textEdit->moveCursor(QTextCursor::End); + m_textEdit->setTextColor(Qt::darkGreen); + m_textEdit->append(m_currObject.className() + + QLatin1String(": ") + + (m_currObject.name().isEmpty() ? QLatin1String("<unnamed>") : m_currObject.name())); + appendPrompt(); +} + +void ExpressionQueryWidget::executeExpression() +{ + if (!m_client) + return; + + if (m_style == Compact) + m_expr = m_lineEdit->text().trimmed(); + else + m_expr = m_expr.trimmed(); + + if (!m_expr.isEmpty() && m_currObject.debugId() != -1) { + if (m_query) + delete m_query; + m_query = m_client->queryExpressionResult(m_currObject.debugId(), m_expr, this); + if (!m_query->isWaiting()) + showResult(); + else + QObject::connect(m_query, SIGNAL(stateChanged(State)), + this, SLOT(showResult())); + + m_lastExpr = m_expr; + if (m_lineEdit) + m_lineEdit->clear(); + } +} + +void ExpressionQueryWidget::showResult() +{ + if (m_query) { + m_textEdit->moveCursor(QTextCursor::End); + QString result; + if (m_query->result().isNull()) + result = QLatin1String("<no value>"); + else + result = m_query->result().toString(); + + if (m_style == Compact) { + m_textEdit->setTextColor(Qt::black); + m_textEdit->setFontWeight(QFont::Bold); + m_textEdit->insertPlainText(m_expr + " : "); + m_textEdit->setFontWeight(QFont::Normal); + m_textEdit->insertPlainText(result); + } else { + m_textEdit->append(result); + } + appendPrompt(); + m_expr.clear(); + } +} + +bool ExpressionQueryWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == m_textEdit) { + switch (event->type()) { + case QEvent::KeyPress: + { + QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); + int key = keyEvent->key(); + if (key == Qt::Key_Return || key == Qt::Key_Enter) { + executeExpression(); + return true; + } else if (key == Qt::Key_Backspace) { + // ensure m_expr doesn't contain backspace characters + QTextCursor cursor = m_textEdit->textCursor(); + bool atLastLine = !(cursor.block().next().isValid()); + if (!atLastLine) + return true; + if (cursor.columnNumber() <= m_prompt.count()) + return true; + cursor.deletePreviousChar(); + m_expr = cursor.block().text().mid(m_prompt.count()); + return true; + } else { + m_textEdit->moveCursor(QTextCursor::End); + m_textEdit->setTextColor(Qt::black); + m_expr += keyEvent->text(); + } + break; + } + case QEvent::FocusIn: + checkCurrentContext(); + m_textEdit->moveCursor(QTextCursor::End); + break; + default: + break; + } + } else if (obj == m_lineEdit) { + switch (event->type()) { + case QEvent::KeyPress: + { + QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); + int key = keyEvent->key(); + if (key == Qt::Key_Up && m_lineEdit->text() != m_lastExpr) { + m_expr = m_lineEdit->text(); + if (!m_lastExpr.isEmpty()) + m_lineEdit->setText(m_lastExpr); + } else if (key == Qt::Key_Down) { + m_lineEdit->setText(m_expr); + } + break; + } + case QEvent::FocusIn: + checkCurrentContext(); + break; + default: + break; + } + } + return QWidget::eventFilter(obj, event); +} diff --git a/tools/qmldebugger/standalone/expressionquerywidget.h b/tools/qmldebugger/standalone/expressionquerywidget.h new file mode 100644 index 0000000..6fab059 --- /dev/null +++ b/tools/qmldebugger/standalone/expressionquerywidget.h @@ -0,0 +1,65 @@ +#ifndef EXPRESSIONQUERYWIDGET_H +#define EXPRESSIONQUERYWIDGET_H + +#include <QWidget> + +#include <QtDeclarative/qmldebug.h> + +QT_BEGIN_NAMESPACE + +class QGroupBox; +class QTextEdit; +class QLineEdit; +class QPushButton; + +class ExpressionQueryWidget : public QWidget +{ + Q_OBJECT +public: + enum Style { + Compact, + Shell + }; + + ExpressionQueryWidget(QmlEngineDebug *client = 0, QWidget *parent = 0); + + void setEngineDebug(QmlEngineDebug *client); + void clear(); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +public slots: + void setCurrentObject(const QmlDebugObjectReference &obj); + +private slots: + void executeExpression(); + void showResult(); + +private: + void appendPrompt(); + void checkCurrentContext(); + void showCurrentContext(); + void updateTitle(); + + Style m_style; + + QmlEngineDebug *m_client; + QmlDebugExpressionQuery *m_query; + QTextEdit *m_textEdit; + QLineEdit *m_lineEdit; + QPushButton *m_button; + QString m_prompt; + QString m_expr; + QString m_lastExpr; + + QString m_title; + + QmlDebugObjectReference m_currObject; + QmlDebugObjectReference m_objectAtLastFocus; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/tools/qmldebugger/standalone/main.cpp b/tools/qmldebugger/standalone/main.cpp new file mode 100644 index 0000000..c9983cd --- /dev/null +++ b/tools/qmldebugger/standalone/main.cpp @@ -0,0 +1,34 @@ +#include <QtGui/qapplication.h> + +#include "qmldebugger.h" + +int main(int argc, char ** argv) +{ + QApplication app(argc, argv); + + QStringList args = app.arguments(); + + QmlDebugger win; + if (args.contains("--engine")) + win.showEngineTab(); + + for (int i=0; i<args.count(); i++) { + if (!args[i].contains(':')) + continue; + QStringList hostAndPort = args[i].split(':'); + bool ok = false; + quint16 port = hostAndPort.value(1).toInt(&ok); + if (ok) { + qWarning() << "qmldebugger connecting to" + << hostAndPort[0] << port << "..."; + win.setHost(hostAndPort[0]); + win.setPort(port); + win.connectToHost(); + break; + } + } + + win.show(); + + return app.exec(); +} diff --git a/tools/qmldebugger/standalone/objectpropertiesview.cpp b/tools/qmldebugger/standalone/objectpropertiesview.cpp new file mode 100644 index 0000000..ae9e8be --- /dev/null +++ b/tools/qmldebugger/standalone/objectpropertiesview.cpp @@ -0,0 +1,197 @@ +#include <QtCore/qdebug.h> + +#include <QtGui/qtreewidget.h> +#include <QtGui/qlayout.h> + +#include <QtDeclarative/qmldebugservice.h> +#include <QtDeclarative/qmldebug.h> +#include <QtDeclarative/qmldebugclient.h> + +#include "objectpropertiesview.h" + +QT_BEGIN_NAMESPACE + +class PropertiesViewItem : public QObject, public QTreeWidgetItem +{ + Q_OBJECT +public: + PropertiesViewItem(QTreeWidget *widget); + PropertiesViewItem(QTreeWidgetItem *parent); + + QmlDebugPropertyReference property; +}; + +PropertiesViewItem::PropertiesViewItem(QTreeWidget *widget) + : QTreeWidgetItem(widget) +{ +} + +PropertiesViewItem::PropertiesViewItem(QTreeWidgetItem *parent) + : QTreeWidgetItem(parent) +{ +} + +ObjectPropertiesView::ObjectPropertiesView(QmlEngineDebug *client, QWidget *parent) + : QWidget(parent), + m_client(client), + m_query(0), + m_watch(0) +{ + QVBoxLayout *layout = new QVBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + setLayout(layout); + + m_tree = new QTreeWidget(this); + m_tree->setAlternatingRowColors(true); + m_tree->setExpandsOnDoubleClick(false); + m_tree->setHeaderLabels(QStringList() << tr("Property") << tr("Value")); + QObject::connect(m_tree, SIGNAL(itemActivated(QTreeWidgetItem *, int)), + this, SLOT(itemActivated(QTreeWidgetItem *))); + + m_tree->setColumnCount(2); + + layout->addWidget(m_tree); +} + +void ObjectPropertiesView::setEngineDebug(QmlEngineDebug *client) +{ + m_client = client; +} + +void ObjectPropertiesView::clear() +{ + setObject(QmlDebugObjectReference()); +} + +void ObjectPropertiesView::reload(const QmlDebugObjectReference &obj) +{ + if (!m_client) + return; + if (m_query) + delete m_query; + + m_query = m_client->queryObjectRecursive(obj, this); + if (!m_query->isWaiting()) + queryFinished(); + else + QObject::connect(m_query, SIGNAL(stateChanged(State)), + this, SLOT(queryFinished())); +} + +void ObjectPropertiesView::queryFinished() +{ + if (!m_client || !m_query) + return; + + QmlDebugObjectReference obj = m_query->object(); + + QmlDebugWatch *watch = m_client->addWatch(obj, this); + if (watch->state() == QmlDebugWatch::Dead) { + delete watch; + watch = 0; + } else { + if (m_watch) { + m_client->removeWatch(m_watch); + delete m_watch; + } + m_watch = watch; + QObject::connect(watch, SIGNAL(valueChanged(QByteArray,QVariant)), + this, SLOT(valueChanged(QByteArray,QVariant))); + } + + delete m_query; + m_query = 0; + + setObject(obj); +} + +void ObjectPropertiesView::setObject(const QmlDebugObjectReference &object) +{ + m_object = object; + m_tree->clear(); + + QList<QmlDebugPropertyReference> properties = object.properties(); + for (int i=0; i<properties.count(); i++) { + const QmlDebugPropertyReference &p = properties[i]; + + PropertiesViewItem *item = new PropertiesViewItem(m_tree); + item->property = p; + + item->setText(0, p.name()); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + if (!p.hasNotifySignal()) { + item->setForeground(0, Qt::gray); + item->setForeground(1, Qt::gray); + } + + if (!p.binding().isEmpty()) { + PropertiesViewItem *binding = new PropertiesViewItem(item); + binding->setText(1, p.binding()); + binding->setForeground(1, Qt::darkGreen); + } + + item->setExpanded(true); + } + + m_tree->resizeColumnToContents(0); +} + +void ObjectPropertiesView::watchCreated(QmlDebugWatch *watch) +{ + if (watch->objectDebugId() == m_object.debugId() + && qobject_cast<QmlDebugPropertyWatch*>(watch)) { + connect(watch, SIGNAL(stateChanged(State)), SLOT(watchStateChanged())); + setWatched(qobject_cast<QmlDebugPropertyWatch*>(watch)->name(), true); + } +} + +void ObjectPropertiesView::watchStateChanged() +{ + QmlDebugWatch *watch = qobject_cast<QmlDebugWatch*>(sender()); + + if (watch->objectDebugId() == m_object.debugId() + && qobject_cast<QmlDebugPropertyWatch*>(watch) + && watch->state() == QmlDebugWatch::Inactive) { + setWatched(qobject_cast<QmlDebugPropertyWatch*>(watch)->name(), false); + } +} + +void ObjectPropertiesView::setWatched(const QString &property, bool watched) +{ + for (int i=0; i<m_tree->topLevelItemCount(); i++) { + PropertiesViewItem *item = static_cast<PropertiesViewItem *>(m_tree->topLevelItem(i)); + if (item->property.name() == property && item->property.hasNotifySignal()) { + QFont font = m_tree->font(); + font.setBold(watched); + item->setFont(0, font); + } + } +} + +void ObjectPropertiesView::valueChanged(const QByteArray &name, const QVariant &value) +{ + for (int i=0; i<m_tree->topLevelItemCount(); i++) { + PropertiesViewItem *item = static_cast<PropertiesViewItem *>(m_tree->topLevelItem(i)); + if (item->property.name() == name) { + if (value.isNull()) { + item->setText(1, QLatin1String("<null>") + + QLatin1String(" : ") + + item->property.valueTypeName()); + } else { + item->setText(1, value.toString()); + } + } + } +} + +void ObjectPropertiesView::itemActivated(QTreeWidgetItem *i) +{ + PropertiesViewItem *item = static_cast<PropertiesViewItem *>(i); + if (!item->property.name().isEmpty()) + emit activated(m_object, item->property); +} + +QT_END_NAMESPACE + +#include "objectpropertiesview.moc" diff --git a/tools/qmldebugger/standalone/objectpropertiesview.h b/tools/qmldebugger/standalone/objectpropertiesview.h new file mode 100644 index 0000000..6a1fc03 --- /dev/null +++ b/tools/qmldebugger/standalone/objectpropertiesview.h @@ -0,0 +1,51 @@ +#ifndef PROPERTIESTABLEMODEL_H +#define PROPERTIESTABLEMODEL_H + +#include <QtDeclarative/qmldebug.h> + +#include <QtGui/qwidget.h> + +QT_BEGIN_NAMESPACE + +class QTreeWidget; +class QTreeWidgetItem; +class QmlDebugConnection; + +class ObjectPropertiesView : public QWidget +{ + Q_OBJECT +public: + ObjectPropertiesView(QmlEngineDebug *client = 0, QWidget *parent = 0); + + void setEngineDebug(QmlEngineDebug *client); + void clear(); + +signals: + void activated(const QmlDebugObjectReference &, const QmlDebugPropertyReference &); + +public slots: + void reload(const QmlDebugObjectReference &); + void watchCreated(QmlDebugWatch *); + +private slots: + void queryFinished(); + void watchStateChanged(); + void valueChanged(const QByteArray &name, const QVariant &value); + void itemActivated(QTreeWidgetItem *i); + +private: + void setObject(const QmlDebugObjectReference &object); + void setWatched(const QString &property, bool watched); + + QmlEngineDebug *m_client; + QmlDebugObjectQuery *m_query; + QmlDebugWatch *m_watch; + + QTreeWidget *m_tree; + QmlDebugObjectReference m_object; +}; + + +QT_END_NAMESPACE + +#endif diff --git a/tools/qmldebugger/standalone/objecttree.cpp b/tools/qmldebugger/standalone/objecttree.cpp new file mode 100644 index 0000000..4059e77 --- /dev/null +++ b/tools/qmldebugger/standalone/objecttree.cpp @@ -0,0 +1,180 @@ +#include <QtGui/qevent.h> +#include <QtGui/qmenu.h> +#include <QtGui/qaction.h> + +#include <QInputDialog> + +#include <QtDeclarative/qmldebugservice.h> +#include <QtDeclarative/qmldebug.h> +#include <QtDeclarative/qmldebugclient.h> + +#include "objecttree.h" + +Q_DECLARE_METATYPE(QmlDebugObjectReference) + +ObjectTree::ObjectTree(QmlEngineDebug *client, QWidget *parent) + : QTreeWidget(parent), + m_client(client), + m_query(0) +{ + setHeaderHidden(true); + setMinimumWidth(250); + + connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + this, SLOT(currentItemChanged(QTreeWidgetItem *))); +} + +void ObjectTree::setEngineDebug(QmlEngineDebug *client) +{ + m_client = client; +} + +void ObjectTree::reload(int objectDebugId) +{ + if (!m_client) + return; + + if (m_query) { + delete m_query; + m_query = 0; + } + + m_query = m_client->queryObjectRecursive(QmlDebugObjectReference(objectDebugId), this); + if (!m_query->isWaiting()) + objectFetched(); + else + QObject::connect(m_query, SIGNAL(stateChanged(State)), + this, SLOT(objectFetched())); +} + +void ObjectTree::setCurrentObject(int debugId) +{ + QTreeWidgetItem *item = findItemByObjectId(debugId); + if (item) { + setCurrentItem(item); + scrollToItem(item); + item->setExpanded(true); + } +} + +void ObjectTree::objectFetched() +{ + dump(m_query->object(), 0); + buildTree(m_query->object(), 0); + + delete m_query; + m_query = 0; +} + +void ObjectTree::currentItemChanged(QTreeWidgetItem *item) +{ + if (!item) + return; + + QmlDebugObjectReference obj = item->data(0, Qt::UserRole).value<QmlDebugObjectReference>(); + if (obj.debugId() < 0) { + qWarning("QML Object Tree: bad object id"); + return; + } + emit currentObjectChanged(obj); +} + +void ObjectTree::buildTree(const QmlDebugObjectReference &obj, QTreeWidgetItem *parent) +{ + if (!parent) + clear(); + + QTreeWidgetItem *item = parent ? new QTreeWidgetItem(parent) : new QTreeWidgetItem(this); + item->setText(0, obj.className()); + item->setData(0, Qt::UserRole, qVariantFromValue(obj)); + + if (parent && obj.contextDebugId() >= 0 + && obj.contextDebugId() != parent->data(0, Qt::UserRole + ).value<QmlDebugObjectReference>().contextDebugId()) { + QmlDebugFileReference source = obj.source(); + if (!source.url().isEmpty()) { + QString toolTipString = QLatin1String("URL: ") + source.url().toString(); + item->setToolTip(0, toolTipString); + } + item->setForeground(0, QColor("orange")); + } else { + item->setExpanded(true); + } + + if (obj.contextDebugId() < 0) + item->setForeground(0, Qt::lightGray); + + for (int ii = 0; ii < obj.children().count(); ++ii) + buildTree(obj.children().at(ii), item); +} + +void ObjectTree::dump(const QmlDebugContextReference &ctxt, int ind) +{ + QByteArray indent(ind * 4, ' '); + qWarning().nospace() << indent.constData() << ctxt.debugId() << " " + << qPrintable(ctxt.name()); + + for (int ii = 0; ii < ctxt.contexts().count(); ++ii) + dump(ctxt.contexts().at(ii), ind + 1); + + for (int ii = 0; ii < ctxt.objects().count(); ++ii) + dump(ctxt.objects().at(ii), ind); +} + +void ObjectTree::dump(const QmlDebugObjectReference &obj, int ind) +{ + QByteArray indent(ind * 4, ' '); + qWarning().nospace() << indent.constData() << qPrintable(obj.className()) + << " " << qPrintable(obj.name()) << " " + << obj.debugId(); + + for (int ii = 0; ii < obj.children().count(); ++ii) + dump(obj.children().at(ii), ind + 1); +} + +QTreeWidgetItem *ObjectTree::findItemByObjectId(int debugId) const +{ + for (int i=0; i<topLevelItemCount(); i++) { + QTreeWidgetItem *item = findItem(topLevelItem(i), debugId); + if (item) + return item; + } + + return 0; +} + +QTreeWidgetItem *ObjectTree::findItem(QTreeWidgetItem *item, int debugId) const +{ + if (item->data(0, Qt::UserRole).value<QmlDebugObjectReference>().debugId() == debugId) + return item; + + QTreeWidgetItem *child; + for (int i=0; i<item->childCount(); i++) { + child = findItem(item->child(i), debugId); + if (child) + return child; + } + + return 0; +} + +void ObjectTree::mousePressEvent(QMouseEvent *me) +{ + QTreeWidget::mousePressEvent(me); + if (!currentItem()) + return; + if(me->button() == Qt::RightButton && me->type() == QEvent::MouseButtonPress) { + QAction action(tr("Add watch..."), 0); + QList<QAction *> actions; + actions << &action; + QmlDebugObjectReference obj = + currentItem()->data(0, Qt::UserRole).value<QmlDebugObjectReference>(); + if (QMenu::exec(actions, me->globalPos())) { + bool ok = false; + QString watch = QInputDialog::getText(this, tr("Watch expression"), + tr("Expression:"), QLineEdit::Normal, QString(), &ok); + if (ok && !watch.isEmpty()) + emit expressionWatchRequested(obj, watch); + } + } +} diff --git a/tools/qmldebugger/standalone/objecttree.h b/tools/qmldebugger/standalone/objecttree.h new file mode 100644 index 0000000..95820f3 --- /dev/null +++ b/tools/qmldebugger/standalone/objecttree.h @@ -0,0 +1,54 @@ +#ifndef OBJECTTREE_H +#define OBJECTTREE_H + +#include <QtGui/qtreewidget.h> + +QT_BEGIN_NAMESPACE + +class QTreeWidgetItem; + +class QmlEngineDebug; +class QmlDebugObjectReference; +class QmlDebugObjectQuery; +class QmlDebugContextReference; +class QmlDebugConnection; + + +class ObjectTree : public QTreeWidget +{ + Q_OBJECT +public: + ObjectTree(QmlEngineDebug *client = 0, QWidget *parent = 0); + + void setEngineDebug(QmlEngineDebug *client); + +signals: + void currentObjectChanged(const QmlDebugObjectReference &); + void expressionWatchRequested(const QmlDebugObjectReference &, const QString &); + +public slots: + void reload(int objectDebugId); + void setCurrentObject(int debugId); + +protected: + virtual void mousePressEvent(QMouseEvent *); + +private slots: + void objectFetched(); + void currentItemChanged(QTreeWidgetItem *); + +private: + QTreeWidgetItem *findItemByObjectId(int debugId) const; + QTreeWidgetItem *findItem(QTreeWidgetItem *item, int debugId) const; + void dump(const QmlDebugContextReference &, int); + void dump(const QmlDebugObjectReference &, int); + void buildTree(const QmlDebugObjectReference &, QTreeWidgetItem *parent); + + QmlEngineDebug *m_client; + QmlDebugObjectQuery *m_query; +}; + +QT_END_NAMESPACE + + +#endif diff --git a/tools/qmldebugger/standalone/qmldebugger.cpp b/tools/qmldebugger/standalone/qmldebugger.cpp new file mode 100644 index 0000000..5455878 --- /dev/null +++ b/tools/qmldebugger/standalone/qmldebugger.cpp @@ -0,0 +1,131 @@ +#include <QtCore/qtimer.h> +#include <QtCore/qdebug.h> +#include <QVBoxLayout> +#include <QPushButton> +#include <QLineEdit> +#include <QTabWidget> +#include <QSpinBox> +#include <QLabel> + +#include "canvasframerate.h" +#include "engine.h" +#include "qmldebugger.h" + +QmlDebugger::QmlDebugger(QWidget *parent) +: QWidget(parent) +{ + QVBoxLayout *layout = new QVBoxLayout; + setLayout(layout); + + + QHBoxLayout *connectLayout = new QHBoxLayout; + layout->addLayout(connectLayout); + connectLayout->addStretch(2); + + 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()), + this, SLOT(connectToHost())); + connectLayout->addWidget(m_connectButton); + m_disconnectButton = new QPushButton(tr("Disconnect"), this); + QObject::connect(m_disconnectButton, SIGNAL(clicked()), + this, SLOT(disconnectFromHost())); + m_disconnectButton->setEnabled(false); + connectLayout->addWidget(m_disconnectButton); + + m_tabs = new QTabWidget(this); + layout->addWidget(m_tabs); + + CanvasFrameRate *cfr = new CanvasFrameRate(this); + cfr->reset(&client); + cfr->setSizeHint(QSize(800, 600)); + m_tabs->addTab(cfr, tr("Frame Rate")); + + m_enginePane = new EnginePane(&client, this); + m_tabs->addTab(m_enginePane, tr("QML Engine")); + + QObject::connect(&client, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(connectionStateChanged())); + connectionStateChanged(); + + QObject::connect(&client, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(connectionError(QAbstractSocket::SocketError))); + + + m_tabs->setCurrentIndex(1); + connectToHost(); +} + +void QmlDebugger::setHost(const QString &host) +{ + m_host->setText(host); +} + +void QmlDebugger::setPort(quint16 port) +{ + m_port->setValue(port); +} + +void QmlDebugger::showEngineTab() +{ + m_tabs->setCurrentWidget(m_enginePane); +} + +void QmlDebugger::connectionStateChanged() +{ + switch (client.state()) { + default: + case QAbstractSocket::UnconnectedState: + m_connectionState->setText(tr("Disconnected")); + m_connectButton->setEnabled(true); + m_disconnectButton->setEnabled(false); + break; + case QAbstractSocket::HostLookupState: + m_connectionState->setText(tr("Resolving")); + m_connectButton->setEnabled(false); + m_disconnectButton->setEnabled(true); + break; + case QAbstractSocket::ConnectingState: + m_connectionState->setText(tr("Connecting")); + m_connectButton->setEnabled(false); + m_disconnectButton->setEnabled(true); + break; + case QAbstractSocket::ConnectedState: + m_connectionState->setText(tr("Connected")); + m_connectButton->setEnabled(false); + m_disconnectButton->setEnabled(true); + + QTimer::singleShot(0, m_enginePane, SLOT(refreshEngines())); + break; + case QAbstractSocket::ClosingState: + m_connectionState->setText(tr("Closing")); + m_connectButton->setEnabled(false); + m_disconnectButton->setEnabled(false); + break; + } +} + +void QmlDebugger::connectionError(QAbstractSocket::SocketError socketError) +{ + qWarning() << "qmldebugger cannot connect:" << socketError + << client.errorString(); +} + +void QmlDebugger::connectToHost() +{ + client.connectToHost(m_host->text(), m_port->value()); +} + +void QmlDebugger::disconnectFromHost() +{ + client.disconnectFromHost(); +} diff --git a/tools/qmldebugger/standalone/qmldebugger.h b/tools/qmldebugger/standalone/qmldebugger.h new file mode 100644 index 0000000..9203e33 --- /dev/null +++ b/tools/qmldebugger/standalone/qmldebugger.h @@ -0,0 +1,47 @@ +#ifndef QMLDEBUGGER_H +#define QMLDEBUGGER_H + +#include <QtDeclarative/qmldebugclient.h> +#include <QtNetwork/qtcpsocket.h> +#include <QtGui/qwidget.h> + +class QLabel; +class QLineEdit; +class QSpinBox; +class QPushButton; +class QTabWidget; + +class EnginePane; + +class QmlDebugger : public QWidget +{ + Q_OBJECT +public: + QmlDebugger(QWidget * = 0); + + void setHost(const QString &host); + void setPort(quint16 port); + void showEngineTab(); + +public slots: + void connectToHost(); + void disconnectFromHost(); + +private slots: + void connectionStateChanged(); + void connectionError(QAbstractSocket::SocketError socketError); + +private: + QmlDebugConnection client; + + QLabel *m_connectionState; + QLineEdit *m_host; + QSpinBox *m_port; + QPushButton *m_connectButton; + QPushButton *m_disconnectButton; + + EnginePane *m_enginePane; + QTabWidget *m_tabs; +}; + +#endif diff --git a/tools/qmldebugger/standalone/qmldebugger.pri b/tools/qmldebugger/standalone/qmldebugger.pri new file mode 100644 index 0000000..aad5eb1 --- /dev/null +++ b/tools/qmldebugger/standalone/qmldebugger.pri @@ -0,0 +1,16 @@ +QT += network declarative +contains(QT_CONFIG, opengles2)|contains(QT_CONFIG, opengles1): QT += opengl + +# Input +HEADERS += $$PWD/canvasframerate.h \ + $$PWD/watchtable.h \ + $$PWD/objecttree.h \ + $$PWD/objectpropertiesview.h \ + $$PWD/expressionquerywidget.h + +SOURCES += $$PWD/canvasframerate.cpp \ + $$PWD/watchtable.cpp \ + $$PWD/objecttree.cpp \ + $$PWD/objectpropertiesview.cpp \ + $$PWD/expressionquerywidget.cpp + diff --git a/tools/qmldebugger/standalone/qmldebugger.qrc b/tools/qmldebugger/standalone/qmldebugger.qrc new file mode 100644 index 0000000..cb53ad5 --- /dev/null +++ b/tools/qmldebugger/standalone/qmldebugger.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/"> + <file>engines.qml</file> + <file>engine.png</file> + <file>refresh.png</file> + </qresource> +</RCC> diff --git a/tools/qmldebugger/standalone/refresh.png b/tools/qmldebugger/standalone/refresh.png Binary files differnew file mode 100644 index 0000000..8befc80 --- /dev/null +++ b/tools/qmldebugger/standalone/refresh.png diff --git a/tools/qmldebugger/standalone/standalone.pro b/tools/qmldebugger/standalone/standalone.pro new file mode 100644 index 0000000..72e3ef5 --- /dev/null +++ b/tools/qmldebugger/standalone/standalone.pro @@ -0,0 +1,18 @@ +DESTDIR = ../../bin + +include(qmldebugger.pri) + +HEADERS += $$PWD/qmldebugger.h \ + $$PWD/engine.h + +SOURCES += $$PWD/qmldebugger.cpp \ + $$PWD/engine.cpp \ + $$PWD/main.cpp + +RESOURCES += $$PWD/qmldebugger.qrc +OTHER_FILES += $$PWD/engines.qml + +target.path=$$[QT_INSTALL_BINS] +INSTALLS += target + +CONFIG += console diff --git a/tools/qmldebugger/standalone/watchtable.cpp b/tools/qmldebugger/standalone/watchtable.cpp new file mode 100644 index 0000000..ee74cfb --- /dev/null +++ b/tools/qmldebugger/standalone/watchtable.cpp @@ -0,0 +1,325 @@ +#include "watchtable.h" + +#include <QtCore/qdebug.h> +#include <QtGui/qevent.h> +#include <QtGui/qaction.h> +#include <QtGui/qmenu.h> + +#include <QtDeclarative/qmldebug.h> +#include <QtDeclarative/qmlmetatype.h> + +QT_BEGIN_NAMESPACE + + +WatchTableModel::WatchTableModel(QmlEngineDebug *client, QObject *parent) + : QAbstractTableModel(parent), + m_client(client) +{ +} + +WatchTableModel::~WatchTableModel() +{ + for (int i=0; i<m_columns.count(); i++) + delete m_columns[i].watch; +} + +void WatchTableModel::setEngineDebug(QmlEngineDebug *client) +{ + m_client = client; +} + +void WatchTableModel::addWatch(QmlDebugWatch *watch, const QString &title) +{ + QString property; + if (qobject_cast<QmlDebugPropertyWatch *>(watch)) + property = qobject_cast<QmlDebugPropertyWatch *>(watch)->name(); + + connect(watch, SIGNAL(valueChanged(QByteArray,QVariant)), + SLOT(watchedValueChanged(QByteArray,QVariant))); + + connect(watch, SIGNAL(stateChanged(State)), SLOT(watchStateChanged())); + + int col = columnCount(QModelIndex()); + beginInsertColumns(QModelIndex(), col, col); + + WatchedEntity e; + e.title = title; + e.hasFirstValue = false; + e.property = property; + e.watch = watch; + m_columns.append(e); + + endInsertColumns(); +} + +void WatchTableModel::removeWatch(QmlDebugWatch *watch) +{ + int column = columnForWatch(watch); + if (column == -1) + return; + + WatchedEntity entity = m_columns.takeAt(column); + + for (QList<Value>::Iterator iter = m_values.begin(); iter != m_values.end();) { + if (iter->column == column) { + iter = m_values.erase(iter); + } else { + if(iter->column > column) + --iter->column; + ++iter; + } + } + + reset(); +} + +void WatchTableModel::updateWatch(QmlDebugWatch *watch, const QVariant &value) +{ + int column = columnForWatch(watch); + if (column == -1) + return; + + addValue(column, value); + + if (!m_columns[column].hasFirstValue) { + m_columns[column].hasFirstValue = true; + m_values[m_values.count() - 1].first = true; + } +} + +QmlDebugWatch *WatchTableModel::findWatch(int column) const +{ + if (column < m_columns.count()) + return m_columns.at(column).watch; + return 0; +} + +QmlDebugWatch *WatchTableModel::findWatch(int objectDebugId, const QString &property) const +{ + for (int i=0; i<m_columns.count(); i++) { + if (m_columns[i].watch->objectDebugId() == objectDebugId + && m_columns[i].property == property) { + return m_columns[i].watch; + } + } + return 0; +} + +int WatchTableModel::rowCount(const QModelIndex &) const +{ + return m_values.count(); +} + +int WatchTableModel::columnCount(const QModelIndex &) const +{ + return m_columns.count(); +} + +QVariant WatchTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal) { + if (section < m_columns.count() && role == Qt::DisplayRole) + return m_columns.at(section).title; + } else { + if (role == Qt::DisplayRole) + return section + 1; + } + return QVariant(); +} + +QVariant WatchTableModel::data(const QModelIndex &idx, int role) const +{ + if (m_values.at(idx.row()).column == idx.column()) { + if (role == Qt::DisplayRole) { + const QVariant &value = m_values.at(idx.row()).variant; + QString str = value.toString(); + + if (str.isEmpty() && QmlMetaType::isObject(value.userType())) { + QObject *o = QmlMetaType::toQObject(value); + if(o) { + QString objectName = o->objectName(); + if(objectName.isEmpty()) + objectName = QLatin1String("<unnamed>"); + str = QLatin1String(o->metaObject()->className()) + + QLatin1String(": ") + objectName; + } + } + + if(str.isEmpty()) { + QDebug d(&str); + d << value; + } + return QVariant(str); + } else if(role == Qt::BackgroundRole) { + if(m_values.at(idx.row()).first) + return QColor(Qt::green); + else + return QVariant(); + } else { + return QVariant(); + } + } else { + return QVariant(); + } +} + +void WatchTableModel::watchStateChanged() +{ + QmlDebugWatch *watch = qobject_cast<QmlDebugWatch*>(sender()); + + if (watch && watch->state() == QmlDebugWatch::Inactive) { + removeWatch(watch); + watch->deleteLater(); + } +} + +int WatchTableModel::columnForWatch(QmlDebugWatch *watch) const +{ + for (int i=0; i<m_columns.count(); i++) { + if (m_columns.at(i).watch == watch) + return i; + } + return -1; +} + +void WatchTableModel::addValue(int column, const QVariant &value) +{ + int row = columnCount(QModelIndex()); + beginInsertRows(QModelIndex(), row, row); + + Value v; + v.column = column; + v.variant = value; + v.first = false; + m_values.append(v); + + endInsertRows(); +} + +void WatchTableModel::togglePropertyWatch(const QmlDebugObjectReference &object, const QmlDebugPropertyReference &property) +{ + if (!m_client || !property.hasNotifySignal()) + return; + + QmlDebugWatch *watch = findWatch(object.debugId(), property.name()); + if (watch) { + // watch will be deleted in watchStateChanged() + m_client->removeWatch(watch); + return; + } + + watch = m_client->addWatch(property, this); + if (watch->state() == QmlDebugWatch::Dead) { + delete watch; + watch = 0; + } else { + QString desc = property.name() + + QLatin1String(" on\n") + + object.className() + + QLatin1String(": ") + + (object.name().isEmpty() ? QLatin1String("<unnamed>") : object.name()); + addWatch(watch, desc); + emit watchCreated(watch); + } +} + +void WatchTableModel::watchedValueChanged(const QByteArray &propertyName, const QVariant &value) +{ + Q_UNUSED(propertyName); + QmlDebugWatch *watch = qobject_cast<QmlDebugWatch*>(sender()); + if (watch) + updateWatch(watch, value); +} + +void WatchTableModel::expressionWatchRequested(const QmlDebugObjectReference &obj, const QString &expr) +{ + if (!m_client) + return; + + QmlDebugWatch *watch = m_client->addWatch(obj, expr, this); + + if (watch->state() == QmlDebugWatch::Dead) { + delete watch; + watch = 0; + } else { + addWatch(watch, expr); + emit watchCreated(watch); + } +} + +void WatchTableModel::removeWatchAt(int column) +{ + if (!m_client) + return; + + QmlDebugWatch *watch = findWatch(column); + if (watch) { + m_client->removeWatch(watch); + delete watch; + watch = 0; + } +} + +void WatchTableModel::removeAllWatches() +{ + for (int i=0; i<m_columns.count(); i++) { + if (m_client) + m_client->removeWatch(m_columns[i].watch); + delete m_columns[i].watch; + } + m_columns.clear(); + m_values.clear(); + reset(); +} + +//---------------------------------------------- + +WatchTableHeaderView::WatchTableHeaderView(WatchTableModel *model, QWidget *parent) + : QHeaderView(Qt::Horizontal, parent), + m_model(model) +{ + setClickable(true); +} + +void WatchTableHeaderView::mousePressEvent(QMouseEvent *me) +{ + QHeaderView::mousePressEvent(me); + + if (me->button() == Qt::RightButton && me->type() == QEvent::MouseButtonPress) { + int col = logicalIndexAt(me->pos()); + if (col >= 0) { + QAction action(tr("Stop watching"), 0); + QList<QAction *> actions; + actions << &action; + if (QMenu::exec(actions, me->globalPos())) + m_model->removeWatchAt(col); + } + } +} + + +//---------------------------------------------- + +WatchTableView::WatchTableView(WatchTableModel *model, QWidget *parent) + : QTableView(parent), + m_model(model) +{ + setAlternatingRowColors(true); + connect(model, SIGNAL(watchCreated(QmlDebugWatch*)), SLOT(watchCreated(QmlDebugWatch*))); + connect(this, SIGNAL(activated(QModelIndex)), SLOT(indexActivated(QModelIndex))); +} + +void WatchTableView::indexActivated(const QModelIndex &index) +{ + QmlDebugWatch *watch = m_model->findWatch(index.column()); + if (watch) + emit objectActivated(watch->objectDebugId()); +} + +void WatchTableView::watchCreated(QmlDebugWatch *watch) +{ + int column = m_model->columnForWatch(watch); + resizeColumnToContents(column); +} + +QT_END_NAMESPACE diff --git a/tools/qmldebugger/standalone/watchtable.h b/tools/qmldebugger/standalone/watchtable.h new file mode 100644 index 0000000..67c2565 --- /dev/null +++ b/tools/qmldebugger/standalone/watchtable.h @@ -0,0 +1,114 @@ +#ifndef WATCHTABLEMODEL_H +#define WATCHTABLEMODEL_H + +#include <QtCore/qpointer.h> +#include <QtCore/qlist.h> + +#include <QWidget> +#include <QHeaderView> +#include <QAbstractTableModel> +#include <QTableView> + +QT_BEGIN_NAMESPACE + +class QmlDebugWatch; +class QmlEngineDebug; +class QmlDebugConnection; +class QmlDebugPropertyReference; +class QmlDebugObjectReference; + +class WatchTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + WatchTableModel(QmlEngineDebug *client = 0, QObject *parent = 0); + ~WatchTableModel(); + + void setEngineDebug(QmlEngineDebug *client); + + QmlDebugWatch *findWatch(int column) const; + int columnForWatch(QmlDebugWatch *watch) const; + + void removeWatchAt(int column); + void removeAllWatches(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +signals: + void watchCreated(QmlDebugWatch *watch); + +public slots: + void togglePropertyWatch(const QmlDebugObjectReference &obj, const QmlDebugPropertyReference &prop); + void expressionWatchRequested(const QmlDebugObjectReference &, const QString &); + +private slots: + void watchStateChanged(); + void watchedValueChanged(const QByteArray &propertyName, const QVariant &value); + +private: + void addWatch(QmlDebugWatch *watch, const QString &title); + void removeWatch(QmlDebugWatch *watch); + void updateWatch(QmlDebugWatch *watch, const QVariant &value); + + QmlDebugWatch *findWatch(int objectDebugId, const QString &property) const; + + void addValue(int column, const QVariant &value); + + struct WatchedEntity + { + QString title; + bool hasFirstValue; + QString property; + QPointer<QmlDebugWatch> watch; + }; + + struct Value { + int column; + QVariant variant; + bool first; + }; + + QmlEngineDebug *m_client; + QList<WatchedEntity> m_columns; + QList<Value> m_values; +}; + + +class WatchTableHeaderView : public QHeaderView +{ + Q_OBJECT +public: + WatchTableHeaderView(WatchTableModel *model, QWidget *parent = 0); + +protected: + void mousePressEvent(QMouseEvent *me); + +private: + WatchTableModel *m_model; +}; + + +class WatchTableView : public QTableView +{ + Q_OBJECT +public: + WatchTableView(WatchTableModel *model, QWidget *parent = 0); + +signals: + void objectActivated(int objectDebugId); + +private slots: + void indexActivated(const QModelIndex &index); + void watchCreated(QmlDebugWatch *watch); + +private: + WatchTableModel *m_model; +}; + + +QT_END_NAMESPACE + +#endif // WATCHTABLEMODEL_H |