UI improvements and functionality fixes to qml inspector

now you can modify expression values after you set a watch on them.
doesn't seem to work yet for all cases, but there must be some issues
within the qml debug server holding things back.
This commit is contained in:
Lasse Holmstedt
2010-04-16 15:39:23 +02:00
parent 0a643a1994
commit 9b060071d9
6 changed files with 182 additions and 70 deletions

View File

@@ -28,6 +28,7 @@
**************************************************************************/ **************************************************************************/
#include "objectpropertiesview.h" #include "objectpropertiesview.h"
#include "inspectorcontext.h" #include "inspectorcontext.h"
#include "watchtable.h"
#include <QtCore/QDebug> #include <QtCore/QDebug>
@@ -67,11 +68,13 @@ PropertiesViewItem::PropertiesViewItem(QTreeWidgetItem *parent, Type type)
{ {
} }
ObjectPropertiesView::ObjectPropertiesView(QDeclarativeEngineDebug *client, QWidget *parent) ObjectPropertiesView::ObjectPropertiesView(WatchTableModel *watchTableModel,
QDeclarativeEngineDebug *client, QWidget *parent)
: QWidget(parent), : QWidget(parent),
m_client(client), m_client(client),
m_query(0), m_query(0),
m_watch(0), m_clickedItem(0) m_watch(0), m_clickedItem(0), m_showUnwatchableProperties(false),
m_watchTableModel(watchTableModel)
{ {
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
@@ -92,15 +95,22 @@ ObjectPropertiesView::ObjectPropertiesView(QDeclarativeEngineDebug *client, QWid
m_addWatchAction = new QAction(tr("Watch expression"), this); m_addWatchAction = new QAction(tr("Watch expression"), this);
m_removeWatchAction = new QAction(tr("Remove watch"), this); m_removeWatchAction = new QAction(tr("Remove watch"), this);
m_toggleUnwatchablePropertiesAction = new QAction(tr("Show unwatchable properties"), this);
connect(m_addWatchAction, SIGNAL(triggered()), SLOT(addWatch())); connect(m_addWatchAction, SIGNAL(triggered()), SLOT(addWatch()));
connect(m_removeWatchAction, SIGNAL(triggered()), SLOT(removeWatch())); connect(m_removeWatchAction, SIGNAL(triggered()), SLOT(removeWatch()));
connect(m_toggleUnwatchablePropertiesAction, SIGNAL(triggered()), SLOT(toggleUnwatchableProperties()));
m_tree->setColumnCount(3); m_tree->setColumnCount(3);
m_tree->header()->setDefaultSectionSize(150); m_tree->header()->setDefaultSectionSize(150);
layout->addWidget(m_tree); layout->addWidget(m_tree);
} }
void ObjectPropertiesView::toggleUnwatchableProperties()
{
m_showUnwatchableProperties = !m_showUnwatchableProperties;
setObject(m_object);
}
void ObjectPropertiesView::changeItemSelection() void ObjectPropertiesView::changeItemSelection()
{ {
if (m_tree->selectedItems().isEmpty()) if (m_tree->selectedItems().isEmpty())
@@ -208,21 +218,31 @@ void ObjectPropertiesView::setObject(const QDeclarativeDebugObjectReference &obj
for (int i=0; i<properties.count(); ++i) { for (int i=0; i<properties.count(); ++i) {
const QDeclarativeDebugPropertyReference &p = properties[i]; const QDeclarativeDebugPropertyReference &p = properties[i];
PropertiesViewItem *item = new PropertiesViewItem(m_tree); if (m_showUnwatchableProperties || p.hasNotifySignal()) {
item->property = p;
item->setText(0, p.name()); PropertiesViewItem *item = new PropertiesViewItem(m_tree);
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); item->property = p;
setPropertyValue(item, p.value(), !p.hasNotifySignal()); item->setText(0, p.name());
item->setText(2, p.valueTypeName()); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
if (m_watchTableModel.data() && m_watchTableModel.data()->isWatchingProperty(p)) {
QFont font = m_tree->font();
font.setBold(true);
item->setFont(0, font);
}
setPropertyValue(item, p.value(), !p.hasNotifySignal());
item->setText(2, p.valueTypeName());
// binding is set after property value to ensure it is added to the end of the
// list, if the value is a list
if (!p.binding().isEmpty()) {
PropertiesViewItem *binding = new PropertiesViewItem(item, PropertiesViewItem::BindingType);
binding->setText(1, p.binding());
binding->setForeground(1, Qt::darkGreen);
}
// binding is set after property value to ensure it is added to the end of the
// list, if the value is a list
if (!p.binding().isEmpty()) {
PropertiesViewItem *binding = new PropertiesViewItem(item, PropertiesViewItem::BindingType);
binding->setText(1, p.binding());
binding->setForeground(1, Qt::darkGreen);
} }
} }
} }
@@ -315,6 +335,14 @@ void ObjectPropertiesView::contextMenuEvent(QContextMenuEvent *event)
} else { } else {
menu.addAction(m_removeWatchAction); menu.addAction(m_removeWatchAction);
} }
menu.addSeparator();
if (m_showUnwatchableProperties)
m_toggleUnwatchablePropertiesAction->setText(tr("Hide unwatchable properties"));
else
m_toggleUnwatchablePropertiesAction->setText(tr("Show unwatchable properties"));
menu.addAction(m_toggleUnwatchablePropertiesAction);
menu.exec(event->globalPos()); menu.exec(event->globalPos());
} }

