first run at per-visualization timeouts, as per the design worked out on panel-devel.

i'm sure there are several bugs at this point (so don't try and use it quite yet ;) but most importantly:

- all methods are implemented
- it compiles ;)
- it does not have any negative impact on existing engines and applets as they currently are written

next will be to put it all through its paces, starting with the time engine and analog clock applet. i may not get to that until tomorrow, hoever.

this commit also fixed a number of buglettes along the way that had creeped in, e.g. not properly connecting all signals in connectAllSources (moved that ccode to the Private class to avoid code dupe and similar bugs in the future)

svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=710849
This commit is contained in:
Aaron J. Seigo 2007-09-11 00:49:51 +00:00
parent 1cf7d7c0f6
commit fc89eff135
4 changed files with 245 additions and 49 deletions

View File

@ -17,6 +17,7 @@
*/ */
#include "datacontainer.h" #include "datacontainer.h"
#include "datacontainer_p.h"
#include <QVariant> #include <QVariant>
@ -25,17 +26,6 @@
namespace Plasma namespace Plasma
{ {
class DataContainer::Private
{
public:
Private()
: dirty(false)
{}
DataEngine::Data data;
bool dirty : 1;
};
DataContainer::DataContainer(QObject* parent) DataContainer::DataContainer(QObject* parent)
: QObject(parent), : QObject(parent),
d(new Private()) 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() 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! // DO NOT CALL ANYTHING AFTER THIS LINE AS IT MAY GET DELETED!
emit unused(objectName()); emit unused(objectName());
} }
} }
void DataContainer::disconnectVisualization(QObject* visualization)
{
QMap<QObject *, SignalRelay *>::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) void DataContainer::disconnectNotify(const char *signal)
{ {
Q_UNUSED(signal)
checkUsage(); checkUsage();
} }
} // Plasma namespace } // Plasma namespace
#include "datacontainer.moc" #include "datacontainer.moc"
#include "datacontainer_p.moc"

View File

