From 1ddf17e0b53300acd158e747fc6c8c444868066a Mon Sep 17 00:00:00 2001 From: Robert Middleton Date: Mon, 14 Oct 2019 21:27:10 -0400 Subject: [PATCH 1/4] Added example of using libsigc++ with Qt --- .gitignore | 1 + examples/qt/exampleclass.cpp | 34 +++++++++++++++++++++++++++++++ examples/qt/exampleclass.h | 36 +++++++++++++++++++++++++++++++++ examples/qt/main.cpp | 13 ++++++++++++ examples/qt/qt-sigcpp.pro | 39 ++++++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+) create mode 100644 examples/qt/exampleclass.cpp create mode 100644 examples/qt/exampleclass.h create mode 100644 examples/qt/main.cpp create mode 100644 examples/qt/qt-sigcpp.pro diff --git a/.gitignore b/.gitignore index 5237bf44..7ee0f04c 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ Makefile.in /sigc++config.h /sigc++-*.pc /stamp-h? +*.pro.user diff --git a/examples/qt/exampleclass.cpp b/examples/qt/exampleclass.cpp new file mode 100644 index 00000000..7165b3d3 --- /dev/null +++ b/examples/qt/exampleclass.cpp @@ -0,0 +1,34 @@ +#include + +#include "exampleclass.h" + +ExampleClass::ExampleClass(QObject *parent) : + QObject(parent), + m_times(0) +{ + /* Create a slot from our example_slot method. */ + m_slot = sigc::mem_fun( *this, &ExampleClass::example_slot ); + + connect( &m_timer, &QTimer::timeout, + this, &ExampleClass::timer_slot ); + m_timer.start( 200 ); +} + +void ExampleClass::timer_slot(){ + qDebug() << "Timer slot called"; + + /* Call the slot that is used by libsigc++ */ + m_slot(); + + /* Emit a Qt signal */ + Q_EMIT example_signal(); + + /* Only run for a few times before exiting */ + if( m_times++ > 10 ){ + QCoreApplication::exit( 0 ); + } +} + +void ExampleClass::example_slot(){ + qDebug() << "Example slot called"; +} diff --git a/examples/qt/exampleclass.h b/examples/qt/exampleclass.h new file mode 100644 index 00000000..b9dbc35f --- /dev/null +++ b/examples/qt/exampleclass.h @@ -0,0 +1,36 @@ +#ifndef EXAMPLECLASS_H +#define EXAMPLECLASS_H + +#include +#include +#include + +#include + +class ExampleClass : public QObject +{ + Q_OBJECT +public: + explicit ExampleClass(QObject *parent = nullptr); + +/* Instead of using the keyword 'signals', use the 'Q_SIGNALS' macro */ +Q_SIGNALS: + void example_signal(); + +/* Instead of using the keyword 'slots', use the 'Q_SLOTS' macro */ +public Q_SLOTS: + void timer_slot(); + + /** + * This slot is called using libsigc++, however since it is defined under Q_SLOTS + * it could also be used with the Qt signals/slots + */ + void example_slot(); + +private: + sigc::slot m_slot; + QTimer m_timer; + int m_times; +}; + +#endif // EXAMPLECLASS_H diff --git a/examples/qt/main.cpp b/examples/qt/main.cpp new file mode 100644 index 00000000..c7dd7825 --- /dev/null +++ b/examples/qt/main.cpp @@ -0,0 +1,13 @@ +#include +#include + +#include "exampleclass.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + ExampleClass ex; + + return a.exec(); +} diff --git a/examples/qt/qt-sigcpp.pro b/examples/qt/qt-sigcpp.pro new file mode 100644 index 00000000..154ab8f0 --- /dev/null +++ b/examples/qt/qt-sigcpp.pro @@ -0,0 +1,39 @@ +QT -= gui + +CONFIG += console +CONFIG -= app_bundle + +# Qt 5.12 introduces the 'c++17' config option; this sets it manually +QMAKE_CXXFLAGS += -std=c++17 + +# Since Qt #defines emit, signal, and slot, we need to disable those keywords +CONFIG += no_keywords + +# We must link with sigc++-3.0 +unix{ + CONFIG += link_pkgconfig + PKGCONFIG += sigc++-3.0 +} + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp \ + exampleclass.cpp + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +HEADERS += \ + exampleclass.h From 2d3ebb71e668ff66c86ce5bbd05bcdecf7dc04e8 Mon Sep 17 00:00:00 2001 From: Robert Middleton Date: Sun, 3 Nov 2019 11:43:42 -0500 Subject: [PATCH 2/4] updated example based on feedback --- examples/qt/README.md | 45 ++++++++++++++++++++++++++++++++++++ examples/qt/exampleclass.cpp | 26 ++++++++++----------- examples/qt/exampleclass.h | 4 ++-- 3 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 examples/qt/README.md diff --git a/examples/qt/README.md b/examples/qt/README.md new file mode 100644 index 00000000..3d010d9e --- /dev/null +++ b/examples/qt/README.md @@ -0,0 +1,45 @@ +# libsigc++ with Qt + +It is possible to use libsigc++ with Qt. However, because of the signals/slots +mechanism of Qt, there is some setup that must be done in order for this to +happen correctly. + +Steps to use libsigc++ with Qt: +1. In your .pro file, add `CONFIG += no_keywords`. This configures Qt to not +define the keywords `emit`, `signals`, and `slot`. +2. In your header files, change the `signals:` section of your class to instead +be `Q_SIGNALS` +3. In your header files, change the `public slots:` section of your class to +instead be `public Q_SLOTS:` +4. In any class that you emit a signal, change `emit` to be `Q_EMIT`. + +Here's an example of a class before and after this conversion(note: irrelevant +code has been removed): + +``` +class ExampleClass : public QObject { +signals: + void exampleSignal(); +public slots: + void example_slot(){ + emit exampleSignal(); + } +}; +``` + +After conversion: +``` +class ExampleClass : public QObject { +Q_SIGNALS: + void exampleSignal(); +public Q_SLOTS: + void example_slot(){ + Q_EMIT exampleSignal(); + } +}; +``` + +## Qt Slots Notes +Since libsigc++ simply requires a slot to be a function, you can call Qt +slots easily using libsigc++. Similarly, a function that is a libsigc++ slot +can also be used as a Qt slot. diff --git a/examples/qt/exampleclass.cpp b/examples/qt/exampleclass.cpp index 7165b3d3..2d178b49 100644 --- a/examples/qt/exampleclass.cpp +++ b/examples/qt/exampleclass.cpp @@ -3,30 +3,30 @@ #include "exampleclass.h" ExampleClass::ExampleClass(QObject *parent) : - QObject(parent), - m_times(0) + QObject(parent) { /* Create a slot from our example_slot method. */ - m_slot = sigc::mem_fun( *this, &ExampleClass::example_slot ); + m_sigc_slot = sigc::mem_fun( *this, &ExampleClass::example_slot ); + /* Connect our sigc++ signal to our sigc++ slot */ + m_sigc_signal.connect( m_sigc_slot ); + + /* Emit a sigc++ signal */ + m_sigc_signal.emit(); + + /* Connect the Qt signal to our Qt slot */ connect( &m_timer, &QTimer::timeout, this, &ExampleClass::timer_slot ); m_timer.start( 200 ); + + /* Emit a Qt signal */ + Q_EMIT example_signal(); } void ExampleClass::timer_slot(){ qDebug() << "Timer slot called"; - /* Call the slot that is used by libsigc++ */ - m_slot(); - - /* Emit a Qt signal */ - Q_EMIT example_signal(); - - /* Only run for a few times before exiting */ - if( m_times++ > 10 ){ - QCoreApplication::exit( 0 ); - } + QCoreApplication::exit( 0 ); } void ExampleClass::example_slot(){ diff --git a/examples/qt/exampleclass.h b/examples/qt/exampleclass.h index b9dbc35f..37f456fb 100644 --- a/examples/qt/exampleclass.h +++ b/examples/qt/exampleclass.h @@ -28,9 +28,9 @@ public Q_SLOTS: void example_slot(); private: - sigc::slot m_slot; + sigc::slot m_sigc_slot; + sigc::signal m_sigc_signal; QTimer m_timer; - int m_times; }; #endif // EXAMPLECLASS_H From 94a97679b5d99bbfc35d3f23c7e983bd3b7eb0f6 Mon Sep 17 00:00:00 2001 From: Robert Middleton Date: Sun, 5 Jan 2020 18:04:57 -0500 Subject: [PATCH 3/4] Renamed example --- examples/{qt => qt_with_qmake}/README.md | 0 examples/{qt => qt_with_qmake}/exampleclass.cpp | 0 examples/{qt => qt_with_qmake}/exampleclass.h | 0 examples/{qt => qt_with_qmake}/main.cpp | 0 examples/{qt => qt_with_qmake}/qt-sigcpp.pro | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename examples/{qt => qt_with_qmake}/README.md (100%) rename examples/{qt => qt_with_qmake}/exampleclass.cpp (100%) rename examples/{qt => qt_with_qmake}/exampleclass.h (100%) rename examples/{qt => qt_with_qmake}/main.cpp (100%) rename examples/{qt => qt_with_qmake}/qt-sigcpp.pro (100%) diff --git a/examples/qt/README.md b/examples/qt_with_qmake/README.md similarity index 100% rename from examples/qt/README.md rename to examples/qt_with_qmake/README.md diff --git a/examples/qt/exampleclass.cpp b/examples/qt_with_qmake/exampleclass.cpp similarity index 100% rename from examples/qt/exampleclass.cpp rename to examples/qt_with_qmake/exampleclass.cpp diff --git a/examples/qt/exampleclass.h b/examples/qt_with_qmake/exampleclass.h similarity index 100% rename from examples/qt/exampleclass.h rename to examples/qt_with_qmake/exampleclass.h diff --git a/examples/qt/main.cpp b/examples/qt_with_qmake/main.cpp similarity index 100% rename from examples/qt/main.cpp rename to examples/qt_with_qmake/main.cpp diff --git a/examples/qt/qt-sigcpp.pro b/examples/qt_with_qmake/qt-sigcpp.pro similarity index 100% rename from examples/qt/qt-sigcpp.pro rename to examples/qt_with_qmake/qt-sigcpp.pro From 62add21ebcc48e7172d81d9043a25c4222fdbdc5 Mon Sep 17 00:00:00 2001 From: Robert Middleton Date: Sun, 5 Jan 2020 18:14:44 -0500 Subject: [PATCH 4/4] Updated README.md based off of feedback --- examples/qt_with_qmake/README.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/examples/qt_with_qmake/README.md b/examples/qt_with_qmake/README.md index 3d010d9e..e19ccf61 100644 --- a/examples/qt_with_qmake/README.md +++ b/examples/qt_with_qmake/README.md @@ -4,15 +4,22 @@ It is possible to use libsigc++ with Qt. However, because of the signals/slots mechanism of Qt, there is some setup that must be done in order for this to happen correctly. +The official Qt documentation may be found here: https://doc.qt.io/qt-5/signalsandslots.html#using-qt-with-3rd-party-signals-and-slots + Steps to use libsigc++ with Qt: 1. In your .pro file, add `CONFIG += no_keywords`. This configures Qt to not -define the keywords `emit`, `signals`, and `slot`. +define the macros `emit`, `signals`, and `slot`. These are keywords for moc, +which preprocesses the source files in order to use Qt signals/slots. 2. In your header files, change the `signals:` section of your class to instead be `Q_SIGNALS` 3. In your header files, change the `public slots:` section of your class to instead be `public Q_SLOTS:` 4. In any class that you emit a signal, change `emit` to be `Q_EMIT`. +In general, using the Q\_ macros is a good idea if your code is a library +intended to be used by people other than yourself, as they may be using +code(e.g. libsigc++/boost signals) that will conflict with Qt(moc) keywords. + Here's an example of a class before and after this conversion(note: irrelevant code has been removed): @@ -43,3 +50,18 @@ public Q_SLOTS: Since libsigc++ simply requires a slot to be a function, you can call Qt slots easily using libsigc++. Similarly, a function that is a libsigc++ slot can also be used as a Qt slot. + +# Other Build Systems +If you are not using qmake to build your Qt project, you must tell your +buildsystem to define `QT_NO_KEYWORDS`. If you're using CMake, this may +be done like the following: + +``` +add_definitions(-DQT_NO_KEYWORDS) +``` + +or in a more modern CMake way: + +``` +target_compile_definitions(some_target PRIVATE QT_NO_KEYWORDS) +```