Merge branch 'mart/modelsInDataEngine'

This commit is contained in:
Marco Martin 2014-01-02 11:05:20 +01:00
commit 84df832f5c
14 changed files with 259 additions and 5 deletions

View File

@ -1,5 +1,6 @@
plasma_install_package(config org.kde.example.configuration)
plasma_install_package(dataenginemodel org.kde.example.dataenginemodel)
plasma_install_package(notes org.kde.example.notes)
plasma_install_package(widgetgallery org.kde.example.widgetgallery)
plasma_install_package(qmltasks org.kde.example.tasks)

View File

@ -0,0 +1,55 @@
// -*- coding: iso-8859-1 -*-
/*
* Copyright 2012 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
Column {
width: 500
height: 500
property int minimumWidth: 200
property int minimumHeight: 300
PlasmaCore.DataSource {
id: source
dataEngine: "org.kde.examples.sourcesOnRequest"
interval: 1000
connectedSources: "test"
}
PlasmaComponents.Label {
text: source.data.test["Update Count"]
}
PlasmaExtras.ScrollArea {
anchors {
left: parent.left
right: parent.right
}
height: 500
ListView {
model: source.models.test
delegate: PlasmaComponents.Label {
text: model.display
}
}
}
}

View File

@ -0,0 +1,20 @@
[Desktop Entry]
Comment=Example applet that shows how to use Models embeeded in DataEngines
Encoding=UTF-8
Keywords=
Name=Dataengine model
Type=Service
Icon=
X-KDE-ParentApp=
X-KDE-PluginInfo-Author=Marco Martin
X-KDE-PluginInfo-Category=Miscellaneous
X-KDE-PluginInfo-Email=mart@kde.org
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-Name=org.kde.example.dataenginemodel
X-KDE-PluginInfo-Version=
X-KDE-PluginInfo-Website=
X-KDE-ServiceTypes=Plasma/Applet
X-Plasma-API=declarativeappletscript
X-Plasma-MainScript=ui/main.qml
X-Plasma-RemoteLocation=

View File

@ -12,5 +12,5 @@ target_link_libraries(plasma_dataengine_example_customDataContainers
KF5::Service
)
install(TARGETS plasma_dataengine_example_customDataContainers DESTINATION ${PLUGIN_INSTALL_DIR})
install(TARGETS plasma_dataengine_example_customDataContainers DESTINATION ${PLUGIN_INSTALL_DIR}/plasma/dataengine)
install(FILES plasma-dataengine-example-customDataContainers.desktop DESTINATION ${SERVICES_INSTALL_DIR} )

View File

@ -12,5 +12,5 @@ target_link_libraries(plasma_dataengine_example_simpleEngine
KF5::I18n
)
install(TARGETS plasma_dataengine_example_simpleEngine DESTINATION ${PLUGIN_INSTALL_DIR})
install(TARGETS plasma_dataengine_example_simpleEngine DESTINATION ${PLUGIN_INSTALL_DIR}/plasma/dataengine)
install(FILES plasma-dataengine-example-simpleEngine.desktop DESTINATION ${SERVICES_INSTALL_DIR} )

View File

@ -11,5 +11,5 @@ target_link_libraries(plasma_dataengine_example_sourcesOnRequest
KF5::Service
)
install(TARGETS plasma_dataengine_example_sourcesOnRequest DESTINATION ${PLUGIN_INSTALL_DIR})
install(TARGETS plasma_dataengine_example_sourcesOnRequest DESTINATION ${PLUGIN_INSTALL_DIR}/plasma/dataengine)
install(FILES plasma-dataengine-example-sourcesOnRequest.desktop DESTINATION ${SERVICES_INSTALL_DIR} )

View File

@ -27,9 +27,12 @@
#include <Plasma/DataContainer>
#include <QStandardItemModel>
/*
This DataEngine shows how to created sources on demand as they are requested
and update them on visualization-requested update intervals.
It also shows how to bind and arbitrary QAbstractItemModel to a source
*/
SourcesOnRequestEngine::SourcesOnRequestEngine(QObject *parent, const QVariantList &args)
@ -61,6 +64,14 @@ bool SourcesOnRequestEngine::sourceRequestEvent(const QString &source)
// expects. So ALWAYS key the new data by the source string as below:
setData(source, "Update Count", 0);
if (!modelForSource(source)) {
QStandardItemModel *m = new QStandardItemModel;
m->appendRow(new QStandardItem("Item1, first update"));
m->appendRow(new QStandardItem("Item2, first update"));
m->appendRow(new QStandardItem("Item3, first update"));
setModel(source, m);
}
// as we successfully set up the source, return true
return true;
}
@ -81,7 +92,16 @@ bool SourcesOnRequestEngine::updateSourceEvent(const QString &source)
// sourceRequestEvent, however, this will result in expected behavior: visualizations
// connected to the sources which have setData called for them will be notified
// of these changes.
setData(source, "Update Count", containerForSource(source)->data().value("Update Count").toInt() + 1);
const int updateCount = containerForSource(source)->data().value("Update Count").toInt() + 1;
setData(source, "Update Count", updateCount);
QStandardItemModel *m = qobject_cast<QStandardItemModel *>(modelForSource(source));
if (m) {
m->clear();
m->appendRow(new QStandardItem(QString("Item1, update %1").arg(updateCount)));
m->appendRow(new QStandardItem(QString("Item2, update %1").arg(updateCount)));
m->appendRow(new QStandardItem(QString("Item3, update %1").arg(updateCount)));
}
// Since we updated the source immediately here, we need to return true so the DataEngine
// knows to continue with the update notification for visualizations.

