Merge branch 'mart/modelsInDataEngine'
This commit is contained in:
commit
84df832f5c
@ -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)
|
||||
|
55
examples/applets/dataenginemodel/contents/ui/main.qml
Normal file
55
examples/applets/dataenginemodel/contents/ui/main.qml
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
examples/applets/dataenginemodel/metadata.desktop
Normal file
20
examples/applets/dataenginemodel/metadata.desktop
Normal 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=
|
@ -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} )
|
||||
|
@ -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} )
|
||||
|
@ -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} )
|
||||
|
@ -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.
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user