I try to create a some standart signal definitionsfor some classes with macros like:
#define CREATE_SIGNALS signals: void error_signal(QString error);
Functions are created, but moc did not create methods for these functions. It seems that moc is running before macros processed. How can i make this work?
Moc preprocesses the sources it works on, so your macro will work once you actually make the macro define some signals, and not regular methods.
The following is a complete example:
#include <QtCore>
#include <array>
#include <algorithm>
#define CREATE_SIGNALS\
Q_SIGNAL void signal1(const QString & = QString());\
Q_SIGNAL void signal2(int = 0);
struct Foo : QObject {
CREATE_SIGNALS
Q_OBJECT
};
struct Bar : QObject {
CREATE_SIGNALS
Q_OBJECT
};
int main()
{
std::array<int, 4> s;
Foo foo;
Bar bar;
s.fill(0);
QObject::connect(&foo, &Foo::signal1, [&]{++s[0];});
QObject::connect(&foo, &Foo::signal2, [&]{++s[1];});
QObject::connect(&bar, &Bar::signal1, [&]{++s[2];});
QObject::connect(&bar, &Bar::signal2, [&]{++s[3];});
emit foo.signal1();
emit foo.signal2();
emit bar.signal1();
emit bar.signal2();
Q_ASSERT(std::all_of(std::begin(s), std::end(s), [](int val) { return val == 1; }));
}
#include "main.moc"
Related
I am trying to connect a gui to my logic thread using boosts signals and slots, the logic class has a neat method to connect functions to the signal.
Here is a simplified replica of the locig class:
#include <boost/signals2.hpp>
#include <boost/function.hpp>
typedef boost::signals2::signal<void (const double&)> some_signal;
typedef some_signal::slot_type some_slot;
class LogicClass {
some_signal sig;
public:
LogicClass();
~LogicClass();
void register_callback(boost::function<void (const double&)>) {
sig.connect(boost::bind(&LogicClass::doStuff(), this, _1));
}
void doStuff(); // Does a bunch of stuff in a separate thread and fires LogicClass::sig every now and then
}
Here is a simplified replica of the gui class
#include <boost/signals2.hpp>
#include <QWidget.h>
class GuiClass : public QWidget {
Q_OBJECT //etc. etc. w.r.t. Qt stuff
public:
GuiClass ();
~GuiClass ();
void draw_stuff(const double&); // Want this to listen to LogicClass::sig;
}
At some point in my code the gui class is already instantiated but the logic class isn't. So i want to instantiate the LogicClass and subscribe the GuiClass::draw_stuff(const double&) to the LogicClass::sig signal. Something like
#include <logicclass.h>
#include <guiclass.h>
GuiClass *gui; //Was initialized elsewhere, but available here;
void some_function() {
LogicClass *logic = new LogicClass();
logic->register_callback(gui->drawStuff);
logic->do_stuff(); //Start drawing on the gui
delete logic;
}
This unfortunately doesn't work. Goes without saying it would like it to work very much!
I know Qt also implements signals & slots, but i would like to use boost for portability with other UI libraries.
Looks like you meant to bind draw_stuff instead of hardcoded doStuff inside of that register function:
logic->register_callback(boost::bind(&GuiClass::draw_stuff, gui, _1));
And then
void register_callback(boost::function<void(double)> handler) {
sig.connect(handler);
}
Simple Demo
Live On Coliru
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/signals2.hpp>
#include <boost/thread.hpp>
#include <iostream>
typedef boost::signals2::signal<void(double)> some_signal;
typedef some_signal::slot_type some_slot;
class LogicClass {
some_signal sig;
public:
LogicClass() = default;
~LogicClass() = default;
void register_callback(boost::function<void(double)> handler) {
sig.connect(handler);
}
void doStuff() {
std::cout << __PRETTY_FUNCTION__ << "\n";
// Does a bunch of stuff in a separate thread and fires LogicClass::sig every now and then
boost::thread([this] {
for (double d : {1,2,3}) {
boost::this_thread::sleep_for(boost::chrono::milliseconds(500));
sig(d);
}
}).join();
}
};
#define Q_OBJECT
struct QWidget {};
class GuiClass : public QWidget {
Q_OBJECT // etc. etc. w.r.t. Qt stuff
public:
GuiClass() = default;
~GuiClass() = default;
void draw_stuff(double v) { std::cout << __PRETTY_FUNCTION__ << "(" << v << ")\n"; }
};
//#include <logicclass.h>
//#include <guiclass.h>
GuiClass *gui; // Was initialized elsewhere, but available here;
void some_function() {
LogicClass *logic = new LogicClass();
logic->register_callback(boost::bind(&GuiClass::draw_stuff, gui, _1));
logic->doStuff(); // Start drawing on the gui
delete logic;
}
int main() {
GuiClass gui_instance;
gui = &gui_instance;
some_function();
}
Prints
void LogicClass::doStuff()
void GuiClass::draw_stuff(double)(1)
void GuiClass::draw_stuff(double)(2)
void GuiClass::draw_stuff(double)(3)
I have an object that I define in C++ and am trying expose a member string to QML. The class is defined as:
#ifndef MYTYPE_H
#define MYTYPE_H
#include <QString>
#include <QObject>
class MyType : public QObject
{
Q_OBJECT
Q_PROPERTY(QString foo READ foo WRITE setFoo NOTIFY fooChanged)
public:
MyType(QObject *parent = nullptr) :
QObject(parent),
mFoo("0")
{
}
QString foo() const
{
return mFoo;
}
void setFoo(QString foo)
{
if (foo == mFoo)
return;
mFoo = foo;
emit fooChanged(mFoo);
}
signals:
void fooChanged(QString foo);
private:
QString mFoo;
};
#endif // MYTYPE_H
So I am trying to expose the mFoo object to QML. Now, I am setting it with the application context as:
QtQuickControlsApplication app(argc, argv);
QQmlApplicationEngine engine(QUrl("qrc:/main.qml"));
qmlRegisterType<MyType>("MyType", 1, 0, "MyType");
MyType myType;
QObject *topLevel = engine.rootObjects().value(0);
engine.rootContext()->setContextProperty("foo", &myType);
Now in my qml, how can I listen to the change of the string that I am exposing. So I would like a listener method that gets called every time the mFoo member is changing.
You can use the Connections-object for that.
Connections {
target: yourContextProperty
onFooChanged: console.log('I do something cool when foo changes!')
}
See also here some more examples, how to use context properties. (It also has an example for Connections)
I'm trying to pass my Class trough a signal with this:
connect(this, SIGNAL(SIG_connectSerial(SerialSetting::Settings)), serial, SLOT(openConnection(SerialSetting::Settings)),Qt::QueuedConnection);
The class I want to pass is that class:
#ifndef SERIALSETTING_H
#define SERIALSETTING_H
#include <QWidget>
#include <QtSerialPort/QSerialPort>
namespace Ui {
class SerialSetting;
}
class QIntValidator;
class SerialSetting : public QWidget
{
Q_OBJECT
public:
struct Settings {
QString portName;
qint32 baudRate;
};
Settings settings();
public:
explicit SerialSetting(QWidget *parent = 0);
~SerialSetting();
private slots:
void apply();
void on_btnApply_clicked();
private:
void fillPortsParameters();
void fillPortsInfo();
void updateSettings();
private:
Ui::SerialSetting *ui;
Settings currentSettings;
QIntValidator *intValidator;
};
#endif // SERIALSETTING_H
#include "serialsetting.h"
#include "ui_serialsetting.h"
#include <QtSerialPort/QSerialPortInfo>
#include <QIntValidator>
#include <QLineEdit>
QT_USE_NAMESPACE
static const char blankString[] = QT_TRANSLATE_NOOP("SettingsDialog", "N/A");
SerialSetting::SerialSetting(QWidget *parent) :
QWidget(parent),
ui(new Ui::SerialSetting)
{
ui->setupUi(this);
intValidator = new QIntValidator(0, 4000000, this);
//ui->cboBaudRate->setInsertPolicy(QComboBox::NoInsert);
fillPortsParameters(); //call function to fill comboboxes
connect(ui->btnApply, SIGNAL(clicked()),this, SLOT(apply()));
}
SerialSetting::~SerialSetting()
{
delete ui;
}
void SerialSetting::fillPortsParameters()
{
//fill cboComport with all available comports
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
ui->cboComport->addItem(info.portName());
}
}
void SerialSetting::apply()
{
SerialSetting::currentSettings.portName = ui->cboComport->currentText();
hide();
}
SerialSetting::Settings SerialSetting::settings()
{
return SerialSetting::currentSettings;
}
void SerialSetting::on_btnApply_clicked()
{
}
The compiler throws this exception:
QObject::connect: Cannot queue arguments of type 'SerialSetting::Settings'
(Make sure 'SerialSetting::Settings' is registered using qRegisterMetaType().)
I tried qRegisterMetaType<SerialSetting>(); but this ended in the following error:
static assertion failed: Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system
#define Q_STATIC_ASSERT_X(Condition, Message) static_assert(bool(Condition), Message)
Adding the Makro Q_DECLARE_METATYPE(Ui::SerialSetting) at the end of the class-header throws another error:
invalid application of 'sizeof' to incomplete type 'Ui::SerialSetting'
isLarge = (sizeof(T)>sizeof(void*)),
You can't call Q_DECLARE_METATYPE on a forward-declared class (Ui::SerialSetting). Also, you need to declare the type that the signal uses as a parameter, in this case, SerialSetting::Settings.
Replace
Q_DECLARE_METATYPE(Ui::SerialSetting)
with
Q_DECLARE_METATYPE(SerialSetting::Settings)
and you should be fine.
I have wrote a simple signal - slot. where i member objects signal is connected to slot of the class. I gets error
I have given the code below... If I keep the connect in the constructor then also i get error.
#ifndef COUNTER_H
#define COUNTER_H
#include <QObject>
#include <QTextEdit>
class Counter : public QObject
{
Q_OBJECT
public:
Counter()
{
m_value = 0;
}
int value() const
{
return m_value;
}
public slots:
void setValue(int value);
void callSetValue();
signals:
void valueChanged(int newValue);
private:
int m_value;
QTextEdit m_text;
};
#endif // COUNTER_H
//counter.cpp
#include "counter.h"
void Counter::setValue(int value)
{
QObject::connect (&m_text, SIGNAL(textChanged()), this, SLOT(callSetValue()));
qDebug("setValue invoked");
if (value != m_value)
{
m_value = value;
m_text.setText("hai");
emit valueChanged(value);
qDebug("emited signal");
}
}
void callSetValue()
{
qDebug("callSetValue() invoked");
}
gives me error "undefined reference to 'Counter::callSetValue()'
At the bottom of counter.cpp change
void callSetValue()
to
void Counter::callSetValue()
Did you try re-running qmake? The code for signals may not have been generated if you modified signals or slots without running qmake again.
I have some trouble with using enum types in signals. Basicly I have two classes, a state machine and a thread handling the state machine. When the state is changed I want to send a signal with the new state. I also want to represent the state using an enum. In my full blown code the state machine is implemented in a separate shared library, but the code below gives the exact same error.
When I run the code I get the following behaviour:
kotte#EMO-Ubuntu:sigenum $ ./sigenum
Object::connect: No such slot MyThread::onNewState(state)
Test signal
Test signal
...
I have four files in my sample code: statemachine.h, statemachine.cpp, main.h and main.cpp. The main function simply starts the thread, the thread then creates an instance of the StateMachine and processes signals from the StateMachine. I am pretty new to Qt, so I was a bit puzzled when I realised that you have to enclose the enum with Q_ENUMS and register it with the type system. So It's fully possible that I've made some rookie mistake
The code below is a bit long, but I wanted it to be as similar to my real code as possible.
statemachine.h looks like:
// statemachine.h
#ifndef _STATEMACHINE_H
#define _STATEMACHINE_H
#include <QtCore>
class StateMachine : public QObject
{
Q_OBJECT
Q_ENUMS(state)
public:
enum state {S0, S1, S2};
void setState(state newState);
signals:
void stateChanged(state newState);
void testSignal(void);
};
Q_DECLARE_METATYPE(StateMachine::state);
#endif
And it is implemented as:
// statemachine.cpp
#include <QtCore>
#include "statemachine.h"
void StateMachine::setState(state newState)
{
emit stateChanged(newState);
emit testSignal();
}
The thread is defined as
// main.h
#ifndef _MAIN_H
#define _MAIN_H
#include <QtCore>
#include "statemachine.h"
class MyThread : public QThread
{
Q_OBJECT
private:
void run(void);
private slots:
void onNewState(StateMachine::state);
void onTestSignal(void);
private:
StateMachine *myStateMachine;
};
#endif
And it is implemented as follows:
// main.cpp
#include <QtCore>
#include <QApplication>
#include "statemachine.h"
#include "main.h"
void MyThread::run()
{
myStateMachine = new StateMachine();
qRegisterMetaType<StateMachine::state>("state");
// This does not work
connect(myStateMachine, SIGNAL(stateChanged(state)),
this, SLOT(onNewState(state)));
// But this does...
connect(myStateMachine, SIGNAL(testSignal()),
this, SLOT(onTestSignal()));
forever {
// ...
myStateMachine->setState(StateMachine::S0);
}
}
void MyThread::onTestSignal()
{
qDebug() << "Test signal";
}
void MyThread::onNewState(StateMachine::state newState)
{
qDebug() << "New state is:" << newState;
}
By using fully qualified names everywhere I got it to work
If I change the declaration of stateChanged() to
signals:
void stateChanged(StateMachine::state newState);
And registers the type with
qRegisterMetaType<StateMachine::state>("StateMachine::state");
and also uses this name in the connect statement
connect(myStateMachine, SIGNAL(stateChanged(StateMachine::state)),
this, SLOT(onNewState(StateMachine::state)));
Wouldn't have solved this without the input from drescherjm, thanks :-)
I believe the following is state is not defined in your MyThread class.
Use the following
connect(myStateMachine, SIGNAL(stateChanged(StateMachine::state)),
this, SLOT(onNewState(StateMachine::state)));
Edit:
Maybe this will work
connect(myStateMachine, SIGNAL(stateChanged(state)),
this, SLOT(onNewState(StateMachine::state)));
You should get rid of SIGNAL and SLOT since Qt can detect mismatches at compile time. You can also avoid having to use Q_DECLARE_METATYPE and qRegisterMetaType() by using Q_ENUM instead of Q_ENUMS - this was introduced in Qt 5.5, Finally enum class is a strongly typed version of enum:
// statemachine.h
#ifndef _STATEMACHINE_H
#define _STATEMACHINE_H
#include <QtCore>
class StateMachine : public QObject
{
Q_OBJECT
Q_ENUM(state)
public:
enum class state {S0, S1, S2};
void setState(state newState);
signals:
void stateChanged(state newState);
void testSignal(void);
};
#endif
// main.cpp
#include <QtCore>
#include <QApplication>
#include "statemachine.h"
#include "main.h"
void MyThread::run()
{
myStateMachine = new StateMachine();
connect(myStateMachine, &StateMachine::stateChanged, this, &MyThread::NewState);
connect(myStateMachine, &StateMachine::testSignal, this, &MyThread::onTestSignal);
forever {
// ...
myStateMachine->setState(StateMachine::S0);
}
}