I'm trying to trigger a distinct signal from a QSlider for when it increases or decreases. Unfortunately due to restrictions in the program i'm creating outside of my control I cannot track the value of the slider and just do a comparison.
Is there a way to do this?
If I understand you correctly, you can't use valueChanged(int) to decide if the value increased or decreased, since you can't track the value. However, you need to do this, since QSlider doesn't have distinct signals for an increased / decreased value.
Use this helper class to keep track of the current value. It remaps the signal valueChanged(int) of the provided target object to the new signals valueIncreased(int) and valueDecreased(int) telling the (absolute) difference.
class ValueChangeTracker : public QObject
{
Q_OBJECT
int v;
public:
ValueChangeTracker(QObject *target) :
QObject(target)
{
connect(target, SIGNAL(valueChanged(int)), SLOT(changed(int)));
v = target->property("value"); // or ->value() if you specifically use QSlider* instead of QObject* as the target type
}
signals:
void valueIncreased(int);
void valueDecreased(int);
private slots:
void changed(int newValue) {
int diff = newValue - v;
v = newValue;
if(diff > 0) emit valueIncreased(diff);
if(diff < 0) emit valueDecreased(-diff);
}
};
To use it, simply create a new instance of this class and connect to its signals. In the QObject tree, it becomes a child of the target object, so you don't have to keep the pointer to an instance.
QSlider *mySlider = ...
ValueChangeTracker *tracker = new ValueChangeTracker(mySlider);
connect(tracker, SIGNAL(valueIncreased(int)), ...);
connect(tracker, SIGNAL(valueDecreased(int)), ...);
QSlider emits the signal valueChanged, just connect it to a slot:
mySlider.valueChanged.connect(self.on_mySlider_valueChanged)
and to your tests there and emit a custom signal if you like.
I don't fully understand the question, maybe an example would help. But if you look at the documentation for QSlider, it emits signal valueChanged(int) when the value of the slider changes.
Related
In a function, how to understand the signal sent from Qtimer objects that I created in Qmap, how to find from which object the signal comes from in the slot function.
I created Qmap above code
SQL.h
public slots:
void experiment();
void run();
private:
QMap<QString,QTimer*> job;
I create QMap value and key with Qtimer.
SQL.cpp
void SQL::experiment()
{
QTimer *timer=new Qtimer();
job.insert("dd",timer);
QTimer *timer1=new Qtimer();
job.insert("ss",timer1);
job.value("dd")->start();
job.value("dd")->setInterval)(5000);
job.value("ss")->start();
job.value("ss")->setInterval)(10000);
connect(job.value("dd"),SIGNAL(timeout()),this,SLOT(run()));
connect(job.value("ss"),SIGNAL(timeout()),this,SLOT(run()));
}
In this slot, how can I understand which of the Qtimer in the Qmap receives a signal at that time?
void SQL::run()
{
//job.value(key) // how to understand key
}
I thought I could use sender() with Qmapiterator, but I couldn't find out how. can you help?
Old way is using QSignalMapper. You could also set the key as dynamic property of the timer object, so you could access it through QObject::sender(). But, today you should probably just use a lambda.
First, change the run slot to take any parameters you want:
void SQL::run(const QString &key)
{
QTimer *timer = job.value(key);
}
Then, just use lambda to easily pass the required parameters
QString name="ff";
connect(job.value(name), &QTimer::timeout, this, [this, name]() {
run(name);
});
// name is capture by value above,
// so changing name variable later does not
// affect the value captured by the lambda
As a side note, you shouldn't use the old SIGNAL() and SLOT() macros unless you really have to for some reason. Use the "new" (10 years old) connect syntax.
Let's say I have multiple input widgets to set up the same parameter. For example, there is a QSlider and a QSpinBox which need to show the same value. In the valueChanged() slot of one of them I call the setValue() of the other one.
Obviously, this would result in an endless loop of them calling each other.
A similar problem arises when this input widget controls some external resource or device. If the user changes the value, it will send the new value to the external device. But if the external device changes the value (or it is read from a settings file, etc) then I have to update the widget, which in turn will send the value, which in turn will update the widget, and so on.
A third scenario is when I save the values into a file or database, but I have to initialize the widgets to some value at the beginning, possibly before I got all the values from the database. But by initializing the widgets at the beginning of my program, they will write that dummy value into the database, overwriting the real values.
The obvious solution for these problems is to just have a bool which allows or forbids the side effects of the valueChanged() functions.
For example, if I want to change the value of my slider, I use
editing = true;
slider.setValue(value);
editing = false;
While I have if (editing) return; at the beginning of my valueChanged() function.
Assuming I didn't fiddle with setting up the signals and slots manually, but they were done by QtCreator, is there a danger of the slot being called later, for example after the editing flag is set to false again? I tried it, and it works, but I am unsure how guaranteed it is.
If you use direct connection (the default for objects in the same thread), the slot is called as soon as the signal is emitted, that is before the setValue method returns.
If you use Qt::QueuedConnection, the slot is invoked when control returns to the event loop of the receiver's thread.
See Qt::ConnectionType
The way I would go about solving this problem is by having another QObject that will be your data model. Your data will be centralized in your model, and will gotten/set via the model. This way your widgets wouldn't need to know about one another and can be created in separate places in your code as long as they can access your model.
Your model will have a method setValue and a signal valueChanged, so it will look some thing like this:
class Model : public QObject {
Q_OBJECT
public:
void setValue(const QVariant& value) {
if (_value != value) {
_value = value;
emit valueChanged(_value);
}
}
const QVariant& getValue() const {
return _value;
}
public signals:
void valueChanged(QVariant& value);
private:
QVariant _value;
}
Then your widgets can take the same instance of Model as a dependency and listen to its valueChanged signal and update themselves. The widgets will also listen to user input, and when the user changes the value then they will change the value in the model. That way the other widgets will get notified about the change.
Your widgets will look like this:
class MySlider : public QSlider {
Q_OBJECT
public:
explicit MySlider(QSharedPointer<Model> model, QWidget *parent=nullptr)
: QSlider(parent), _model(model) {
connect(this, &QSlider::valueChanged, this, [this](int value){
_model->setValue(value);
});
connect(_model.data(), &Model::valueChanged, this, &MySlider::onValueChanged);
//this is to update the widget with the latest value upon creation
onValueChanged(_model->getValue());
}
private slots:
void onValueChanged(const QVariant& value) {
if (value.toInt() != value()) {
//this is calling QSlider::setValue
setValue(value.toInt());
}
}
}
Before you create all your widgets you can create your model with the default value, so let's assume it's in main:
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
//note that your model doesn't have a parent, it's a shared pointer
auto model = QSharedPointer<Model>::create();
auto mySlider1 = new MySlider(model, &w);
auto mySlider2 = new MySlider(model, &w);
return a.exec();
}
P.S. You can also look into QDataWidgetMapper and see if it can accomplish what you're looking for.
I'm writing some wrappers over QStandardItemModel. Is it possible to track lifetime (delete events) of QStandardItems?
I think that the only way is to interhit QObject + QStandardItem. But I don't want to do it for some reasons.
UPDATE:
I need to delete my object, that contains pointer to QStandardItem, when this item removed from model.
Here is solution. But I want to do the same for external (not mine) QStandardItem.
class ItemWrap : public QObject, public QStandardItem
{
// ...
};
class MyObject : public QObject
{
MyObject( ItemWrap *item ) // I need MyObject( QStandardItem *item )
{
connect( item, &QObject::destroyed, this, &MyObject::deletelater );
}
// ...
};
As is often the case in Qt, there are objects that are not QObjects, but that are managed by a QObject (or otherwise accessible via one). You need to make MyObject monitor the model the items are in. The code below could be a starting point.
Another approach, not implemented but certainly feasible, is to dynamically replace all items in a model with copies that are instances that you yourself created. By monitoring the relevant model signals, you can be notified of all item additions and replace items with instances that you are a factory for. It would be a thinly veiled dependency injection into a QStandardItemModel.
The lowest-overhead approach would be to move the signals and slots from individual objects to the model itself, so that you avoid the overhead of having potentially very many QObjects, while still retaining their signal/slot functionality.
class MyObject : public QObject {
Q_OBJECT
QStandardItem * m_item;
Q_SLOT void onRowsAboutToBeRemoved(const QModelIndex & parent, int start, int end) {
if (m_item->parent() == parent &&
m_item->index().row() >= start &&
m_item->index().row() <= end) onItemGone;
}
Q_SLOT void onColumnsAboutToBeRemoved(const QModelIndex & parent, int start, int end) {
if (m_item->parent() == parent &&
m_item->index().column() >= start &&
m_item->index().column() <= end) onItemGone;
}
Q_SLOT void onItemGone() {
m_item = 0;
deleteLater();
}
public:
MyObject(QStandardItem* item, QObject * parent = 0) :
QObject(parent), m_item(item)
{
Q_ASSERT(m_item.model());
connect(m_item.model(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
SLOT(onRowsAboutToBeRemoved(QModelIndex,int,int)));
connect(m_item.model(), SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
SLOT(onColumnsAboutToBeRemoved(QModelIndex,int,int)));
connect(m_item.model(), SIGNAL(modelAboutToBeReset()), SLOT(onItemGone());
connect(m_item.model(), SIGNAL(destroyed()), SLOT(onItemGone());
}
};
Every data model has signals about changes in the model, see documentation of QAbstractItemModel and this is what you need.
Note that QStandardItem is not a QObject so it doesn't have any signals or slots.
Can you please clarify what you mean by track lifetime.
Why do you want to subclass QObject?
Are you going to use SIGNALS and SLOTs? If no then I don't think it is going to be of much use.
Apart from that you can subclass QStandardItem and track the lifetime using your constructor and destructor or a suitable function?
What is a simple way to display the actual time (Hh: mm: ss) in MainWindow's title ?
Using slots and signals technology.
I'm building my answer on the one provided by Riateche. Instead of using a user defined QTimer and connecting signals/slots, you can use the provided timerEvent() of any QObject. It will basically do the same under the hood, but save you a lot of typing. This would look like this:
class MainWindow : public QMainWindow
{
public:
MainWindow();
protected:
void timerEvent(QTimerEvent *event);
};
MainWindow::MainWindow()
{
startTimer(1000); // 1-second timer
}
void MainWindow::timerEvent(QTimerEvent * event)
{
setWindowTitle(QTime::currentTime().toString("hh:mm:ss"));
}
Create a QTimer with 1 sec interval (or e.g. 100 msec for more accuracy), connect its timeout signal to your slot. In the slot get the current time using QTime::currentTime() static function, convert it to string using toString and assign it to a GUI element (e.g. a label).
I got a from QWidget derived class that holds three QSpinBoxes (e.g. coordinates). The valueChanged() signal is connected and is emitted in at least these three cases:
up/down button
manually entered number
setValue()
However, when using setValue(), I want to suppress the signal(s), since I don't want to have (three) signals. In my understanding, there are two ways to handle this:
QObject::blockSignals()
using a flag that indicates whether the values were explicitely set
Both variants work, but I think they are not straight-forward at all: For the first one, I generally block all signals AND I need to set blockSignals(true) for all underyling widgets (blockSignals doesn't block children QObjects in my application). For the second one, I need to query the flag in every update method AND the signals are raised although I don't need them.
Are there any general designing patterns that prevent such behavior? If not, what variant would you prefer?
The third option would be to subclass QSpinBox, implement desired functionality there, and used derived class instead of QSpinBox - this hides all associated complexity in derived class and allows you use it just like QSpinBox.
For example, following class
myQSpinBox.h
#ifndef MYQSPINBOX_H
#define MYQSPINBOX_H
#include <QSpinBox>
class myQSpinBox : public QSpinBox
{
Q_OBJECT
public:
myQSpinBox(QWidget * parent = 0 );
protected:
bool valueBeingSet;
public slots:
void setValue (int val);
private slots:
void On_valueChanged(int val);
signals:
void valueChangedNotBySet(int val);
};
#endif // MYQSPINBOX_H
myQSpinBox.cpp
#include "myQSpinBox.h"
myQSpinBox::myQSpinBox(QWidget * parent)
: QSpinBox(parent)
, valueBeingSet(false)
{
connect(this,SIGNAL(valueChanged(int)),this,SLOT(On_valueChanged(int)));
}
void myQSpinBox::setValue ( int val )
{
valueBeingSet = true;
QSpinBox::setValue(val);
valueBeingSet = false;
}
void myQSpinBox::On_valueChanged(int val)
{
if(!valueBeingSet)
emit valueChangedNotBySet(val);
}
will emit valueChangedNotBySet(int); in cases 1. and 2., but not in case 3., keeping all QSpinBox functionality intact