Is there a way to print the signals and slots being called?
I'm experiencing a weird deadlock in Qt which happens only in particular conditions across multiple threads, and I'd like to know the order of signals/slots being called.
Of course, for slots, I write the method body, and in the worst scenario, I can manually add a print out of the method. But the body of signals is generated automatically, so it is not possible, unless I write a custom moc, which seems like an overkill for this task...
If one leverages the built-in hooks, it's possible to automatically instrument all signals, and all slots connected using the Qt 4 connect syntax. Unfortunately, QtPrivate::QSlotObject doesn't implement these hooks: slots connected using the Qt 5 syntax need to be instrumented manually (e.g. by connecting a functor to them, or adding code to them).
Signal notifications can be relied on for connected signals. Objects with no signals, and some signals of objects with other connections, will not be reported. This is presumably what you want.
Thus:
// https://github.com/KubaO/stackoverflown/tree/master/questions/signal-spy-39597233
#include <QtCore>
#include <private/qobject_p.h>
int signalToMethodIndex(const QMetaObject * mo, int signal)
{
Q_ASSERT(signal >= 0);
for (int i = 0; i < mo->methodCount(); ++i) {
if (mo->method(i).methodType() == QMetaMethod::Signal) {
if (signal == 0) return i;
-- signal;
}
}
return -1;
}
class Spy {
static QThreadStorage<bool> entered;
static void signalBegin(QObject *caller, int signalIndex, void **) {
if (entered.localData()) return;
QScopedValueRollback<bool> roll{entered.localData(), true};
auto index = signalToMethodIndex(caller->metaObject(), signalIndex);
if (index >= 0)
qDebug() << "SIGNAL" << caller << caller->metaObject()->method(index).methodSignature();
}
static void slotBegin(QObject *caller, int index, void **) {
if (entered.localData()) return;
QScopedValueRollback<bool> roll{entered.localData(), true};
qDebug() << "SLOT" << caller << caller->metaObject()->method(index).methodSignature();
}
public:
static void start() {
QSignalSpyCallbackSet set{&signalBegin, &slotBegin, nullptr, nullptr};
qt_signal_spy_callback_set = set;
}
};
QThreadStorage<bool> Spy::entered;
struct Class : QObject {
Q_SIGNAL void aSignal();
Q_SLOT void aSlot() { qDebug() << "slot"; }
Q_OBJECT
};
int main(int argc, char ** argv) {
Spy::start();
QCoreApplication app{argc, argv};
Class obj;
QObject::connect(&obj, SIGNAL(aSignal()), &obj, SLOT(aSlot()));
obj.setObjectName("obj");
emit obj.aSignal();
}
#include "main.moc"
Output:
SIGNAL Class(0x7fff51901af0, name = "obj") "objectNameChanged(QString)"
SIGNAL Class(0x7fff51901af0, name = "obj") "aSignal()"
SLOT Class(0x7fff51901af0, name = "obj") "aSlot()"
slot
Related
How could using boost::asio::deadline_timer with Qt ? I am new to Boost C++ library and try documentation examples in separate project and it work. But i could not using it in my project which use Qt for GUI.
Example of my problem
Note:
I Need a long interval and QTimer could not work with a long interval.
Based on an answer to your previous question it's very easy to implement your own deadline timer using Qt only (that seems to be what you'd prefer)...
class deadline_timer: public QObject {
using super = QObject;
Q_OBJECT;
public:
using deadline_id = uint_least64_t;
signals:
void timeout(deadline_id, QDateTime) const;
public:
explicit deadline_timer (int resolution_milliseconds = 1000, QObject *parent = nullptr)
: super(parent)
{
m_timer.setInterval(resolution_milliseconds);
QObject::connect(&m_timer, &QTimer::timeout, this, &deadline_timer::handle_timeout);
}
/*
* Adds a new deadline and returns associated id. When the deadline is
* reached the timeout signal will be emitted with the corresponding id
* and time.
*/
deadline_id start (const QDateTime &deadline)
{
m_deadlines[deadline].insert(++s_id);
if (!m_timer.isActive()) {
m_timer.start();
}
return s_id;
}
void stop ()
{
m_timer.stop();
}
private:
void handle_timeout ()
{
auto now = QDateTime::currentDateTime();
for (auto i = m_deadlines.begin(); i != m_deadlines.end() && now >= i->first; i = m_deadlines.begin()) {
for (const auto &j: i->second) {
emit timeout(j, i->first);
}
m_deadlines.erase(i);
}
if (m_deadlines.empty()) {
m_timer.stop();
}
}
static deadline_id s_id;
QTimer m_timer;
std::map<QDateTime, std::set<deadline_id>> m_deadlines;
};
deadline_timer::deadline_id deadline_timer::s_id = 0;
The use as...
deadline_timer dt;
QObject::connect(&dt, &deadline_timer::timeout,
[](deadline_timer::deadline_id id, QDateTime deadline)
{
std::cout << "\ndeadline " << id << " passed\n";
});
auto id = dt.start(QDateTime::currentDateTime().addYears(1));
I have created a wrapper library around QSerialPort. I want to communicate with my device. First, I send list command to my device and it should return list of commands supported by that device. However, while debugging my code, i observed that list command is being send to the device and device returns the proper response to it (i debugged using serial traffic sniffer Linux tools). However, i am not getting any response from my device using QSerialPort (while serial traffic sniffer tool was disable). I am unable to get it work after testing it several times.
My Serial.h:
class Serial : public Print {
public:
Serial();
explicit Serial(const char *dev_path);
~Serial();
int begin(unsigned long baudrate);
int begin(unsigned long baudrate, uint8_t cfg);
void end(void);
int available(void) const;
bool availableForWrite(void) const;
void flush(void);
bool isError(void) const;
void reset(void);
unsigned long write(uint8_t c);
unsigned long write(uint8_t *p_data, unsigned long maxSize);
int read(void);
void close();
QSerialPort &getPort()
{
return *_p_port;
}
public slots:
void readyBe(void);
private:
QSerialPort *_p_port;
unsigned long _baudrate;
};
My Serial.cpp:
Serial::Serial()
{
_p_port = new QSerialPort();
if (_p_port == nullptr)
throw std::runtime_error("Can't allocate memory");
}
Serial::Serial(const char *dev_path)
{
_p_port = new QSerialPort(QString(dev_path), QApplication::instance());
if (_p_port == nullptr)
throw std::runtime_error("Can't allocate memory");
// _p_port->setPortName(QString(dev_path));
if (_p_port->open(QIODevice::ReadWrite) == false) {
throw std::runtime_error("Can't open the serial _p_port");
delete _p_port;
}
_p_port->setBaudRate(QSerialPort::Baud9600);
_p_port->setDataBits(QSerialPort::Data8);
_p_port->setParity(QSerialPort::NoParity);
_p_port->setStopBits(QSerialPort::OneStop);
_p_port->setFlowControl(QSerialPort::NoFlowControl);
}
Serial::~Serial()
{
if (_p_port != nullptr) {
end();
delete _p_port;
}
}
int Serial::begin(unsigned long baudrate)
{
if (_p_port->setBaudRate(baudrate, QSerialPort::AllDirections) == false)
return -1;
_baudrate = baudrate;
return 0;
}
void Serial::end()
{
if (_p_port->isOpen())
_p_port->close();
}
int Serial::available(void) const
{
int num_bytes = _p_port->bytesAvailable();
return num_bytes;
}
bool Serial::availableForWrite(void) const
{
if (_p_port->isWritable())
return true;
return false;
}
void Serial::flush()
{
_p_port->flush();
}
unsigned long Serial::write(uint8_t c)
{
if (_p_port->putChar(c))
return 1;
return 0;
}
unsigned long Serial::write(uint8_t *p_data, unsigned long maxSize)
{
return _p_port->write(reinterpret_cast<const char *>(p_data), (qint64)maxSize);
}
int Serial::read(void)
{
char c;
_p_port->getChar(&c);
return c;
}
void Serial::reset(void)
{
_p_port->clear(QSerialPort::AllDirections);
_p_port->clearError();
}
bool Serial::isError(void) const
{
if (_p_port->error() == QSerialPort::NoError)
return false;
return true;
}
And my main.cpp:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
// w.show();
Serial serial("ttyACM0");
if (serial.begin(115200))
std::cout << "Failed to set Baud rate" << std::endl;
std::cout << "Sending data" << std::endl;
// QObject::connect(&(serial.getPort()), SIGNAL(readyRead()), &serial, SLOT(readyBe()));
serial.print("list\r");
serial.flush();
while (true) {
while (true) {
while (serial.available() == 0) {
if (serial.isError()) {
std::cout << "Error" << std::endl;
// serial.reset();
}
}
char c = serial.read();
std::cout << c;
if (c == '\n')
break;
}
std::cout << std::endl;
}
return a.exec();
}
You've pretty much missed everything needed for this code to work: the event loop. I/O in real life is asynchronous. You can't just "read" from the port without having some means of getting informed when the data is available, and actually letting the asynchronous I/O requests get processed. Yes, there are some legacy APIs that let you do that, but they mostly lead to spaghetti code, wasted threads, and poor performance.
The while (serial.available() == 0) loop is a no-op. It does nothing to actually let the available() return any other value. All that available() does internally is read an integer member of a class. You never run any code that could update the value stored in that member. Even if you would convert this to serial.waitForReadyRead(), which does update the number of available bytes, you're still not spinning an event loop, and thus you won't be able to process timeouts, or react to any other events an application might need to react to. QIODevice::waitForReadyRead is only meant to do one thing: return when a readyRead signal would fire. It won't process any other events, and it's a crutch used to port blocking code and is not really meant for production use.
You should redesign your code to be asynchronous and driven by signals from QSerialPort. The work will then be done from within QCoreApplication::exec - you won't have a loop of your own. This inversion of control is critical for getting async I/O working.
See e.g. this answer for a very simple example of an async approach, and this answer for a more complete one.
I have a problem, that remaining sent signals are not received after calling quit on QThread object.
The scenario contains 2 additional threads (QThread and std::thread) and the main execution thread. Let's call the QThread Q, the std::thread T and the main thread M.
In M I create Q, the Receiver-object R "living" in Q and the Sender-object S. Also a std::thread T is created executing a bunch if emits with S.
class Sender : public QObject
{
Q_OBJECT;
public:
std::vector<int> m_Sent;
Sender()
{
}
public slots:
signals:
void signal(int i);
public:
void send(int i)
{
m_Sent.emplace_back(i);
emit signal(i);
}
};
class Receiver : public QObject
{
Q_OBJECT;
public:
std::vector<int> m_Received;
Receiver()
{
}
void Connect(Sender* s)
{
connect(s, &Sender::signal, this, &Receiver::slot, Qt::QueuedConnection);
}
void Disconnect(Sender* s)
{
disconnect(s, &Sender::signal, this, &Receiver::slot);
}
public slots:
void slot(int i)
{
m_Received.emplace_back(i);
}
};
void main(int argc, char** argv)
{
QApplication app(argc, argv);
qint64 random_seed = QDateTime::currentMSecsSinceEpoch();
std::cout << "Setting random seed " << random_seed << "\n";
std::srand(random_seed);
std::unique_ptr<Receiver> R(new Receiver);
std::unique_ptr<Sender> S(new Sender);
auto actions = [&S]() {
int i = 0;
std::chrono::steady_clock::time_point current =
std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point finish =
current + std::chrono::milliseconds(100);
while (current < finish)
{
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_until(current + std::chrono::milliseconds(17));
current = std::chrono::steady_clock::now();
}
};
std::unique_ptr<QThread> Q(new QThread());
R->moveToThread(Q.get());
R->Connect(S.get());
Q->start();
std::thread T(actions);
T.join();
// approach 1:
QMetaObject::invokeMethod(Q.get(), "quit", Qt::QueuedConnection);
Q->wait(); // never returns
// approach 2:
Q->quit();
Q->wait(); // missing events
std::cout << "Sent: ";
for(auto v : S->m_Sent)
{
std::cout << v << " ";
}
std::cout << std::endl;
std::cout << "Received: ";
for(auto v : R->m_Received)
{
std::cout << v << " ";
}
std::cout << std::endl;
}
I'm working on Windows with VS2013 and Qt 5.5.1. I tested it with kind of counter in R to track received signals. While debugging I went through all emits so all should be inserted to event loop in Q. After Q.wait() the counter for the slots do not correspond to the emitted signals. I would have expected the event loop with remaining input events was handled by Q.quit() or Q.wait() but seems not so, It's always that there is a cut of "event-stream" from a certain point onward. I tried now for 4 days going through Qt-Docu and several other stuff found by google, but no proposal worked so far.
I am not 100% sure since the documentation is not crystal clear, but what makes you think that the even loop is processing all pending events before exiting ? My assumption would be that there is a check "should I exit" at every loop and that it can discard some pending events when the exit flag is set.
In order to summarize the discussion below, I would suggest to add a new signal that you emit from wherever you want (say for instance from the std::thread once you have emitted everything you wanted) that would get into the QThread event loop queue and be connected to the QThread quit method so that the thread exits when being processed.
You can also avoid defining a new signal if you want.
Your code would look like (not tested):
Sender S = new Sender();
QThread Q = new QThread();
Receiver R = new Receiver();
R->moveToThread(Q);
connect(S, &Sender::signal, R, &Receiver::slot, Qt::QueuedConnection);
Q->start();
while(!Q.isRunning())
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::thread T([&S](){
emit S->signal(); // only an example, several other connects are used too
})
T.join();
QMetaObject::invokeMethod(Q, "quit",
Qt::QueuedConnection);
Q.wait();
In my application, I am receiving messages from LCM (Lightweight Communications and Marshalling) that contain data for multiple consumers within the application. I imagined this working with the LCM handler as a singleton so that there is one instance that each class could use. For example, each consumer class would have:
QObject::connect(LCMHandler::getInstance(), SIGNAL(messageReceived()),
this, SLOT(processMessage()));
Where lcmhandler.h is:
class LCMHandler : public QObject
{
Q_OBJECT
public:
static LCMHandler* getInstance();
LCMHandler();
~LCMHandler() {}
void handleMessage(const lcm::ReceiveBuffer* rbuf,
const std::string &chan,
const example::example_t *msg);
signals:
void messageReceived();
private:
static LCMReceiver* _instance;
};
And lcmhandler.cpp is:
LCMHandler* LCMHandler::_instance = 0;
LCMHandler::LCMHandler()
{
lcm::LCM lcm;
if(lcm.good())
{
lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this);
while(0 == lcm.handle());
} else {
std::cerr << "LCM Error" << std::endl;
}
}
LCMHandler* LCMHandler::getInstance() {
if (!_instance) {
_instance = new LCMHandler();
}
return _instance;
}
void LCMHandler::handleMessage(const lcm::ReceiveBuffer *rbuf,
const std::string &chan,
const hlelcm::transponder_t *msg)
{
std::cout << "Received message on channel " << chan.c_str() << std::endl;
emit messageReceived();
}
The application successfully prints "Received message on channel..." repeatedly; however, nothing else is executed, including code in the consumer class's processMessage(), presumably because the application gets stuck looping on handleMessage(...) and never executes the signal/slot procedure (or refreshes the UI components). So, if the implementation of processMessage() is:
void Consumer::processMessage() {
std::cout << "Message received" << std::endl;
}
It never executes, while handleMessage(...) loops infinitely. Similarly, the Qt UI never loads because handleMessage is busy looping.
What is the best way to handle the incoming messages? Should I refrain from using a singleton for LCMHandler? What do I need to change to make this implementation work?
Move the contents of your LCM constructor to another function:
LCMHandler::beginCommunication()
{
lcm::LCM lcm;
if(lcm.good())
{
//QObject base class call.
moveToThread( &_myLocalQThread );
_myLocalThread.start();
lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this);
_isActive = true;
// This is blocking, we don't want it to interfere with
// the QApplication loop
while(0 == lcm.handle());
}
else
{
std::cerr << "LCM Error" << std::endl;
}
_isActive = false;
}
Then something along these lines to allow your LCM loop to happen in another thread.
auto lcmHandler = LCMHandler::getInstance();
// I like to be explicit about the Qt::QueuedConnection. Default behavior should be thread safe, though.
connect( lcmHandler, &LCMHandler::messageReceived,
this, &Consumer::processMessage, Qt::QueuedConnection );
// Add a std::atomic< bool > _isActive to LCMHandler
if( not lcmHandler.isActive() )
{
lcmHandler.beginCommunication();
}
And then make sure to properly close your QThread in the destructor.
LCMHandler::~LCMHandler()
{
_myLocalQThread.quit();
_myLocalQThread.wait();
}
I implemented reading bytes at a serial communication in a Qt project(5.5) in Visual Studio 2013.
My code is here:
main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
//QCoreApplication coreApplication(argc, argv);
int argumentCount = QCoreApplication::arguments().size();
QStringList argumentList = QCoreApplication::arguments();
QTextStream standardOutput(stdout);
if (argumentCount == 1) {
standardOutput << QObject::tr("Usage: %1 <serialportname> [baudrate]").arg(argumentList.first()) << endl;
return 1;
}
QSerialPort serialPort;
QString serialPortName = argumentList.at(1);
serialPort.setPortName(serialPortName);
int serialPortBaudRate = (argumentCount > 2) ? argumentList.at(2).toInt() : QSerialPort::Baud9600;
serialPort.setBaudRate(serialPortBaudRate);
if (!serialPort.open(QIODevice::ReadOnly)) {
standardOutput << QObject::tr("Failed to open port %1, error: %2").arg(serialPortName).arg(serialPort.errorString()) << endl;
return 1;
}
Camera cam2;
//cam2.showCamera();
cam2.showCamera();
cam2.showFullScreen();
QByteArray abc;
SerialPortReader serialPortReader(&serialPort,abc);
//return coreApplication.exec();
return app.exec();
};
serialportreader.cpp
#include "serialportreader.h"
#include <QCoreApplication>
QT_USE_NAMESPACE
SerialPortReader::SerialPortReader(QSerialPort *serialPort, QByteArray abc, QObject *parent)
: QObject(parent)
, m_serialPort(serialPort)
, m_standardOutput(stdout)
{
connect(m_serialPort, &QSerialPort::readyRead, this, &SerialPortReader::handleReadyRead);
}
SerialPortReader::~SerialPortReader()
{
}
void SerialPortReader::handleReadyRead()
{
m_readData.append(m_serialPort->readAll());
if (!m_readData.isEmpty()) {
m_standardOutput << m_readData << endl;
m_readData.clear();
}
}
This code works successfully. I would like to set the contents of m_readData array to abc array so that I can evaluate the message in main scope. In order to implement this, I gave abc array to handleReadyRead function as a parameter. However, it gives an error.The code is here:
SerialPortReader::SerialPortReader(QSerialPort *serialPort, QByteArray abc, QObject *parent)
: QObject(parent)
, m_serialPort(serialPort)
, m_standardOutput(stdout)
{
connect(m_serialPort, &QSerialPort::readyRead, this, &SerialPortReader::handleReadyRead(abc));
}
SerialPortReader::~SerialPortReader()
{
}
void SerialPortReader::handleReadyRead(QByteArray abc)
{
m_readData.append(m_serialPort->readAll());
if (!m_readData.isEmpty()) {
m_standardOutput << m_readData << endl;
m_readData.clear();
}
}
Error:
serialportreader.cpp(45): error C2102: '&' requires l-value
line45:
connect(m_serialPort, &QSerialPort::readyRead, this, &SerialPortReader::handleReadyRead(abc));
You can't pass the local variable to the slot this way, but what you are trying to do doesn't even require it. You could just store abc to a member variable in the constructor, and use it in the slot. BUT: you have to change it to a reference, otherwise the call to the constructor copies the array, so the original one in your main function is unaffected.
You could also just implement this array as a member variable of your SerialReader, and provide a getter function, so that the main function (or whoever is the consumer of the data) can just pull this data from the reader instance.