1

Can I use global slots in Qt?

I am trying to use this code, but it seems like it doesn't work.

slots void f(int i);

void connect(QSpinBox *spinBox)
{
    QObject::connect(spinBox, SIGNAL(valueChanged(int)), NULL, SLOT(f(int)));
}

void f(int i)
{
    // ...
}
Ufx
  • 2,595
  • 12
  • 44
  • 83
  • 3
    there are no global slots, one option is to create a singleton and connect to a slot of that class. or probably if you are using Qt 5.6 or higher you could call that function using a lambda function – eyllanesc Jan 21 '18 at 19:27
  • Why do you want to use a *"global slot"*? – eyllanesc Jan 21 '18 at 19:28
  • to set property of some widget by the argument. – Ufx Jan 21 '18 at 19:29
  • Explain yourself better, what widget? – eyllanesc Jan 21 '18 at 19:30
  • also provides a more appropriate [mcve] to your original problem, your current example does not show how you want to set some property to some widget. – eyllanesc Jan 21 '18 at 19:31
  • I would say it just for testing before I insert it into class. It's not problem for me if it's impossible. – Ufx Jan 21 '18 at 19:32
  • it is impossible, that is the answer since Qt uses the object to invoke the slot. – eyllanesc Jan 21 '18 at 19:33
  • 1
    I don't get it why it should be impossible. Connect a global function (or lambda) to a signal is very easy since Qt5 (using the new introduced `QObject::connect()` methods). E.g. I used a function in my answer to [SO: Directly executing a batch through clicked function in qt](https://stackoverflow.com/a/47902054/7478597), and I used a lambda in my answer to [SO: Unable to take a 20 byte hex input from Qlinedit and store into Qstring](https://stackoverflow.com/a/47532007/7478597) but there are plenty of other samples. (OK, I don't care whether Qt makes a helper object "under the hood"). – Scheff's Cat Jan 22 '18 at 08:48

1 Answers1

2

It was already pointed out in comments: There are no global slots.


On the other hand, since Qt5 you can connect any method to a signal even if it's not remarked as slot. You can even connect global functions and lambdas to a signal. So, if the requirement for "SLOT" is dropped (and is called, may be, "signal handler" instead) it may provide what you (probably) are asking for.

Sample code testQSpinBox.cc:

// Qt header:
#include <QtWidgets>

// function with matching signature
void f(int value)
{
  qDebug() << value;
}

// function with non-matching signature
void g(const char *text, int value)
{
  qDebug() << text << value;
}

int main(int argc, char **argv)
{
  qDebug() << "Version:" << QT_VERSION_STR;
  // main application
  QApplication app(argc, argv);
  qDebug() << QApplication::style()->objectName();
  // setup GUI
  QWidget qWin;
  QVBoxLayout qBox;
  QSpinBox qSpinBox1;
  qBox.addWidget(&qSpinBox1);
  QSpinBox qSpinBox2;
  qBox.addWidget(&qSpinBox2);
  QSpinBox qSpinBox3;
  qBox.addWidget(&qSpinBox3);
  qWin.setLayout(&qBox);
  qWin.show();
  // install signal handlers
  // connect f() to qSpinBox1::valueChanged
  QObject::connect(&qSpinBox1,
    (void (QSpinBox::*)(int))&QSpinBox::valueChanged,
    &f);
  // connect lambda to qSpinBox2::valueChanged
  QObject::connect(&qSpinBox2,
    (void (QSpinBox::*)(int))&QSpinBox::valueChanged,
    [](int value) { qDebug() << "qSpinBox2:" << value; });
  // use lambda as adapter for function g()
  QObject::connect(&qSpinBox3,
    (void (QSpinBox::*)(int))&QSpinBox::valueChanged,
    [](int value) { g("qSpinBox3:", value); });
  // run application
  return app.exec();
}

To compile it in cygwin, I prepared a QMake file testQSpinBox.pro:

SOURCES = testQSpinBox.cc

QT += widgets

Compiling and testing with g++:

$ qmake-qt5 testQSpinBox.pro

$ make
g++ -c -fno-keep-inline-dllexport -D_GNU_SOURCE -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtCore -I. -I/usr/lib/qt5/mkspecs/cygwin-g++ -o testQSpinBox.o testQSpinBox.cc
g++  -o testQSpinBox.exe testQSpinBox.o   -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread 

$ ./testQSpinBox 
Version: 5.9.2
"fusion"

Snapshot of testQSpinBox

(I clicked the Up-button of each spin box to force valueChanged signals.)

1
qSpinBox2: 1
qSpinBox3: 1

Note:

There is a specialty concerning QSpinBox::valueChanged() – there are actually two of them:

void QSpinBox::valueChanged(int i);

void QSpinBox::valueChanged(const QString &text);

Hence, if we provide the signal in QObject::connect(), the method identifier is ambiguous. We have to provide a "method pointer cast" to solve the ambiguity:

#if 0 // Ambiguity:
QObject::connect(&qSpinBox1, &QSpinBox::valueChanged, &f);
#else // Solve ambinguity:
QObject::connect(&qSpinBox1,
  (void(QSpinBox::*)(int))&QSpinBox::valueChanged,
  &f);
#endif // 0

Signals with multiple signatures are the few exceptions in Qt. Usually, that ugly method pointer cast thing is not necessary to connect a signal in Qt 5 style.


Btw. in my answer to SO: QPushButton doesn't update when setDown is called, I wrote code which supports Qt 4 and Qt 5. So, this might be interesting concerning old-fashioned Qt SLOTs in opposition to modern Qt 5 signal handlers.

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56