View File

@@ -45,12 +45,13 @@ namespace Qml {
namespace Internal { namespace Internal {
class PropertiesViewItem; class PropertiesViewItem;
class WatchTableModel;
class ObjectPropertiesView : public QWidget class ObjectPropertiesView : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
ObjectPropertiesView(QDeclarativeEngineDebug *client = 0, QWidget *parent = 0); ObjectPropertiesView(WatchTableModel *watchTableModel, QDeclarativeEngineDebug *client = 0, QWidget *parent = 0);
void setEngineDebug(QDeclarativeEngineDebug *client); void setEngineDebug(QDeclarativeEngineDebug *client);
void clear(); void clear();
@@ -75,6 +76,8 @@ private slots:
void addWatch(); void addWatch();
void removeWatch(); void removeWatch();
void filterView(const QString &filterText);
void toggleUnwatchableProperties();
private: private:
void toggleWatch(QTreeWidgetItem *item); void toggleWatch(QTreeWidgetItem *item);
@@ -89,9 +92,13 @@ private:
QAction *m_addWatchAction; QAction *m_addWatchAction;
QAction *m_removeWatchAction; QAction *m_removeWatchAction;
QAction *m_toggleUnwatchablePropertiesAction;
QTreeWidgetItem *m_clickedItem; QTreeWidgetItem *m_clickedItem;
bool m_showUnwatchableProperties;
QTreeWidget *m_tree; QTreeWidget *m_tree;
QWeakPointer<WatchTableModel> m_watchTableModel;
QDeclarativeDebugObjectReference m_object; QDeclarativeDebugObjectReference m_object;
}; };

View File

@@ -28,14 +28,18 @@
**************************************************************************/ **************************************************************************/
#include "watchtable.h" #include "watchtable.h"
#include <QtCore/qdebug.h> #include <QtCore/QEvent>
#include <QtGui/qevent.h> #include <QtGui/QAction>
#include <QtGui/qaction.h> #include <QtGui/QMenu>
#include <QtGui/qmenu.h> #include <QMouseEvent>
#include <QApplication>
#include <QCoreApplication>
#include <private/qdeclarativedebug_p.h> #include <private/qdeclarativedebug_p.h>
#include <private/qdeclarativemetatype_p.h> #include <private/qdeclarativemetatype_p.h>
#include <QtCore/QDebug>
namespace Qml { namespace Qml {
namespace Internal { namespace Internal {
@@ -47,7 +51,7 @@ WatchTableModel::WatchTableModel(QDeclarativeEngineDebug *client, QObject *paren
: QAbstractTableModel(parent), : QAbstractTableModel(parent),
m_client(client) m_client(client)
{ {
m_editablePropertyTypes << "qreal" << "bool" << "QString" << "int" << "QVariant" << "QUrl";
} }
WatchTableModel::~WatchTableModel() WatchTableModel::~WatchTableModel()
@@ -61,7 +65,8 @@ void WatchTableModel::setEngineDebug(QDeclarativeEngineDebug *client)
m_client = client; m_client = client;
} }
void WatchTableModel::addWatch(QDeclarativeDebugWatch *watch, const QString &title) void WatchTableModel::addWatch(const QDeclarativeDebugObjectReference &object, const QString &propertyType,
QDeclarativeDebugWatch *watch, const QString &title)
{ {
QString property; QString property;
if (qobject_cast<QDeclarativeDebugPropertyWatch *>(watch)) if (qobject_cast<QDeclarativeDebugPropertyWatch *>(watch))
@@ -79,6 +84,9 @@ void WatchTableModel::addWatch(QDeclarativeDebugWatch *watch, const QString &tit
e.title = title; e.title = title;
e.property = property; e.property = property;
e.watch = watch; e.watch = watch;
e.objectId = object.idString();
e.objectDebugId = object.debugId();
e.objectPropertyType = propertyType;
m_entities.append(e); m_entities.append(e);
@@ -157,31 +165,86 @@ QVariant WatchTableModel::data(const QModelIndex &idx, int role) const
if (role == Qt::DisplayRole || role == Qt::EditRole) { if (role == Qt::DisplayRole || role == Qt::EditRole) {
return QVariant(m_entities.at(idx.row()).value); return QVariant(m_entities.at(idx.row()).value);
} else if (role == CanEditRole || role == Qt::ForegroundRole) {
const WatchedEntity &entity = m_entities.at(idx.row());
bool canEdit = entity.objectId.length() > 0 && canEditProperty(entity.objectPropertyType);
} else if(role == Qt::BackgroundRole) { if (role == Qt::ForegroundRole)
//return QColor(Qt::green); return canEdit ? qApp->palette().color(QPalette::Foreground) : qApp->palette().color(QPalette::Disabled, QPalette::Foreground);
return canEdit;
} }
} }
return QVariant(); return QVariant();
} }
bool WatchTableModel::isWatchingProperty(const QDeclarativeDebugPropertyReference &prop) const
{
foreach (const WatchedEntity &entity, m_entities) {
if (entity.objectDebugId == prop.objectDebugId() && entity.property == prop.name())
return true;
}
return false;
}
bool WatchTableModel::setData ( const QModelIndex & index, const QVariant & value, int role) bool WatchTableModel::setData ( const QModelIndex & index, const QVariant & value, int role)
{ {
Q_UNUSED(index); Q_UNUSED(index);
Q_UNUSED(value); Q_UNUSED(value);
if (role == Qt::EditRole) { if (role == Qt::EditRole) {
if (index.row() >= 0 && index.row() < m_entities.length()) {
WatchedEntity &entity = m_entities[index.row()];
qDebug() << entity.property << entity.title << entity.objectId;
if (entity.objectId.length()) {
QString quoteWrappedValue = value.toString();
if (addQuotesForData(value))
quoteWrappedValue = QString("'%1'").arg(quoteWrappedValue);
QString constructedExpression = entity.objectId + "." + entity.property + "=" + quoteWrappedValue;
qDebug() << "EXPRESSION:" << constructedExpression;
m_client->queryExpressionResult(entity.objectDebugId, constructedExpression, this);
}
}
return true; return true;
} }
return true; return true;
} }
bool WatchTableModel::canEditProperty(const QString &propertyType) const
{
return m_editablePropertyTypes.contains(propertyType);
}
bool WatchTableModel::addQuotesForData(const QVariant &value) const
{
switch (value.type()) {
case QVariant::String:
case QVariant::Color:
case QVariant::Date:
return true;
default:
break;
}
return false;
}
Qt::ItemFlags WatchTableModel::flags ( const QModelIndex & index ) const Qt::ItemFlags WatchTableModel::flags ( const QModelIndex & index ) const
{ {
if (index.column() == C_VALUE) Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
return Qt::ItemIsSelectable | Qt::ItemIsEnabled; // Qt::ItemIsEditable | <- disabled for now
return Qt::ItemIsSelectable | Qt::ItemIsEnabled; if (index.column() == C_VALUE && index.data(CanEditRole).toBool())
{
flags |= Qt::ItemIsEditable;
}
return flags;
} }
void WatchTableModel::watchStateChanged() void WatchTableModel::watchStateChanged()
@@ -220,10 +283,11 @@ void WatchTableModel::togglePropertyWatch(const QDeclarativeDebugObjectReference
delete watch; delete watch;
watch = 0; watch = 0;
} else { } else {
QString desc = (object.idString().isEmpty() ? QLatin1String("<") + object.className() + QLatin1String(">") : object.idString()) QString desc = (object.idString().isEmpty() ? QLatin1String("<") + object.className() + QLatin1String(">") : object.idString())
+ QLatin1String(".") + property.name(); + QLatin1String(".") + property.name();
addWatch(watch, desc); addWatch(object, property.valueTypeName(), watch, desc);
emit watchCreated(watch); emit watchCreated(watch);
} }
} }
@@ -247,7 +311,7 @@ void WatchTableModel::expressionWatchRequested(const QDeclarativeDebugObjectRefe
delete watch; delete watch;
watch = 0; watch = 0;
} else { } else {
addWatch(watch, expr); addWatch(obj, "<expression>", watch, expr);
emit watchCreated(watch); emit watchCreated(watch);
} }
} }

