I'm writig a code using qt libraries, in which I need to get the value of a spin box (by a signal) just before it changes.
I've got:
QSpinBox spinBoxWidth:
QSpinBox spinBoxScale;
I want to connect a signal from spinBoxWidth to spinBoxScale, so that the value of SpinBoxScale is always "the Value of SpinBoxWidth after changing" to "its value before changing".
(Scale = width_new/width_old)
I didn't find any slot in Qt which returns the old value of a spin box while changing the value. Can I somehow write a slot for that?
Best Regards
There are two ways of doing this:
Catch the change before it happens and store the old value using the event system (QKeyEvent, QMouseEvent). This is error-prone, as the value of spinBoxWidth can be set manually.
Connect spinBoxWidth's valueChanged(int) signal to a slot and reference the last value it was called with. I recommend this method.
Try something like this:
class MonitoringObject : public QObject
{
Q_OBJECT
int lastValue;
int currentValue;
...
public Q_SLOTS:
void onValueChanged(int newVal)
{
lastValue = currentValue;
currentValue = newVal;
if (lastValue == 0) //catch divide-by-zero
emit ratioChanged(0);
else
emit ratioChanged(currentValue/lastValue);
}
Q_SIGNALS:
void ratioChanged(int);
After your signals are connected, the flow should look like this:
spinBoxWidth emits valueChanged(int)
MonitoringObject::onValueChanged(int) is invoked, does its work and emits ratioChanged(int)
spinBoxScale receives the signal in its setValue(int) slot and sets the appropriate value.
The easiest way is probably a lambda that caches the value of valueChanged for the next call:
auto const width = new QSpinBox();
width->setValue(200);
connect(width, &QSpinBox::valueChanged,
[prev_value = width->value()](int const value) mutable {
auto const scale = double(value) / double(prev_value);
// do stuff
prev_value = value;
});
I believe there is no specific signal to the "value before change" because you can always store it from the previous signal "onValueChanged()" you received.
So the basic idea would be:
First time, receive signal onValueChanged(value) and store the value value_old;
Next time you receive the signal, you can compute you scale!value/value_old;
Then you can send a new signal, or directly modify the object with the new value.
You can derived your own version of QSpinBox including this code or implemented in the class it has to receive the signal. It depends on your architecture.
Related
i have the same problem like QMediaPlayer positionChanged(). Sound inteerupts on slider updating
I use QMediayPlayer and everytime when the signal positionChanged() is emitted to update my slider position and i set a new value to the slider, the sound interrupts for a moment.
This is in the Constructor:
soundfile = new QMediaPlayer(this, QMediaPlayer::LowLatency); //soundfile is a pointer of a QMediaPlayer Object
QObject::connect(soundfile, SIGNAL(positionChanged(qint64)), this, SLOT(changedPosition(qint64)));
This is the slot function:
void Soundfile::changedPosition(qint64 p) {
QTime time(0,0,0,0);
time = time.addMSecs(soundfile->position());
if(p != 0) recordSlider->setValue(p); //THIS IS THE LINE, WHERE IT INTERRUPTS
changeRecordTime(QString::number(p));
recordPositionLabel->setText("Aktuelle Zeit: " + time.toString());
}
recordSlider is a QSlider.
If i comment out the line with setValue, all works fine.
Does anyone have an idea?
I believe that the problem is: when the media player emits the SIGNAL the SLOT is called, and when you use setValue inside your function, setValue emits the SIGNAL again, and the process happens again.
In order to solve that problem I disabled the slider tracking and move the position using setSliderPosition.
Example:
slider->setTracking(false);
slider->setSliderPosition(pos);
I used the following itemChange function from my own subclass to trigger a signal as:
QVariant WayPointItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
if (change == QGraphicsItem::ItemSelectedChange){
emit selcState(value.toBool());
}
return QGraphicsItem::itemChange(change, value);
}
The selcState signal is connected to a slot as:
void WaypointLineItem::toggleText(bool value)
{
if (wp->isSelected()) //wp is the waypointitem which emit the signal selcState()
text.show();
else
text.hide();
}
I found that when I select a waypointitem, it did trigger the itemChange function, and the QGraphicsItem::ItemSelectedChange's bool value is also correct.
However, when it comes to the slot, the wp->isSelected() return's a value that is different.
How could this happen, is there any solution to this?
The QGraphicsItem::ItemSelectedChange is called, when the selected state is about to change. This means the new selected state is in the value parameter.
You have to react on the QGraphicsItem::ItemSelectedHasChanged, where the selected state has already changed.
How would I connect a signal and slot of 2 objects when the object names, signals, and slots are all specified in a text file?
Getting the right objects names isn't an issue, since I can easily loop through an array and compare the names to the spot in the file, but there has to be some sort of way where I can return the signal and slot from the file and use it in the connect function, like:
connect(rtnObj1(line),SIGNAL(rtnSignal(line)),rtnObj2(line),SLOT(rtnSlot(line)));
where rtn functions return the object name/signal/slot, and "line" is the current QString line from the file.
The only way I know of is by literally coding in every single combination and comparing QStrings with if statements, but that would be incredibly tedious, as the amount of combinations would be incredibly high.
Note:
Here's a simplified example demonstrating essentially how this issue exists.
Frame 1:
4 QComboBoxes. The first and third hold object names, the second holds signals, the fourth holds slots. Every item is of course a QString within these lists. Hitting a button appends a new line to a file, writing the text selected from each box.
Frame 2: Has already has the required objects. Reading the file, it would match the objects defined in the list against the ones already created, and connect them as the file describes.
It's easy enough to create an object based on the data a file holds, but how would one create/pull a signal and a slot from a file?
Edit:
Unless, is one able to connect like this?
connect(objectA, "", objectB, "");
Because I just found out that my code will compile like that, however whenever I try putting in the slot or signal names I just get an error like:
QObject::connect: Use the SIGNAL macro to bind Tile::clicked
Your problem is easily solvable with one of the following static QObject::connect() method:
QMetaObject::Connection QObject::connect(
const QObject *sender, const QMetaMethod &signal,
const QObject *receiver, const QMetaMethod &method,
Qt::ConnectionType type = Qt::AutoConnection)
First of all, you need pointers to sender and receiver objects. There are several ways how you can store an object pool. I would suggest to keep all objects in QHash:
QHash<QString, QObject *> m_objects; // I use QObject as a generic example
Now, it's possible to find a pointer to any object for connecting in an efficient way.
The next step would be obtaining QMetaMethod objects for sender's signal and receiver's slot from corresponding QMetaObject objects. Use QObject::metaObject() QMetaObject instances.
Here's the complete code of a function which connects two object using only string parameters:
void dynamicConnect(const QString &senderName, const QString &signalName,
const QString &receiverName, const QString &slotName)
{
QObject *emitter = m_objects.value(senderName);
int index = emitter->metaObject()
->indexOfSignal(QMetaObject::normalizedSignature(qPrintable(signalName)));
if (index == -1) {
qWarning("Wrong signal name!");
return;
}
QMetaMethod signal = emitter->metaObject()->method(index);
QObject *receiver = m_objects.value(receiverName);
index = receiver->metaObject()
->indexOfSlot(QMetaObject::normalizedSignature(qPrintable(slotName)));
if (index == -1) {
qWarning("Wrong slot name!");
return;
}
QMetaMethod slot = receiver->metaObject()->method(index);
QObject::connect(emitter, signal, receiver, slot);
}
I'm fighing since last week with problem caused by update of QPlainTextEdit. I'm trying to create separate from QMainWindow Dialog window with QPlainTextEdit inside. The problem begins when I try to use appendHtml signal (also tried with appendText), text that is placed is not visible unless marked by by mouse. Repainting or updating cause in program crash or no visible action.
Simplified code of QDialog with QPlainTextEdit header:
namespace Ui {
class LogWindow;
}
class LogWriter: public QDialog
{
Q_OBJECT
QMutex print_lock;
public:
class Log{
Q_OBJECT
const static int MAX_SIZE = 100;
bool to_terminal;
QString color;
QMutex *print_lock;
QPlainTextEdit *text_place;
QVector< QPair<QString,time_t> > history;
LogWriter * obj;
public:
bool print;
Log(bool _print,QString _color,LogWriter *obj_ = NULL)
{print = _print; color = _color; obj = obj_;}
void setLock(QMutex *print_lock_){print_lock = print_lock_;}
void setTextField(QPlainTextEdit *_text) {text_place = _text;}
Log& operator<<(QString &a);
Log& operator<<(const char* a);
};
static LogWriter* getInstance()
{
static LogWriter instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return &instance;
}
~LogWriter();
Log LOW,MEDIUM,HIGH;
Ui::LogWindow *ui;
signals:
void signalLogAppend(QString);
};
Simplified code of methods definitions:
LogWriter::LogWriter(QWidget * parent): QDialog(parent) {
ui = new Ui::LogWindow;
ui->setupUi(this);
LOW.setLock(&print_lock);
MEDIUM.setLock(&print_lock);
HIGH.setLock(&print_lock);
connect(this,SIGNAL(signalLogAppend(QString)),ui->plainTextEdit,
SLOT(appendHtml(QString)),Qt::DirectConnection);
}
LogWriter::Log& LogWriter::Log::operator<< (QString &s){
history.push_front(qMakePair(s,time(NULL)));
if(history.size() > MAX_SIZE) history.pop_back();
if(print){
//print_lock->lock();
QString text = "<font color=\"";
text += color + "\">";
text += s + "</font>";
//cout << text.toStdString() << endl;
//text_place->appendHtml(text);
//text_place->repaint();
emit (obj)->signalLogAppend(text);
//print_lock->unlock();
}
return *this;
}
I have two separate ui files (first for main window, second for log window).
I have to use log window all across my program (something about 10 threads), and I stucked on this issue. My question is - is it possible to force GUI update without using main thread and if not - what else possibilities I have. If possible I would rather avoid reconstructing all my code - it would take me some time to do it. Right now logging is super easy - I ony need:
LogWindow *log = LogWindow::getInstance();
log->MEDIUM << "something";
As additional info I add QTCreator warning:
QObject::connect: Cannot queue arguments of type 'QTextBlock'
(Make sure 'QTextBlock' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
If I understand your code correctly, you're trying to log from a background thread and are using a direct connection to pass the signal to the GUI thread? That's not going to work, you have to send the signal via the default connection so Qt can figure out that it's a cross-thread signal and pass it across threads accordingly (ie, via the message loop on the foreground thread).
In Qt, any GUI interaction has to happen in the Main/foreground thread otherwise bad things happen as you discovered. You can certainly send a signal from a background thread to trigger a GUI update - I do this all the time - but you need to ensure that you're using the correct connection for it. The direct connection results in a direct function call and is not going to work for you in this case.
In your code, the problem is the call to connect() - you explicitly specify the connection mode for the signal to slot connection when you should just use the default setting. If you set the connection explicitly to Qt::DirectConnection, the underlying code will execute a direct call to the specified slot, which means that you end up calling the slot in the thread context of the signal. You don't want that because the signal is triggered in a background thread.
You can't pass arbitrary types/classes to signals and slots. The list is limited and not all Qt classes are in the list. To add types/classes to the list of those that can be passed to a signal/slot, you must call qRegisterMetaType for that class. I recommend calling it in the constructor of the class you're trying to pass to a signal like this:
MyClass::MyClass() : MyParentClass()
{
static int reg = qRegisterMetaType<MyClass>("MyClass");
}
The static int ensures the registration is only called once and before any instance of MyClass could ever be used.
Boost.Signals allows various strategies of using the return values of slots to form the return value of the signal. E.g. adding them, forming a vector out of them, or returning the last one.
The common wisdom (expressed in the Qt documentation [EDIT: as well as some answers to this question ]) is that no such thing is possible with Qt signals.
However, when I run the moc on the following class definition:
class Object : public QObject {
Q_OBJECT
public:
explicit Object( QObject * parent=0 )
: QObject( parent ) {}
public Q_SLOTS:
void voidSlot();
int intSlot();
Q_SIGNALS:
void voidSignal();
int intSignal();
};
Not only doesn't moc complain about the signal with the non-void return type, it seems to actively implement it in such a way as to allow a return value to pass:
// SIGNAL 1
int Object::intSignal()
{
int _t0;
void *_a[] = { const_cast<void*>(reinterpret_cast<const void*>(&_t0)) };
QMetaObject::activate(this, &staticMetaObject, 1, _a);
return _t0;
}
So: according to the docs, this thing isn't possible. Then what is moc doing here?
Slots can have return values, so can we connect a slot with a return value to a signal with a return value now? May that be possible, after all? If so, is it useful?
EDIT: I'm not asking for workarounds, so please don't provide any.
EDIT: It obviously isn't useful in Qt::QueuedConnection mode (neither is the QPrintPreviewWidget API, though, and still it exists and is useful). But what about Qt::DirectConnection and Qt::BlockingQueuedConnection (or Qt::AutoConnection, when it resolves to Qt::DirectConnection).
OK. So, I did a little more investigating. Seems this is possible. I was able to emit a signal, and receive value from the slot the signal was connected to. But, the problem was that it only returned the last return value from the multiple connected slots:
Here's a simple class definition (main.cpp):
#include <QObject>
#include <QDebug>
class TestClass : public QObject
{
Q_OBJECT
public:
TestClass();
Q_SIGNALS:
QString testSignal();
public Q_SLOTS:
QString testSlot1() {
return QLatin1String("testSlot1");
}
QString testSlot2() {
return QLatin1String("testSlot2");
}
};
TestClass::TestClass() {
connect(this, SIGNAL(testSignal()), this, SLOT(testSlot1()));
connect(this, SIGNAL(testSignal()), this, SLOT(testSlot2()));
QString a = emit testSignal();
qDebug() << a;
}
int main() {
TestClass a;
}
#include "main.moc"
When main runs, it constructs one of the test classes. The constructor wires up two slots to the testSignal signal, and then emits the signal. It captures the return value from the slot(s) invoked.
Unfortunately, you only get the last return value. If you evaluate the code above, you'll get: "testSlot2", the last return value from the connected slots of the signal.
Here's why. Qt Signals are a syntax sugared interface to the signaling pattern. Slots are the recipients of a signal. In a direct connected signal-slot relationship, you could think of it similar to (pseudo-code):
foreach slot in connectedSlotsForSignal(signal):
value = invoke slot with parameters from signal
return value
Obviously the moc does a little more to help in this process (rudimentary type checking, etc), but this helps paint the picture.
No, they can't.
Boost::signals are quite different from those in Qt. The former provide an advanced callback mechanism, whereas the latter implement the signaling idiom. In the context of multithreading, Qt's (cross-threaded) signals depend on message queues, so they are called asynchronously at some (unknown to the emitter's thread) point in time.
Qt's qt_metacall function returns an integer status code. Because of this, I believe this makes an actual return value impossible (unless you fudge around with the meta object system and moc files after precompilation).
You do, however, have normal function parameters at your disposal. It should be possible to modify your code in such a way to use "out" parameters that act as your "return".
void ClassObj::method(return_type * return_)
{
...
if(return_) *return_ = ...;
}
// somewhere else in the code...
return_type ret;
emit this->method(&ret);
You may get a return value from Qt signal with the following code:
My example shows how to use a Qt signal to read the text of a QLineEdit.
I'm just extending what #jordan has proposed:
It should be possible to modify your code in such a way to use "out" parameters that act as your "return".
#include <QtCore>
#include <QtGui>
class SignalsRet : public QObject
{
Q_OBJECT
public:
SignalsRet()
{
connect(this, SIGNAL(Get(QString*)), SLOT(GetCurrentThread(QString*)), Qt::DirectConnection);
connect(this, SIGNAL(GetFromAnotherThread(QString*)), SLOT(ReadObject(QString*)), Qt::BlockingQueuedConnection);
edit.setText("This is a test");
}
public slots:
QString call()
{
QString text;
emit Get(&text);
return text;
}
signals:
void Get(QString *value);
void GetFromAnotherThread(QString *value);
private slots:
void GetCurrentThread(QString *value)
{
QThread *thread = QThread::currentThread();
QThread *mainthread = this->thread();
if(thread == mainthread) //Signal called from the same thread that SignalsRet class was living
ReadObject(value);
else //Signal called from another thread
emit GetFromAnotherThread(value);
}
void ReadObject(QString *value)
{
QString text = edit.text();
*value = text;
}
private:
QLineEdit edit;
};
To use this, just request call();.
You can try to workaround this with following:
All your connected slots must save their results in some place (container) accessible from signaling object
The last connected slot should somehow (select max or last value) process collected values and expose the only one
The emitting object can try to access this result
Just as an idea.