Qt: How to know if slot was called by signal-slot mechanism by calling the signal like a function - qt

Inside a slot I check the QObject::sender() but now I want to call this slot directly like a function.
Can I know how this slot was called inside of the slot? Either by signal-slot mechanism or simply by calling the slot like a function?

You can check the sender() in both cases. In case of the slot being called via a signal/slot mechanism the sender will return a pointer while when called as a method it will return null pointer.
Simple example:
class Test : public QObject
{
Q_OBJECT
signals:
void signal();
public slots:
void slot() { qDebug() << sender(); }
};
And the use:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Test test;
Test test2;
QObject::connect(&test, &Test::signal, &test2, &Test::slot);
test.signal(); //slot is caled by signal
test2.slot(); //slot is called as method directly
return a.exec();
}
And the output:
Test(0xa8e8aff5b0)
QObject(0x0)

Add a default parameter to your slot:
public slots:
void slot(bool calledBySignal = true);
Set the parameter to false when calling the slot directly:
void MyClass::method()
{
[...]
slot(false);
[...]
}
Leave the connect() calls as they are; don't add a bool parameter to the signals, nor change SLOT(slot()) to SLOT(slot(bool)).
Disadvantage: It's easy to forget setting the parameter.
If your slot doesn't need to be public, because connect()ing to it is handled from inside the class only, you should make it private and add a wrapper method that's to be called instead, but still you'll need some discipline when calling it from inside the class. Johannes' suggestion would solve these issues.

Another idea to distinguish between direct function call vs. Signal/Slot-invocation:
Use the method int QObject::senderSignalIndex() const and check for -1
if(QObject::senderSignalIndex() == -1){
//called directly as a function
} else {
// invoked via Signal/SLot mechanism
}
Returns the meta-method index of the signal that called the currently executing slot, which is a member of the class returned by sender(). If called outside of a slot activated by a signal, -1 is returned.
see Qt 4.8 documentation
This looks like a clean way to distinguish and there is no chance of getting into trouble with a null-pointer in contrast to using QObject * QObject::sender() const.
Regards,
Felix

Related

How to pass string as argument for QTextEdit signal

Is it possible to pass string as an argument for QTextEdit?
This is how I am trying to implement it currently.
QTextEdit *textEdit = new QTextEdit;
QObject::connect(textEdit, &QTextEdit::textChanged, this, &ParamChangeText);
void ParamChangeText(QString qstr)
{
qDebug() << qstr;
}
But I get the error that signal and slot are not compatible.
You have three quick ways to achieve this:
You know that such slot is invoked only when that specific QTextEdit(textEdit) changes, and the slot have access to that variable. In this case just use textEdit->toPlainText() (or HTML or Markdown variants) to retrieve the string.
The slot is in a different object with no access to textEdit or there may be several QTextEdit connected to the same slot. In Qt, when a slot is invoked from a signal, QObject::sender() returns the object that emitted the signal. You can simply cast it to get the QTextEdit:
void ParamChangeText()
{
auto textEdit = qobject_cast<QTextEdit*>(sender());
if (textEdit) { // will be null if sender() doesn't return a QTextEdit
qDebug() << textEdit->toPlainText();
}
}
Finally, you may use Qt 5 new signal / slot syntax and use a lambda function to do the work, at expenses of an extra function call. Be careful that sender() doesn't return the emitter object on lambdas, but you can solve it by capturing the QTextEdit:
// Omit textEdit from capture list if it is a member of class
QObject::connect(textEdit, &QTextEdit::textChanged, [this, textEdit]() {
ParamChangeText(textEdit->toPlainText());
});
void ParamChangeText(QString qstr)
{
qDebug() << qstr;
}

If I call a setValue() or similar function of a Qt widget, when is its valueChanged() slot guaranteed to be executed?

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.

Emitting signal from callback

I am using RtMidi library to handle midi message in my Qt application and I am facing problem with slot trigger:
My PhMidiInput object is emiting signal from the RtMidi callback upon specific midi message but the slots are not always triggered.
Here is a part of the PhMidiInput class:
class PhMidiInput : QObject
{
Q_OBJECT
public:
void PhMidiInput() {}
signals:
void quarterFrame(unsigned char data);
private:
static void callback(double, std::vector< unsigned char > *message, void *userData ) {
PhMidiInput *midiInput = (PhMidiInput*)userData;
if(midiInput)
midiInput->onMessage(message);
}
void onMessage(std::vector<unsigned char> *message) {
...
emit quarterFrame(data);
...
}
}
Connecting to a lambda functor works:
PhMidiInput midiIn;
int quarterFrameCount;
connect(&midiIn, &PhMidiInput::quarterFrame, [&](unsigned char data) {
quarterFrameCount++;
});
Connecting to my application window works to:
// MyWindow inherits from QMainWindow
connect(_midiIn, &PhMidiInput::quarterFrame, this, &MyWindow::onQuarterFrame);
When trying to connect to a custom class (MidiTest) inheriting from QObject it does'nt trigger:
connect(_midiIn, &PhMidiInput::quarterFrame, this, &MidiTest::onQuarterFrame);
I was wondering if there was something around QObject::moveToThread() but since I don't create the thread myself (the signal is sent from a callback) I don't know if I need to use it or not.
It is as simple as calling emit obj->quarterFrame(data); from the callback. If the connection type is default then this will be perfectly thread safe.
Though you should create a QByteArray from data to pass around as data will likely not be valid by the time the slots get called.
void callback(..., void* user){
//user is the standard void* in most callbacks passed as reinterpret_cast<void*>(this)
unsigned char* data = ...;
QByteArray bytes(data);
emit reinterpret_cast<PhMidiInput>(user)->quarterFrame(bytes);//calling the signal which will behave as you'd expect
}
In the last connect() call you pass this and MidiTest::onQuarterFrame as the receiver object and method. I bet this is not an instance of MidiTest, is it?
The problem here is that you're passing SLOT method from MidiTest, while the receiver object is this, which is not instance of MidiTest. Change receiver from this to some instance of MidiTest.
I'm surprised this code doesn't crash your application when running.