View File

@ -30,6 +30,7 @@ DataSource::DataSource(QObject* parent)
m_dataEngine(0),
m_dataEngineConsumer(0)
{
m_models = new QQmlPropertyMap(this);
setObjectName("DataSource");
}
@ -148,9 +149,24 @@ void DataSource::dataUpdated(const QString &sourceName, const Plasma::DataEngine
}
}
void DataSource::modelChanged(const QString &sourceName, QAbstractItemModel *model)
{
if (!model) {
m_models->clear(sourceName);
return;
}
m_models->insert(sourceName, QVariant::fromValue(model));
//FIXME: this will break in the case a second model is set
connect(model, &QObject::destroyed, [=]() {
m_models->clear(sourceName);
});
}
void DataSource::removeSource(const QString &source)
{
m_data.remove(source);
m_models->clear(source);
//TODO: emit those signals as last thing
if (m_connectedSources.contains(source)) {

View File

@ -89,11 +89,19 @@ public:
/**
* All the data fetched by this dataengine.
* This is a map of maps. At the first level, there are the source names, at the secons, they keys set by the DataEngine
* This is a map of maps. At the first level, there are the source names, at the second, they keys set by the DataEngine
*/
Q_PROPERTY(QVariantMap data READ data NOTIFY dataChanged);
QVariantMap data() const {return m_data;}
/**
* All the models associated to this DataEngine, indexed by source.
* In order for a model to be present, besides being implemented in the DataEngine,
* The user has to be connected to its source, so the source name has to be present in the connectedSources property.
*/
Q_PROPERTY(QQmlPropertyMap *models READ models CONSTANT);
QQmlPropertyMap *models() const {return m_models;}
/**
* @returns a Plasma::Service given a source name
* @arg QString source source name we want a service of
@ -112,6 +120,7 @@ public:
protected Q_SLOTS:
void dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data);
void modelChanged(const QString &sourceName, QAbstractItemModel *model);
void removeSource(const QString &source);
void setupData();
@ -132,6 +141,7 @@ private:
int m_interval;
QString m_engine;
QVariantMap m_data;
QQmlPropertyMap *m_models;
Plasma::DataEngine* m_dataEngine;
Plasma::DataEngineConsumer* m_dataEngineConsumer;
QStringList m_connectedSources;

View File

@ -21,6 +21,7 @@
#include "private/storage_p.h"
#include <QDebug>
#include <QAbstractItemModel>
#include "plasma.h"
@ -65,6 +66,26 @@ void DataContainer::setData(const QString &key, const QVariant &value)
setNeedsToBeStored(true);
}
void DataContainer::setModel(QAbstractItemModel *model)
{
if (d->model.data() == model) {
return;
}
if (d->model) {
d->model.data()->deleteLater();
}
d->model = model;
model->setParent(this);
emit modelChanged(objectName(), model);
}
QAbstractItemModel *DataContainer::model()
{
return d->model.data();
}
void DataContainer::removeAllData()
{
if (d->data.isEmpty()) {
@ -109,6 +130,9 @@ void DataContainer::connectVisualization(QObject *visualization, uint pollingInt
} else {
disconnect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)),
visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data)));
//modelChanged is always emitted by the dataSource since there is no polling there
disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)),
visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
//relay->isUnused();
}
} else if (pollingInterval < 1) {
@ -120,6 +144,8 @@ void DataContainer::connectVisualization(QObject *visualization, uint pollingInt
} else {
disconnect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)),
visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data)));
disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)),
visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
}
} else {
connect(visualization, SIGNAL(destroyed(QObject*)),
@ -131,6 +157,8 @@ void DataContainer::connectVisualization(QObject *visualization, uint pollingInt
d->relayObjects[visualization] = 0;
connect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)),
visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data)));
connect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)),
visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
} else {
//qDebug() << " connecting to a relay";
// we only want to do an imediate update if this is not the first object to connect to us
@ -141,6 +169,9 @@ void DataContainer::connectVisualization(QObject *visualization, uint pollingInt
alignment, immediateUpdate);
connect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)),
visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data)));
//modelChanged is always emitted by the dataSource since there is no polling there
connect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)),
visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
}
}
@ -271,6 +302,8 @@ void DataContainer::disconnectVisualization(QObject *visualization)
// it is connected directly to the DataContainer itself
disconnect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)),
visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data)));
disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)),
visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
} else {
SignalRelay *relay = objIt.value();
@ -280,6 +313,9 @@ void DataContainer::disconnectVisualization(QObject *visualization)
} else {
disconnect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)),
visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data)));
//modelChanged is always emitted by the dataSource since there is no polling there
disconnect(this, SIGNAL(modelChanged(QString, QAbstractItemModel *)),
visualization, SLOT(modelChanged(QString, QAbstractItemModel *)));
}
}
@ -344,6 +380,12 @@ void DataContainer::timerEvent(QTimerEvent * event)
if (!isUsed()) {
// DO NOT CALL ANYTHING AFTER THIS LINE AS IT MAY GET DELETED!
//qDebug() << objectName() << "is unused";
//NOTE: Notifying visualization of the model destruction before actual deletion avoids crashes in some edge cases
if (d->model) {
d->model.clear();
emit modelChanged(objectName(), 0);
}
emit becameUnused(objectName());
}
d->checkUsageTimer.stop();

View File

@ -28,6 +28,8 @@
#include <plasma/plasma_export.h>
#include <plasma/dataengine.h>
class QAbstractItemModel;
namespace Plasma
{
@ -106,6 +108,26 @@ class PLASMA_EXPORT DataContainer : public QObject
**/
void removeAllData();
/**
* Associates a model with this DataContainer. Use this for data
* that is intended to be a long list of items.
*
* The ownership of the model is transferred to the DataContainer,
* so the model will be deletd when a new one is set or when the
* DataContainer itself is deleted, so it will be deleted when there won't be any
* visualization associated to this source.
*
* Normally you should set the model from DataEngine::setModel instead from here.
*
* @param model the model that will be associated with this DataContainer
*/
void setModel(QAbstractItemModel *model);
/**
* @return the model owned by this DataSource
*/
QAbstractItemModel *model();
/**
* @return true if the visualization is currently connected
*/
@ -188,6 +210,16 @@ class PLASMA_EXPORT DataContainer : public QObject
**/
void dataUpdated(const QString &source, const Plasma::DataEngine::Data &data);
/**
* A new model has been associated to this source,
* visualizations can safely use it as long they are connected to this source.
*
* @param source the objectName() of the DataContainer (and hence the name
* of the source) that owns the model
* @param model the QAbstractItemModel instance
*/
void modelChanged(const QString &source, QAbstractItemModel *model);
/**
* Emitted when the last visualization is disconnected.
*

View File

@ -21,6 +21,7 @@
#include "private/dataengine_p.h"
#include "private/datacontainer_p.h"
#include <QAbstractItemModel>
#include <QQueue>
#include <QTimer>
#include <QTime>
@ -227,6 +228,32 @@ void DataEngine::removeData(const QString &source, const QString &key)
}
}
void DataEngine::setModel(const QString &source, QAbstractItemModel *model)
{
if (model) {
setData(source, "HasModel", true);
} else {
removeData(source, "HasModel");
}
Plasma::DataContainer *s = containerForSource(source);
if (s) {
s->setModel(model);
}
}
QAbstractItemModel *DataEngine::modelForSource(const QString &source)
{
Plasma::DataContainer *s = containerForSource(source);
if (s) {
return s->model();
} else {
return 0;
}
}
void DataEngine::addSource(DataContainer *source)
{
if (d->sources.contains(source->objectName())) {
@ -521,6 +548,11 @@ void DataEnginePrivate::connectSource(DataContainer *s, QObject *visualization,
QMetaObject::invokeMethod(visualization, "dataUpdated",
Q_ARG(QString, s->objectName()),
Q_ARG(Plasma::DataEngine::Data, s->data()));
if (s->d->model) {
QMetaObject::invokeMethod(visualization, "modelChanged",
Q_ARG(QString, s->objectName()),
Q_ARG(QAbstractItemModel *, s->d->model.data()));
}
s->d->dirty = false;
}
}

View File

@ -31,6 +31,8 @@
#include <plasma/plasma.h>
#include <plasma/service.h>
class QAbstractItemModel;
namespace Plasma
{
@ -175,6 +177,12 @@ Types::NoAlignment) const;
**/
Q_INVOKABLE DataContainer *containerForSource(const QString &source);
/**
* @return The model associated to a source if any. The ownership of the model stays with the DataContainer.
* Returns 0 if there isn't any model associated or if the source doesn't exists.
*/
QAbstractItemModel *modelForSource(const QString &source);
/**
* Returns true if this engine is valid, otherwise returns false
*
@ -306,6 +314,21 @@ Types::NoAlignment) const;
**/
void removeData(const QString &source, const QString &key);
/**
* Associates a model to a data source. If the source
* doesn't exist then it is created. The source will have the key "HasModel" to easily indicate there is a model present.
*
* The ownership of the model is transferred to the DataContainer,
* so the model will be deletd when a new one is set or when the
* DataContainer itself is deleted. As the DataContainer, it will be
* deleted when there won't be any
* visualization associated to this source.
*
* @param source the name of the data source
* @param model the model instance
*/
void setModel(const QString &source, QAbstractItemModel *model);
/**
* Adds an already constructed data source. The DataEngine takes
* ownership of the DataContainer object. The objectName of the source

View File

@ -27,6 +27,8 @@
#include <QtCore/QTime>
#include <QtCore/QBasicTimer>
#include <QAbstractItemModel>
class QTimer;
namespace Plasma
@ -87,6 +89,7 @@ public:
Storage* storage;
QBasicTimer storageTimer;
QBasicTimer checkUsageTimer;
QWeakPointer<QAbstractItemModel> model;
int storageCount;
bool dirty : 1;
bool cached : 1;