@ -80,6 +80,12 @@ class PLASMA_EXPORT DataContainer : public QObject
**/ **/
void checkForUpdate(); 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: public Q_SLOTS:
/** /**
* Check if the DataContainer is still in use. * Check if the DataContainer is still in use.
@ -88,6 +94,11 @@ class PLASMA_EXPORT DataContainer : public QObject
*/ */
void checkUsage(); void checkUsage();
/**
* Disconnects an object from this DataContainer.
**/
void disconnectVisualization(QObject* visualization);
Q_SIGNALS: Q_SIGNALS:
/** /**
* Emitted when the data has been updated, allowing visualization to * 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); 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: protected:
void disconnectNotify(const char *signal); void disconnectNotify(const char *signal);
private: private:
friend class SignalRelay;
class Private; class Private;
Private* const d; Private* const d;
}; };

View File

@ -21,6 +21,7 @@
#include <QQueue> #include <QQueue>
#include <QTimer> #include <QTimer>
#include <QTimerEvent>
#include <QVariant> #include <QVariant>
#include <KDebug> #include <KDebug>
@ -35,11 +36,15 @@ class DataEngine::Private
public: public:
Private(DataEngine* e) Private(DataEngine* e)
: engine(e), : engine(e),
ref(0),
updateTimerId(0),
minUpdateFreq(-1),
limit(0), limit(0),
valid(true) valid(true)
{ {
updateTimer = new QTimer(engine); updateTimer = new QTimer(engine);
updateTimer->setSingleShot(true); updateTimer->setSingleShot(true);
updateTimestamp.start();
} }
DataContainer* source(const QString& sourceName, bool createWhenMissing = true) DataContainer* source(const QString& sourceName, bool createWhenMissing = true)
@ -71,6 +76,7 @@ class DataEngine::Private
DataContainer* s = new DataContainer(engine); DataContainer* s = new DataContainer(engine);
s->setObjectName(sourceName); s->setObjectName(sourceName);
sources.insert(sourceName, s); sources.insert(sourceName, s);
connect(s, SIGNAL(requestUpdate(QString)), engine, SLOT(updateSource(QString)));
if (limit > 0) { if (limit > 0) {
trimQueue(); trimQueue();
@ -80,6 +86,32 @@ class DataEngine::Private
return s; 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* requestSource(const QString& sourceName)
{ {
DataContainer* s = source(sourceName, false); DataContainer* s = source(sourceName, false);
@ -92,6 +124,7 @@ class DataEngine::Private
// now we have a source; since it was created on demand, assume // now we have a source; since it was created on demand, assume
// it should be removed when not used // it should be removed when not used
connect(s, SIGNAL(unused(QString)), engine, SLOT(removeSource(QString))); 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); updateTimer->start(0);
} }
DataEngine* engine;
int ref; int ref;
int updateTimerId;
int minUpdateFreq;
QTime updateTimestamp;
DataEngine::SourceDict sources; DataEngine::SourceDict sources;
QQueue<DataContainer*> sourceQueue; QQueue<DataContainer*> sourceQueue;
DataEngine* engine;
QTimer* updateTimer; QTimer* updateTimer;
QString icon; QString icon;
uint limit; uint limit;
@ -146,7 +182,7 @@ QStringList DataEngine::sources() const
return d->sources.keys(); 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); DataContainer* s = d->requestSource(source);
@ -154,12 +190,14 @@ void DataEngine::connectSource(const QString& source, QObject* visualization) co
return; return;
} }
connect(visualization, SIGNAL(destroyed(QObject*)), s, SLOT(checkUsage()), Qt::QueuedConnection); d->connectSource(s, visualization, updateInterval);
connect(s, SIGNAL(updated(QString,Plasma::DataEngine::Data)), }
visualization, SLOT(updated(QString,Plasma::DataEngine::Data)));
QMetaObject::invokeMethod(visualization, "updated", void DataEngine::connectAllSources(QObject* visualization, uint updateInterval) const
Q_ARG(QString, s->objectName()), {
Q_ARG(Plasma::DataEngine::Data, s->data())); foreach (const DataContainer* s, d->sources) {
d->connectSource(s, visualization, updateInterval);
}
} }
void DataEngine::disconnectSource(const QString& source, QObject* visualization) const void DataEngine::disconnectSource(const QString& source, QObject* visualization) const
@ -170,22 +208,7 @@ void DataEngine::disconnectSource(const QString& source, QObject* visualization)
return; return;
} }
disconnect(s, SIGNAL(updated(QString,Plasma::DataEngine::Data)), s->disconnectVisualization(visualization);
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()));
}
} }
DataContainer* DataEngine::containerForSource(const QString &source) DataContainer* DataEngine::containerForSource(const QString &source)
@ -224,6 +247,13 @@ bool DataEngine::sourceRequested(const QString &name)
return false; 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) void DataEngine::setData(const QString& source, const QVariant& value)
{ {
setData(source, source, 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) void DataEngine::removeSource(const QString& source)
{ {
//kDebug() << "removing source " << source; //kDebug() << "removing source " << source;
@ -345,6 +406,33 @@ DataEngine::SourceDict DataEngine::sourceDict() const
return d->sources; 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<QString, Plasma::DataContainer*> it(d->sources);
while (it.hasNext()) {
it.next();
updateSource(it.key());
}
checkForUpdates();
}
void DataEngine::setIcon(const QString& icon) void DataEngine::setIcon(const QString& icon)
{ {
d->icon = icon; d->icon = icon;

View File

@ -87,16 +87,15 @@ class PLASMA_EXPORT DataEngine : public QObject
* *
* @param source the name of the data source * @param source the name of the data source
* @param visualization the object to connect the data source to * @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; Q_INVOKABLE void connectSource(const QString& source, QObject* visualization, 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;
/** /**
* Connects all sources to an object for data updates. The object must * 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. * one data source to provide sets of related data.
* *
* @param visualization the object to connect the data source to * @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 * 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); 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 * Sets a value for a data source. If the source
* doesn't exist then it is created. * doesn't exist then it is created.
@ -265,9 +291,42 @@ class PLASMA_EXPORT DataEngine : public QObject
**/ **/
void setSourceLimit(uint limit); void setSourceLimit(uint limit);
/* DataContainer* domain(const QString &domain); /**
void createDataContainer(const QString& source, * Sets the minimum amount of time, in milliseconds, that must pass between
const QString& domain = QString());*/ * 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 * Removes all data sources
@ -288,6 +347,11 @@ class PLASMA_EXPORT DataEngine : public QObject
*/ */
SourceDict sourceDict() const; SourceDict sourceDict() const;
/**
* Reimplemented from QObject
**/
void timerEvent(QTimerEvent *event);
protected Q_SLOTS: protected Q_SLOTS:
/** /**
* Call this method when you call setData directly on a DataContainer instead * Call this method when you call setData directly on a DataContainer instead