diff --git a/datacontainer.cpp b/datacontainer.cpp index 765a0315d..130982866 100644 --- a/datacontainer.cpp +++ b/datacontainer.cpp @@ -17,6 +17,7 @@ */ #include "datacontainer.h" +#include "datacontainer_p.h" #include @@ -25,17 +26,6 @@ namespace Plasma { -class DataContainer::Private -{ - public: - Private() - : dirty(false) - {} - - DataEngine::Data data; - bool dirty : 1; -}; - DataContainer::DataContainer(QObject* parent) : QObject(parent), d(new Private()) @@ -86,20 +76,56 @@ void DataContainer::checkForUpdate() } } +QObject* DataContainer::signalRelay(QObject *visualization, uint updateInterval) const +{ + return d->signalRelay(this, visualization, updateInterval); +} + void DataContainer::checkUsage() { - if (receivers(SIGNAL(updated(QString, Plasma::DataEngine::Data))) < 1) { + if (d->relays.count() < 1 && + receivers(SIGNAL(updated(QString, Plasma::DataEngine::Data))) < 1) { // DO NOT CALL ANYTHING AFTER THIS LINE AS IT MAY GET DELETED! emit unused(objectName()); } } +void DataContainer::disconnectVisualization(QObject* visualization) +{ + QMap::iterator objIt = d->relayObjects.find(visualization); + + if (objIt == d->relayObjects.end()) { + // we will assume that it is connected directly to the DataContainer itself + disconnect(this, SIGNAL(updated(QString,Plasma::DataEngine::Data)), + visualization, SLOT(updated(QString,Plasma::DataEngine::Data))); + + // NOTE: we don't need to call checkUsage here as it will happen in disconnectNotify + // checkUsage() + } else { + SignalRelay *relay = objIt.value(); + d->relayObjects.erase(objIt); + objIt = d->relayObjects.end(); //for safety's sake? + + disconnect(relay, SIGNAL(updated(QString,Plasma::DataEngine::Data)), + visualization, SLOT(updated(QString,Plasma::DataEngine::Data))); + + if (relay->isUnused()) { + d->relays.erase(d->relays.find(relay->interval)); + delete relay; + } + + checkUsage(); + } +} + void DataContainer::disconnectNotify(const char *signal) { + Q_UNUSED(signal) checkUsage(); } } // Plasma namespace #include "datacontainer.moc" +#include "datacontainer_p.moc" diff --git a/datacontainer.h b/datacontainer.h index 1e39f1987..a1dbcda8b 100644 --- a/datacontainer.h +++ b/datacontainer.h @@ -80,6 +80,12 @@ class PLASMA_EXPORT DataContainer : public QObject **/ void checkForUpdate(); + /** + * Returns a QObject that can be used to relay the updated() signal + * at a given timing frequency independantly. + **/ + QObject* signalRelay(QObject *visualization, uint updateInterval) const; + public Q_SLOTS: /** * Check if the DataContainer is still in use. @@ -88,6 +94,11 @@ class PLASMA_EXPORT DataContainer : public QObject */ void checkUsage(); + /** + * Disconnects an object from this DataContainer. + **/ + void disconnectVisualization(QObject* visualization); + Q_SIGNALS: /** * Emitted when the data has been updated, allowing visualization to @@ -100,10 +111,17 @@ class PLASMA_EXPORT DataContainer : public QObject **/ void unused(const QString& source); + /** + * Emitted when the source, usually due to an internal timer firing, + * requests to be updated. + **/ + void requestUpdate(const QString& source); + protected: void disconnectNotify(const char *signal); private: + friend class SignalRelay; class Private; Private* const d; }; diff --git a/dataengine.cpp b/dataengine.cpp index c0d3d6454..5ca01f314 100644 --- a/dataengine.cpp +++ b/dataengine.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -35,11 +36,15 @@ class DataEngine::Private public: Private(DataEngine* e) : engine(e), + ref(0), + updateTimerId(0), + minUpdateFreq(-1), limit(0), valid(true) { updateTimer = new QTimer(engine); updateTimer->setSingleShot(true); + updateTimestamp.start(); } DataContainer* source(const QString& sourceName, bool createWhenMissing = true) @@ -71,6 +76,7 @@ class DataEngine::Private DataContainer* s = new DataContainer(engine); s->setObjectName(sourceName); sources.insert(sourceName, s); + connect(s, SIGNAL(requestUpdate(QString)), engine, SLOT(updateSource(QString))); if (limit > 0) { trimQueue(); @@ -80,6 +86,32 @@ class DataEngine::Private return s; } + void connectSource(const DataContainer* s, QObject* visualization, uint updateInterval) + { + connect(visualization, SIGNAL(destroyed(QObject*)), + s, SLOT(disconnectVisualization(QObject*)), Qt::QueuedConnection); + + if (updateInterval < 1) { + connect(s, SIGNAL(updated(QString,Plasma::DataEngine::Data)), + visualization, SLOT(updated(QString,Plasma::DataEngine::Data))); + } else { + // never more frequently than allowed + uint min = minUpdateFreq; // for qMin below + updateInterval = qMin(min, updateInterval); + + // never more than 20 times per second, and align on the 50ms + updateInterval = updateInterval - (updateInterval % 50); + + connect(s->signalRelay(visualization, updateInterval), + SIGNAL(updated(QString,Plasma::DataEngine::Data)), + visualization, SLOT(updated(QString,Plasma::DataEngine::Data))); + } + + QMetaObject::invokeMethod(visualization, "updated", + Q_ARG(QString, s->objectName()), + Q_ARG(Plasma::DataEngine::Data, s->data())); + } + DataContainer* requestSource(const QString& sourceName) { DataContainer* s = source(sourceName, false); @@ -92,6 +124,7 @@ class DataEngine::Private // now we have a source; since it was created on demand, assume // it should be removed when not used connect(s, SIGNAL(unused(QString)), engine, SLOT(removeSource(QString))); + connect(s, SIGNAL(requestUpdate(QString)), engine, SLOT(updateSource(QString))); } } } @@ -114,10 +147,13 @@ class DataEngine::Private updateTimer->start(0); } + DataEngine* engine; int ref; + int updateTimerId; + int minUpdateFreq; + QTime updateTimestamp; DataEngine::SourceDict sources; QQueue sourceQueue; - DataEngine* engine; QTimer* updateTimer; QString icon; uint limit; @@ -146,7 +182,7 @@ QStringList DataEngine::sources() const return d->sources.keys(); } -void DataEngine::connectSource(const QString& source, QObject* visualization) const +void DataEngine::connectSource(const QString& source, QObject* visualization, uint updateInterval) const { DataContainer* s = d->requestSource(source); @@ -154,12 +190,14 @@ void DataEngine::connectSource(const QString& source, QObject* visualization) co return; } - connect(visualization, SIGNAL(destroyed(QObject*)), s, SLOT(checkUsage()), Qt::QueuedConnection); - connect(s, SIGNAL(updated(QString,Plasma::DataEngine::Data)), - visualization, SLOT(updated(QString,Plasma::DataEngine::Data))); - QMetaObject::invokeMethod(visualization, "updated", - Q_ARG(QString, s->objectName()), - Q_ARG(Plasma::DataEngine::Data, s->data())); + d->connectSource(s, visualization, updateInterval); +} + +void DataEngine::connectAllSources(QObject* visualization, uint updateInterval) const +{ + foreach (const DataContainer* s, d->sources) { + d->connectSource(s, visualization, updateInterval); + } } void DataEngine::disconnectSource(const QString& source, QObject* visualization) const @@ -170,22 +208,7 @@ void DataEngine::disconnectSource(const QString& source, QObject* visualization) return; } - disconnect(s, SIGNAL(updated(QString,Plasma::DataEngine::Data)), - visualization, SLOT(updated(QString,Plasma::DataEngine::Data))); -} - -void DataEngine::connectAllSources(QObject* visualization) const -{ - foreach (const DataContainer* s, d->sources) { - connect(s, SIGNAL(updated(QString,Plasma::DataEngine::Data)), - visualization, SLOT(updated(QString,Plasma::DataEngine::Data))); - } - - foreach (const DataContainer* s, d->sources) { - QMetaObject::invokeMethod(visualization, "updated", - Q_ARG(QString, s->objectName()), - Q_ARG(Plasma::DataEngine::Data, s->data())); - } + s->disconnectVisualization(visualization); } DataContainer* DataEngine::containerForSource(const QString &source) @@ -224,6 +247,13 @@ bool DataEngine::sourceRequested(const QString &name) return false; } +bool DataEngine::updateSource(const QString& source) +{ + Q_UNUSED(source); + kDebug() << "updateSource source" << endl; + return false; //TODO: should this be true to trigger, even needless, updates on every tick? +} + void DataEngine::setData(const QString& source, const QVariant& value) { setData(source, source, value); @@ -293,6 +323,37 @@ void DataEngine::setSourceLimit(uint limit) } } +void DataEngine::setMinimumUpdateInterval(int minimumMs) +{ + d->minUpdateFreq = minimumMs; +} + +int DataEngine::minimumUpdateInterval() const +{ + return d->minUpdateFreq; +} + +void DataEngine::setupdateInterval(uint frequency) +{ + killTimer(d->updateTimerId); + d->updateTimerId = 0; + + if (frequency > 0) { + d->updateTimerId = startTimer(frequency); + } +} + +/* +NOTE: This is not implemented to prevent having to store the value internally. + When there is a good use case for needing access to this value, we can + add another member to the Private class and add this method. + +void DataEngine::updateInterval() +{ + return d->updateInterval; +} +*/ + void DataEngine::removeSource(const QString& source) { //kDebug() << "removing source " << source; @@ -345,6 +406,33 @@ DataEngine::SourceDict DataEngine::sourceDict() const return d->sources; } +void DataEngine::timerEvent(QTimerEvent *event) +{ + if (event->timerId() != d->updateTimerId) { + return; + } + + event->accept(); + + // if the freq update is less than 0, don't bother + if (d->minUpdateFreq < 0) { + return; + } + + // minUpdateFreq + if (d->updateTimestamp.elapsed() < d->minUpdateFreq) { + return; + } + + d->updateTimestamp.restart(); + QHashIterator it(d->sources); + while (it.hasNext()) { + it.next(); + updateSource(it.key()); + } + checkForUpdates(); +} + void DataEngine::setIcon(const QString& icon) { d->icon = icon; diff --git a/dataengine.h b/dataengine.h index 26667ee10..41d90aaad 100644 --- a/dataengine.h +++ b/dataengine.h @@ -87,16 +87,15 @@ class PLASMA_EXPORT DataEngine : public QObject * * @param source the name of the data source * @param visualization the object to connect the data source to + * @param updateInterval the frequency, in milliseconds, with which to signal updates; + * a value of 0 (the default) means to update only + * when there is new data spontaneously generated + * (e.g. by the engine); any other value results in + * periodic updates from this source. This value is + * per-visualization and can be handy for items that require + * constant updates such as scrolling graphs or clocks. **/ - Q_INVOKABLE void connectSource(const QString& source, QObject* visualization) const; - - /** - * Disconnects a source to an object that was receiving data updates. - * - * @param source the name of the data source - * @param visualization the object to connect the data source to - **/ - Q_INVOKABLE void disconnectSource(const QString& source, QObject* visualization) const; + Q_INVOKABLE void connectSource(const QString& source, QObject* visualization, uint updateInterval = 0) const; /** * Connects all sources to an object for data updates. The object must @@ -108,8 +107,23 @@ class PLASMA_EXPORT DataEngine : public QObject * one data source to provide sets of related data. * * @param visualization the object to connect the data source to + * @param updateInterval the frequency, in milliseconds, with which to signal updates; + * a value of 0 (the default) means to update only + * when there is new data spontaneously generated + * (e.g. by the engine); any other value results in + * periodic updates from this source. This value is + * per-visualization and can be handy for items that require + * constant updates such as scrolling graphs or clocks. **/ - Q_INVOKABLE void connectAllSources(QObject* viualization) const; + Q_INVOKABLE void connectAllSources(QObject* viualization, uint updateInterval = 0) const; + + /** + * Disconnects a source to an object that was receiving data updates. + * + * @param source the name of the data source + * @param visualization the object to connect the data source to + **/ + Q_INVOKABLE void disconnectSource(const QString& source, QObject* visualization) const; /** * Retrevies a pointer to the DataContainer for a given source. This method @@ -206,6 +220,18 @@ class PLASMA_EXPORT DataEngine : public QObject */ virtual bool sourceRequested(const QString &name); + /** + * Called by internal updating mechanisms to trigger the engine + * to refresh the data contained in a given source. Reimplement this + * method when using facilities such as setupdateInterval. + * @see setupdateInterval + * + * @param source the name of the source that should be updated + * @return true if the data was changed, or false if there was no + * change or if the change will occur later + **/ + virtual bool updateSource(const QString& source); + /** * Sets a value for a data source. If the source * doesn't exist then it is created. @@ -265,9 +291,42 @@ class PLASMA_EXPORT DataEngine : public QObject **/ void setSourceLimit(uint limit); -/* DataContainer* domain(const QString &domain); - void createDataContainer(const QString& source, - const QString& domain = QString());*/ + /** + * Sets the minimum amount of time, in milliseconds, that must pass between + * successive updates of data. This can help prevent too many updates happening + * due to multiple update requests coming in, which can be useful for + * expensive (time- or resource-wise) update mechanisms. + * + * @arg minimumMs the minimum time lapse, in milliseconds, between updates. + * A value less than 0 means to never perform automatic updates, + * a value of 0 means update immediately on every update request, + * a value >0 will result in a minimum time lapse being enforced. + **/ + void setMinimumUpdateInterval(int minimumMs); + + /** + * @return the minimum time between updates. @see setMinimumupdateInterval + **/ + int minimumUpdateInterval() const; + + /** + * Sets up an internal update tick for all data sources. On every update, + * updateSource will be called for each applicable source. + * @see updateSource + * + * @param frequency the time, in milliseconds, between updates. A value of 0 + * will stop internally triggered updates. + **/ + void setupdateInterval(uint frequency); + + /** + * Returns the current update frequency. + * @see setupdateInterval + NOTE: This is not implemented to prevent having to store the value internally. + When there is a good use case for needing access to this value, we can + add another member to the Private class and add this method. + uint updateInterval(); + **/ /** * Removes all data sources @@ -288,6 +347,11 @@ class PLASMA_EXPORT DataEngine : public QObject */ SourceDict sourceDict() const; + /** + * Reimplemented from QObject + **/ + void timerEvent(QTimerEvent *event); + protected Q_SLOTS: /** * Call this method when you call setData directly on a DataContainer instead