Creating a dynamic slot in Qt

I am trying to create slots dynamically and connect them. I am able to dynamically create pushButtons and connect them with existing slots. But what if I have a class with some member functions and I want to use these functions as slots.
From a general perspective, I want to create a template for generating slots given a function pointer. This allows us to create slots without changing the existing code and don't have to recompile using MOC.
If this doesn't make sense let me know. Thank you.
-CV
It does make a lot of sense. I assume QSignalMapper is not what you want. If your functions don't have arguments, maybe something like this is enough:
class SlotForwarder : public QObject
{
Q_OBJECT
public:
typedef void (*Function)(); // or whatever. I never get this syntax right...
SlotForwarder(Function function, QObject* parent = 0)
: QObject(parent)
, m_fptr(function)
{}
public slots:
void forward()
{
m_fptr();
}
private:
Function m_fptr;
};
Create one for each function you want to encapsulate and connect to the forward as usual.
Now, if they do have arguments, maybe this Qt Quarterly article might be of assistance.
Dynamic Signals and Slots by Eskil A. Blomfeldt
The technique involves reimplementing the qt_metacall method yourself. The method has this signature:
int QObject::qt_metacall(QMetaObject::Call call, int id, void **arguments)
Call is the kind of metacall: slot, signal, property read or write, etc. Every slot has an id. The parameters are packed (by value or as pointers) inside the arguments array. Reading the code that the moc generates is a good way to understand how it all works.
Data about raw function signatures is available only during compile time, but slots are resolved at runtime. Because of that mismatch, you will need to wrap the functions into a template adapter type that presents a constant interface to your implementation of qt_metacall and converts the arguments array into something the function can understand (cf. Python unpack operator). Boost.Signals does that kind of template hackery.
A continuation of andref's code so as to use any member function as a slot
class SlotForwarder : public QObject
{
Q_OBJECT
public:
typedef void (*Function)();
SlotForwarder(Function function, QObject* parent = 0)
: QObject(parent)
, m_fptr(function)
{}
public slots:
void forward()
{
m_fptr();
}
private:
Function m_fptr;
};
int main(){
QApplication a(argc, argv);
MyClass myClassObject; //contains a function called MemberFunc
//create a slotforwarder object so
SlotForwarder *memberFuncSlot = new SlotForwarder (std::tr1::bind(&MyClass::MemberFunc, &myClassObject));
QObject::connect(ui.button,SIGNAL(clicked()),memberFuncSlot,SLOT(forward());
}

How to delete a QProcess instance correctly?

I have a class looking like this:
class FakeRunner : public QObject
{
Q_OBJECT
private:
QProcess* proc;
public:
FakeRunner();
int run()
{
if (proc)
return -1;
proc = new QProcess();
QStringList args;
QString programName = "fake.exe";
connect(comp, SIGNAL(started()), this, SLOT(procStarted()));
connect(comp, SIGNAL(error(QProcess::ProcessError)), this,
SLOT(procError(QProcess::ProcessError)));
connect(comp, SIGNAL(finished(int, QProcess::ExitStatus)), this,
SLOT(procFinished(int, QProcess::ExitStatus)));
proc->start(programName, args);
return 0;
};
private slots:
void procStarted() {};
void procFinished(int, QProcess::ExitStatus) {};
void procError(QProcess::ProcessError);
}
Since "fake.exe" does not exist on my system, proc emits the error() signal. If I handle it like following, my program crashes:
void FakeRunner::procError(QProcess::ProcessError rc)
{
delete proc;
proc = 0;
}
It works well, though, if I don't delete the pointer. So, the question is how (and when) should I delete the pointer to QProcess? I believe I have to delete it to avoid a memory leak. FakeRunner::run() can be invoked many times, so the leak, if there is one, will grow.
Thanks!
You can't delete QObject instance inside slot which is connected to a signal in this instance using normal delete operator. This is due to the fact that if signal and slot connected using direct connection then the slot actually called from the signal implementation made by moc. This is like attempt to delete this; from inside the member of a class. There is a solution QObject::deleteLater(). Object will be deleted by Qt event loop inside events processing function. So you need to call proc->deleteLater() in your case.
And you don't need to disconnect signal from slot since Qt do it automatically when QObject is deleted.

Resources