View File

@@ -72,6 +72,8 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
bool setData (const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); bool setData (const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
bool isWatchingProperty(const QDeclarativeDebugPropertyReference &prop) const;
signals: signals:
void watchCreated(QDeclarativeDebugWatch *watch); void watchCreated(QDeclarativeDebugWatch *watch);
void watchRemoved(); void watchRemoved();
@@ -85,7 +87,11 @@ private slots:
void watchedValueChanged(const QByteArray &propertyName, const QVariant &value); void watchedValueChanged(const QByteArray &propertyName, const QVariant &value);
private: private:
void addWatch(QDeclarativeDebugWatch *watch, const QString &title); bool canEditProperty(const QString &propertyType) const;
bool addQuotesForData(const QVariant &value) const;
void addWatch(const QDeclarativeDebugObjectReference &object, const QString &propertyType,
QDeclarativeDebugWatch *watch, const QString &title);
void removeWatch(QDeclarativeDebugWatch *watch); void removeWatch(QDeclarativeDebugWatch *watch);
void updateWatch(QDeclarativeDebugWatch *watch, const QVariant &value); void updateWatch(QDeclarativeDebugWatch *watch, const QVariant &value);
@@ -93,12 +99,21 @@ private:
struct WatchedEntity struct WatchedEntity
{ {
int objectDebugId;
QString objectId;
QString title; QString title;
QString property; QString property;
QString objectPropertyType;
QPointer<QDeclarativeDebugWatch> watch; QPointer<QDeclarativeDebugWatch> watch;
QVariant value; QVariant value;
}; };
enum DataRoles {
CanEditRole = Qt::UserRole + 1
};
QStringList m_editablePropertyTypes;
QDeclarativeEngineDebug *m_client; QDeclarativeEngineDebug *m_client;
QList<WatchedEntity> m_entities; QList<WatchedEntity> m_entities;
}; };

