Connect an external library call by signals and slots
-
Hi! I'd like to ask if using the signals and slots mechanism is possible when using a static library which is not written with Qt? Let's say the setup is like this:
- Qt project with GUI, static library linked
- A class in this Qt project utilizes some of the library methods
- Each time an action is performed in this library, it sets a message string
- It does provide a getMessage() kind of method to retrieve the current string
Since the library cannot emit any signals, is it at all possible to get the changing messages in the GUI application? I thought of a solution with using a QTimer with a short timeout, which would run and probe the library by calling its getMessage function. This doesn't seem right though for several reasons: the timeout would have to be very short = resource hungry, plus I could still miss the messages by calling the getMessage() at a wrong time.
Somewhere arount the Internet I found someone suggesting to wrap the library calls in some Qt class, but I don't have an idea on how I should start with something like this.
Here's some pseudo-code to explain what I'd like to achieve.
// External library bool ClassA::doSomething() { // Perform an action setMessage("Message1"); // Perform another action setMessage("Message2"); ... return true; } // Qt application LibraryA::ClassA *classA = new LibraryA::ClassA(); connect(classA, &LibraryA::ClassA::setMessage, this, &MainWindow::receiveMessage); classA->doSomething(); void MainWindow::receiveMessage() { QString messageReceived = QString::fromStdString(classA->getMessage()); ui->textBrowser->append(messageReceived); } ``
-
@szumial
While you wait for somebody more knowledgeable than I to answer.You won't be able to do the
connect(classA, &LibraryA::ClassA::setMessage, this, &MainWindow::receiveMessage);
because, of course, the external library'sLibraryA::ClassA::setMessage
won't be a Qt signal.It seems to me there can only be two possibilities:
-
ClassA::doSomething()
, or thesetMessage()s
it calls, must be changed to call something Qt to emit a signal. But if you can't change source, or cannot call/include anything Qt however indirectly, it can't. You cannot miraculously "know" that some value has changed or some function has been called in that separate library. If you can have, say, an external function pointer that it calls which you can set to invoke something in Qt that would be great, but otherwise it gets you nowhere. -
Which leaves some kind of
QTimer
in the Qt side which will "poll" whatever to see if somethings has changed in the other library and then raise a Qt signal.
the timeout would have to be very short = resource hungry, plus I could still miss the messages by calling the getMessage() at a wrong time.
It would have to be as short as the minimum delay acceptable to you to notice whatever had changed. Not necessarily "resource hungry" per se, not ideal, but not that bad, if your frequency is not too short.
You shouldn't be "missing messages by calling getMessage() at the wrong time". You would have to arrange that this getMessage() gets the last message, or messages, at the external side because that "buffered" or "saved" when whatever event occurred and returned it at the next getMessage() call.
-
-
If its as simple as in your example then I would go totally without signals slots
Just don't call doSomething() method of the class but make a wrapper class in mainwindow and let Class::doSomthing() return the string
QString ClassA::doSomething() { // Perform an action return("Message1"); }
wrapper in mainwindow:
void MainWindow::doSomething() { QString messageReceived = m_classA->doSomething() // do something now directly returns the ui->textBrowser->append(messageReceived); }
and this is how you call it
// class is a member variable of mainwindow LibraryA::ClassA *m_classA = new LibraryA::ClassA(); this->doSomething();
-
is
ClassA::setMessage
declared as virtual and public or protected?
If so then it's trivial:// Qt application class ClassQA : public QObject, public LibraryA::ClassA{ Q_OBJECT public: using ClassA::ClassA; signals: void messageChanged(); protected: // here I assume it's protected void setMessage(const std::string& msg) override{ LibraryA::ClassA::setMessage(msg); messageChanged(); } }; ClassQA *classA = new ClassQA(); connect(classA, &ClassQA::messageChanged, this, &MainWindow::receiveMessage);
-
Thanks for the helpful answers! I do have access to the lower layer library code and I can change it, however, I cannot add Qt proprietary code to it. The setMessage method could be made virtual and I will try with this solution and post the results as soon as I get it working.
-
To interface with a third party library you need an interface layer. You need to decide under what circumstances you need to call those 3rd party routines, and then you have a choice to make:
- create a separate event loop thread that links Qt signals/slots to the callbacks,
- use a timer to poll the callbacks
- extend the Qt event loop to poll/handle the callbacks directly
most of the time I'd choose option number 1.