KUserFeedback

propertyratiosource.cpp
1/*
2 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: MIT
5*/
6
7#include "propertyratiosource.h"
8#include "abstractdatasource_p.h"
9#include "logging_p.h"
10
11#include <QDebug>
12#include <QHash>
13#include <QMap>
14#include <QMetaProperty>
15#include <QObject>
16#include <QPointer>
17#include <QSettings>
18#include <QStringList>
19#include <QTime>
20#include <QElapsedTimer>
21
22using namespace KUserFeedback;
23
24namespace KUserFeedback {
25class PropertyRatioSourcePrivate : public AbstractDataSourcePrivate
26{
27public:
28 PropertyRatioSourcePrivate();
29 ~PropertyRatioSourcePrivate() override;
30
31 void propertyChanged();
32 QString valueToString(const QVariant &value) const;
33 void trySetup();
34
35 QString name;
36 QString description;
38 QByteArray propertyName;
39 QObject *signalMonitor;
40 QMetaProperty property;
41 QString previousValue;
42 QElapsedTimer lastChangeTime;
43 QHash<QString, int> ratioSet; // data we are currently tracking
44 QHash<QString, int> baseRatioSet; // data loaded from storage
46};
47
48// inefficient workaround for not being able to connect QMetaMethod to a function directly
49class SignalMonitor : public QObject
50{
52public:
53 explicit SignalMonitor(PropertyRatioSourcePrivate *r) : m_receiver(r) {}
54public Q_SLOTS:
55 void propertyChanged()
56 {
57 m_receiver->propertyChanged();
58 }
59private:
60 PropertyRatioSourcePrivate *m_receiver;
61};
62
63}
64
65PropertyRatioSourcePrivate::PropertyRatioSourcePrivate()
66 : obj(nullptr)
67 , signalMonitor(nullptr)
68{
69}
70
71PropertyRatioSourcePrivate::~PropertyRatioSourcePrivate()
72{
73 delete signalMonitor;
74}
75
76void PropertyRatioSourcePrivate::propertyChanged()
77{
78 if (!previousValue.isEmpty() && lastChangeTime.elapsed() > 1000) {
79 ratioSet[previousValue] += lastChangeTime.elapsed() / 1000;
80 }
81
82 lastChangeTime.start();
83 previousValue = valueToString(property.read(obj));
84}
85
86QString PropertyRatioSourcePrivate::valueToString(const QVariant &value) const
87{
88 const auto it = std::find_if(valueMap.constBegin(), valueMap.constEnd(), [value](QPair<QVariant, QString> p) {
89 return p.first == value;
90 });
91 if (it != valueMap.constEnd()) {
92 return it->second;
93 }
94 return value.toString();
95}
96
97void PropertyRatioSourcePrivate::trySetup()
98{
99 if (!obj || propertyName.isEmpty())
100 return;
101
102 auto idx = obj->metaObject()->indexOfProperty(propertyName.constData());
103 Q_ASSERT(idx >= 0);
104 if (idx < 0) {
105 qCWarning(Log) << "Property" << propertyName << "not found in" << obj << "!";
106 return;
107 }
108
109 property = obj->metaObject()->property(idx);
110 if (!property.hasNotifySignal()) {
111 qCWarning(Log) << "Property" << propertyName << "has no notification signal!";
112 return;
113 }
114
115 idx = signalMonitor->metaObject()->indexOfMethod("propertyChanged()");
116 Q_ASSERT(idx >= 0);
117 const auto propertyChangedMethod = signalMonitor->metaObject()->method(idx);
118 QObject::connect(obj, property.notifySignal(), signalMonitor, propertyChangedMethod);
119
120 lastChangeTime.start();
121 propertyChangedMethod.invoke(signalMonitor, Qt::QueuedConnection);
122}
123
124PropertyRatioSource::PropertyRatioSource(QObject *obj, const char *propertyName, const QString &sampleName) :
125 AbstractDataSource(sampleName, Provider::DetailedUsageStatistics, new PropertyRatioSourcePrivate)
126{
128
129 d->obj = obj;
130 d->propertyName = propertyName;
131 d->signalMonitor = new SignalMonitor(d);
132 d->trySetup();
133}
134
136{
138 return d->obj;
139}
140
142{
144 if (d->obj == object)
145 return;
146 d->obj = object;
147 d->trySetup();
148}
149
151{
153 return QString::fromUtf8(d->propertyName.constData());
154}
155
157{
159 const auto n = name.toUtf8();
160 if (d->propertyName == n)
161 return;
162 d->propertyName = n;
163 d->trySetup();
164}
165
167{
169 auto it = std::find_if(d->valueMap.begin(), d->valueMap.end(), [value](QPair<QVariant, QString> p) {
170 return p.first == value;
171 });
172 if (it != d->valueMap.end()) {
173 it->second = str;
174 } else {
175 d->valueMap.append(QPair<QVariant, QString>(value, str));
176 }
177}
178
180{
182 return d->name;
183}
184
186{
188 d->name = name;
189}
190
192{
194 return d->description;
195}
196
198{
200 d->description = desc;
201}
202
204{
206 d->propertyChanged();
207
208 QVariantMap m;
209 int total = 0;
210 for (auto it = d->ratioSet.constBegin(); it != d->ratioSet.constEnd(); ++it)
211 total += it.value() + d->baseRatioSet.value(it.key());
212 if (total <= 0)
213 return m;
214
215 for (auto it = d->ratioSet.constBegin(); it != d->ratioSet.constEnd(); ++it) {
216 double currentValue = it.value() + d->baseRatioSet.value(it.key());
217 QVariantMap v;
218 v.insert(QStringLiteral("property"), currentValue / (double)(total));
219 m.insert(it.key(), v);
220 }
221
222 return m;
223}
224
226{
228 foreach (const auto &value, settings->childKeys()) {
229 const auto amount = std::max(settings->value(value, 0).toInt(), 0);
230 d->baseRatioSet.insert(value, amount);
231 if (!d->ratioSet.contains(value))
232 d->ratioSet.insert(value, 0);
233 }
234}
235
237{
239 d->propertyChanged();
240
241 // note that a second process can have updated the data meanwhile!
242 for (auto it = d->ratioSet.begin(); it != d->ratioSet.end(); ++it) {
243 if (it.value() == 0)
244 continue;
245 const auto oldValue = std::max(settings->value(it.key(), 0).toInt(), 0);
246 const auto newValue = oldValue + it.value();
247 settings->setValue(it.key(), newValue);
248 *it = 0;
249 d->baseRatioSet.insert(it.key(), newValue);
250 }
251}
252
254{
256 d->baseRatioSet.clear();
257 d->ratioSet.clear();
258 settings->remove(QString());
259}
260
261#include "propertyratiosource.moc"
Base class for data sources for telemetry data.
Records the time ratio a given QObject property has a specific value.
void setName(const QString &name)
Set human readable name.
QString description() const override
Returns a human-readable, translated description of what this source provides.
void addValueMapping(const QVariant &value, const QString &str)
Map property value value to str for sending to the server.
void resetImpl(QSettings *settings) override
Invoked by reset() in order to reset individual settings of this data source.
void storeImpl(QSettings *settings) override
Invoked by store() in order to store individual settings of this data source.
void setDescription(const QString &desc)
Set human-readable and translated description of the data provided by this source.
QString propertyName() const
Returns the property name.
QObject * object() const
Returns the monitored object.
QVariant data() override
Returns the data gathered by this source.
QString name() const override
Returns a short name of this data source.
void loadImpl(QSettings *settings) override
Invoked by load() in order to load individual settings of this data source.
PropertyRatioSource(QObject *obj, const char *propertyName, const QString &sampleName)
Create a new property ratio data source.
void setPropertyName(const QString &name)
Sets the property name that should be monitored.
void setObject(QObject *object)
Sets the monitoried object.
The central object managing data sources and transmitting feedback to the server.
Definition provider.h:32
Classes for integrating telemetry collection, survey targeting, and contribution encouragenemt and co...
const char * constData() const const
bool isEmpty() const const
qint64 elapsed() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
int indexOfMethod(const char *method) const const
QMetaMethod method(int index) const const
bool hasNotifySignal() const const
QMetaMethod notifySignal() const const
QVariant read(const QObject *object) const const
Q_OBJECTQ_OBJECT
Q_SLOTSQ_SLOTS
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual const QMetaObject * metaObject() const const
QStringList childKeys() const const
void remove(QAnyStringView key)
void setValue(QAnyStringView key, const QVariant &value)
QVariant value(QAnyStringView key) const const
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QByteArray toUtf8() const const
QueuedConnection
int toInt(bool *ok) const const
QString toString() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:18:51 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.