View File

@@ -97,7 +97,7 @@ using namespace Qml;
namespace Qml { namespace Qml {
namespace Internal { namespace Internal {
class EngineSpinBox : public QSpinBox class EngineComboBox : public QComboBox
{ {
Q_OBJECT Q_OBJECT
public: public:
@@ -107,28 +107,27 @@ public:
int id; int id;
}; };
EngineSpinBox(QWidget *parent = 0); EngineComboBox(QWidget *parent = 0);
void addEngine(int engine, const QString &name); void addEngine(int engine, const QString &name);
void clearEngines(); void clearEngines();
protected: protected:
virtual QString textFromValue(int value) const; //virtual QString textFromValue(int value) const;
virtual int valueFromText(const QString &text) const; //virtual int valueFromText(const QString &text) const;
private: private:
QList<EngineInfo> m_engines; QList<EngineInfo> m_engines;
}; };
EngineSpinBox::EngineSpinBox(QWidget *parent) EngineComboBox::EngineComboBox(QWidget *parent)
: QSpinBox(parent) : QComboBox(parent)
{ {
setEnabled(false); setEnabled(false);
setReadOnly(true); setEditable(false);
setRange(0, 0);
} }
void EngineSpinBox::addEngine(int engine, const QString &name) void EngineComboBox::addEngine(int engine, const QString &name)
{ {
EngineInfo info; EngineInfo info;
info.id = engine; info.id = engine;
@@ -138,31 +137,31 @@ void EngineSpinBox::addEngine(int engine, const QString &name)
info.name = name; info.name = name;
m_engines << info; m_engines << info;
setRange(0, m_engines.count()-1); addItem(info.name);
} }
void EngineSpinBox::clearEngines() void EngineComboBox::clearEngines()
{ {
m_engines.clear(); m_engines.clear();
} }
QString EngineSpinBox::textFromValue(int value) const //QString EngineComboBox::textFromValue(int value) const
{ //{
for (int i=0; i<m_engines.count(); ++i) { // for (int i=0; i<m_engines.count(); ++i) {
if (m_engines[i].id == value) // if (m_engines[i].id == value)
return m_engines[i].name; // return m_engines[i].name;
} // }
return QLatin1String("<None>"); // return QLatin1String("<None>");
} //}
int EngineSpinBox::valueFromText(const QString &text) const //int EngineComboBox::valueFromText(const QString &text) const
{ //{
for (int i=0; i<m_engines.count(); ++i) { // for (int i=0; i<m_engines.count(); ++i) {
if (m_engines[i].name == text) // if (m_engines[i].name == text)
return m_engines[i].id; // return m_engines[i].id;
} // }
return -1; // return -1;
} //}
} // Internal } // Internal
@@ -183,7 +182,7 @@ QmlInspector::QmlInspector(QObject *parent)
m_watchTableModel = new Internal::WatchTableModel(0, this); m_watchTableModel = new Internal::WatchTableModel(0, this);
m_objectTreeWidget = new Internal::ObjectTree; m_objectTreeWidget = new Internal::ObjectTree;
m_propertiesWidget = new Internal::ObjectPropertiesView; m_propertiesWidget = new Internal::ObjectPropertiesView(m_watchTableModel);
m_watchTableView = new Internal::WatchTableView(m_watchTableModel); m_watchTableView = new Internal::WatchTableView(m_watchTableModel);
m_expressionWidget = new Internal::ExpressionQueryWidget(Internal::ExpressionQueryWidget::SeparateEntryMode); m_expressionWidget = new Internal::ExpressionQueryWidget(Internal::ExpressionQueryWidget::SeparateEntryMode);
// m_frameRateWidget = new Internal::CanvasFrameRate; // m_frameRateWidget = new Internal::CanvasFrameRate;
@@ -346,9 +345,9 @@ void QmlInspector::connectionError()
void QmlInspector::createDockWidgets() void QmlInspector::createDockWidgets()
{ {
m_engineSpinBox = new Internal::EngineSpinBox; m_engineComboBox = new Internal::EngineComboBox;
m_engineSpinBox->setEnabled(false); m_engineComboBox->setEnabled(false);
connect(m_engineSpinBox, SIGNAL(valueChanged(int)), connect(m_engineComboBox, SIGNAL(currentIndexChanged(int)),
SLOT(queryEngineContext(int))); SLOT(queryEngineContext(int)));
// FancyMainWindow uses widgets' window titles for tab labels // FancyMainWindow uses widgets' window titles for tab labels
@@ -359,7 +358,7 @@ void QmlInspector::createDockWidgets()
treeOptionBarLayout->setContentsMargins(5, 0, 5, 0); treeOptionBarLayout->setContentsMargins(5, 0, 5, 0);
treeOptionBarLayout->setSpacing(5); treeOptionBarLayout->setSpacing(5);
treeOptionBarLayout->addWidget(new QLabel(tr("QML engine:"))); treeOptionBarLayout->addWidget(new QLabel(tr("QML engine:")));
treeOptionBarLayout->addWidget(m_engineSpinBox); treeOptionBarLayout->addWidget(m_engineComboBox);
QWidget *treeWindow = new QWidget; QWidget *treeWindow = new QWidget;
treeWindow->setObjectName(QLatin1String("QmlDebugTree")); treeWindow->setObjectName(QLatin1String("QmlDebugTree"));
@@ -590,7 +589,7 @@ void QmlInspector::reloadEngines()
return; return;
} }
m_engineSpinBox->setEnabled(false); m_engineComboBox->setEnabled(false);
m_engineQuery = m_client->queryAvailableEngines(this); m_engineQuery = m_client->queryAvailableEngines(this);
if (!m_engineQuery->isWaiting()) if (!m_engineQuery->isWaiting())
@@ -602,7 +601,7 @@ void QmlInspector::reloadEngines()
void QmlInspector::enginesChanged() void QmlInspector::enginesChanged()
{ {
m_engineSpinBox->clearEngines(); m_engineComboBox->clearEngines();
QList<QDeclarativeDebugEngineReference> engines = m_engineQuery->engines(); QList<QDeclarativeDebugEngineReference> engines = m_engineQuery->engines();
delete m_engineQuery; m_engineQuery = 0; delete m_engineQuery; m_engineQuery = 0;
@@ -610,13 +609,13 @@ void QmlInspector::enginesChanged()
if (engines.isEmpty()) if (engines.isEmpty())
qWarning("qmldebugger: no engines found!"); qWarning("qmldebugger: no engines found!");
m_engineSpinBox->setEnabled(true); m_engineComboBox->setEnabled(true);
for (int i=0; i<engines.count(); ++i) for (int i=0; i<engines.count(); ++i)
m_engineSpinBox->addEngine(engines.at(i).debugId(), engines.at(i).name()); m_engineComboBox->addEngine(engines.at(i).debugId(), engines.at(i).name());
if (engines.count() > 0) { if (engines.count() > 0) {
m_engineSpinBox->setValue(engines.at(0).debugId()); m_engineComboBox->setCurrentIndex(engines.at(0).debugId());
queryEngineContext(engines.at(0).debugId()); queryEngineContext(engines.at(0).debugId());
} }
} }

View File

@@ -64,7 +64,7 @@ namespace Core {
namespace Qml { namespace Qml {
namespace Internal { namespace Internal {
class EngineSpinBox; class EngineComboBox;
class InspectorContext; class InspectorContext;
class ObjectTree; class ObjectTree;
class ObjectPropertiesView; class ObjectPropertiesView;
@@ -72,7 +72,6 @@ namespace Qml {
class WatchTableView; class WatchTableView;
class CanvasFrameRate; class CanvasFrameRate;
class ExpressionQueryWidget; class ExpressionQueryWidget;
class EngineSpinBox;
} }
const int MaxConnectionAttempts = 50; const int MaxConnectionAttempts = 50;
@@ -132,7 +131,7 @@ private:
Internal::CanvasFrameRate *m_frameRateWidget; Internal::CanvasFrameRate *m_frameRateWidget;
Internal::ExpressionQueryWidget *m_expressionWidget; Internal::ExpressionQueryWidget *m_expressionWidget;
Internal::EngineSpinBox *m_engineSpinBox; Internal::EngineComboBox *m_engineComboBox;
QDockWidget *m_objectTreeDock; QDockWidget *m_objectTreeDock;
QDockWidget *m_frameRateDock; QDockWidget *m_frameRateDock;