I have created a set of watcher/helper classes that monitor an outside application, and I would like these to print to a log window inside my MainWindow's ui when changes are made. My idea was to create this watcher inside a thread in main, but I am not sure the best way to link it to my MainWindow. I know that simply passing it as below will not work, but am unsure of the proper way to do this, if there even is one, because of Qt's design. Some research indicated to me that it isn't really appropriate to pass MainWindow to an outside class, but I am unsure of a better way to do it.
int main(int argc, char *argv[])
{
try {
std::string host = "localhost";
unsigned short port = 8193;
MainWindow w;
w.show();
// Access class for the program that I am watching
UserInterface ui(host, port);
// StatePrinter class, which is registered in the UI and will print changes to MainWindow, ideally
// would I be able to pass something in the StatePrinter constructor to accomplish my goal?
ui.registerObserver(new StatePrinter(w));
UiRunner runner(ui);
boost::thread runnerThread(boost::ref(runner));
w.show();
QApplication a(argc, argv);
runner.cancel();
runnerThread.join();
return a.exec();
} catch(std::exception &ex) {
// ...
}
}
I suppose it is a possibility to make this thread inside MainWindow itself, but I prefer having it in main. What would be the best way to link my StatePrinter class to MainWindow?
You could make your watcher class a QObject itself, push it into a thread, and make it emit signals when it "notices" changes that you want to log with the log informations as the signal parameters.
Then, you could push this object in a QThread as follow :
QThread* thread = new QThread();
ui->moveToThread(thread);
//Create the needed connections
thread->start();
Depending on what you need, you may connect a signal to the thread's start() slot instead of calling it directly. (Read this to know which connections you'll need with your thread so it is started, stopped and cleaned correctly).
You have several problems:
Creating widgets before instance of QApplication
runnerThread.join call will block main Qt thread before entering Qt event loop - so your GUI will be freezed
You should implement notification system to watch for termination of boost threads. But better solution is to use Qt threads.
You should create first class - "watcher" with neccessary signals.
Then create second class with UI logic and neccessaty slots
Then connect signals to slots
Profit!
Look at Qt documentation about QThread - there are simple samples.
Related
I was used, in java, to create events almost anywhere, but in c++ (Qt), I notice that you have to create a class to be able to use the object's events. My question is the following : would it be possible to use MouseEvent (or any other event) belonging to a QPushButton from the main function ?
#include <QApplication>
#include <QWidget>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
QWidget window;
QPushButton* btn = new QPushButton("Add",&window);
//Here, an event related to 'btn' to update the window...
window.show();
return app.exec();
}
PS : I know it's better to use the Qt Designer form, but I'm just asking about the possibility of doing this task.
Some events, e.g. mouse movement or focus changes, are not accessible through slots/signals.
You can use a small proxy QObject to filter events for other objects. See installEventFilter() for a code sample. You don't need to actually filter the events; you may just listen in and let them pass through.
Likewise, you can trigger/fake an event manually through QCoreApplication via notify().
As Joel Bodenmann notes, Qt uses signals and slots. You can have a slot on a QObject, but Qt can also connect to a lambda. Your lambda would have to capture window by reference, so it can update the window.
You'd probably want to connect the clicked event.
Qt offers the signal & slots mechanism. You can simply connect the QPushButton::clicked() signal to a slot that then performs the update you're referring to.
Using C++11's lambda:
// Create pushbutton
QPushButton* btn = nw QPushButton("Add",&window);
// Connect slot to 'clicked' signal
QObject::connect(btn, &QPushButton::clicked, []{
qDebug("Button clicked!");
// ... whatever else you want to happen on a button click
});
Keep in mind that you have other problems in your code as pointed out in the comments.
I am currently coding a program in C++/ Qt 5.9 to control a 3D Printer.
I have divided the project into several libraries :
The GUI Lib: The view
The Device Lib: Control each device (movement, printing tool, sensors)
The Controller Lib: Links the View with the Device lib
etc...
My problem is that I do not know how to initialize and terminate properly the execution of the program. More specifically, the Device library has a class named DeviceManager with two functions :
initialize() : which connect to each device and initialize them. This actions can take several seconds (10s for example)
finalize() : which closes all the connexions, can also take several seconds
What I would like to do is initialize the Device Lib in the right place without blocking the GUI and then finalize it in the right and not block the GUI
This is my main code :
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
My solution now is to call DeviceManager::initialize() in the MainWindow constructor and DeviceManager::finalize() in the MainWindow::closeEvent().
The problem: even if I use concurrency to initialize and finalize in a different thread, the application displays its window several seconds after it was launched and the display freezes when the application is closed because it has to wait for the finalize function to be done.
How should I properly handle this initialization and finalization problem, please ? Do I need to reimplement the QApplication class? Do I try and do it in the main? Do you know any great example of an open source application doing that kind of initialize and finalize work?
The problem: even if I use concurrency to initialize and finalize in a different thread, the application displays its window several seconds after it was launched and the display freezes when the application is closed because it has to wait for the finalize function to be done.
You could start the initialize() function as own detached thread before starting the MainWindow. This could cause problems if the initializing thread doesn't terminate before the first initialized "thing" is needed. Something like:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
std::thread{initialize}.detach();
MainWindow w;
w.show();
finialize();
return a.exec();
}
To prevent the problem, and maybe lock some function in the GUI, then you probably need to have a state variable that it sets under a mutex on its way out, and which you examine under a mutex to see if it has been set.
According this article here, is it Ok to say that, when we close a Qt programm. Once the mainWindow's close event started, nothing else except the close event handler and class deconstructor in the GUI thread will be executed?
For example, if there is a QTimer fired the timeout() signal in the GUI thread to a slot in the same thread. When the mainWindow's close event started, the slot will not get any chance to be executed again?
No, it is wrong on its face to say that once the mainWindow's close event started, nothing else will execute in the GUI thread except the close event handler and class destructors.
Suppose we have following code (in C++11):
#include <QApplication>
#include <QLabel>
#include <QMessageBox>
#include <QTimer>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLabel label1("Hello, World!");
label1.show();
label1.setMinimumSize(200, 100);
app.exec();
QTimer timer;
int counter = 0;
QObject::connect(&timer, &QTimer::timeout, [&counter]{ counter ++; });
timer.start(100);
QMessageBox::information(nullptr, "Done",
QString("We're Done with \"%1\"").arg(label1.text()));
QMessageBox::information(nullptr, "Really Done",
QString("Timer has ticked %1 times").arg(counter));
return 0;
}
Obviously, non-destructor code will execute after exec() has returned - here we show a message box. The main window, in this case a QLabel, still exists after exec() has returned.
It is also incorrect to assume that somehow the timers will be globally killed after exec() has returned. They're still running, and as long as you re-start the event loop, they'll gladly fire for you. The second message box will display a non-zero count, after all.
Your question should be reworded with the problem you're trying to solve. You're asking for very specific details that make little sense on their own. Please tell use why you ask such a question.
It's handled automatically:
QObject::~QObject()
All signals to and from the object are automatically disconnected, and
any pending posted events for the object are removed from the event
queue. However, it is often safer to use deleteLater() rather than
deleting a QObject subclass directly.
However for multi-threaded apps you need to use deleteLater() to avoid crashes:
Warning: Deleting a QObject while pending events are waiting to be
delivered can cause a crash. You must not delete the QObject directly
if it exists in a different thread than the one currently executing.
Use deleteLater() instead, which will cause the event loop to delete
the object after all pending events have been delivered to it.
I will keep the code simple so that you guys can see what I'm trying to do ;)
I am aware of all of the locking issues, etc. I'm trying to figure out how signals and slots play with threads.
In main.cpp:
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MyConsole c; // Subclasses QThread and implements run()
MyReceiver r(app); // We pass app to MyReceiver for later (see below)
QObject::connect(&c, SIGNAL(sendit()),
&r, SLOT(gotit()));
c.start(); // Start the worker thread
app.exec();
}
Assume that the signals and slots were properly set up in the header files (I've tested and they are). Now, here's the issue:
In MyReceiver.cpp:
void MyReceiver::gotit()
{
QLabel *label = new QLabel(0, "Hello"); // Some GUI element, any will do
app.setMainWidget(*label); // Some GUI action, any will do
}
The question is: Because the MyReceiver object was created in main(), which is on the main thread, does that mean that the slots (e.g., gotit()) will run on the main thread and are therefore safe for doing GUI stuff? Even in cases where the signal was raised from a different QThread (like MyConsole in this example)?
Is there a better way to allow worker threads to interact with the GUI (for example, Obj-C/Cocoa have a "send message on main thread" type of approach). What is "The Qt way" of doing this?
Thanks in advance!
By default (Qt::AutoConnection), slots will run in the thread the QObject was created in. So, no matter from what thread you emit the signal, the slot will be run always in the thread, the QObject "lives" in (if a Qt event loop is running in that thread, otherwise the event can't be delivered). Since the main thread will become the Qt GUI thread, this will work as expected. This is indeed the Qt way of interacting with the GUI.
See also: http://doc.qt.nokia.com/4.7/thread-basics.html (look for thread affinity).
The "Qt way" to emit a signal from one thread and receive it in a different thread is to use a Queued connection
connect( obj, SIGNAL(foo()), other_obj, SLOT(bar()), Qt::QueuedConnection )
From the Qt documentation for Qt::QueuedConnection:
The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
I am new to Qt GUI programming and come from a .NET/Winforms background as far as GUI development goes. I am using the Qt Creator IDE.
How can I handle a button press event in Qt to do something like the following:
if (button.clicked = true)
{
startProgram();
}
Also, how would I browse for a configuration file which would populate values into all of my line edit textboxes when opened? I am programming this in C++.
EDIT: I am taking a console app that someone else wrote and building a GUI around it. I want to access one of the functions in there from a button click event. However, I can't figure out how to get the functions of the original app to be in scope of the GUI which I have created when I try to use SIGNALS and SLOTS.
A simple example could be something like this. Say you have a class like ProgramStarter:
#include <QObject>
class ProgramStarter : public QObject {
Q_OBJECT
public slots:
void startProgram() {
//Do stuff
}
};
and something like this where ProgramStarter is used:
#include <QtGui/QApplication>
#include <QPushButton>
#include <QObject>
#include "programstarter.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPushButton *testButton = new QPushButton("Push me");
testButton->resize(100, 40);
testButton->show();
ProgramStarter *program = new ProgramStarter;
QObject::connect(testButton, SIGNAL(clicked()), program, SLOT(startProgram()));
return app.exec();
}
Qt uses signals and slots to communicate between objects. The core idea is that signals are emitted when events occur and these signals can be connected to slots. In this case the button emits a clicked() signal when the button is pressed and the clicked() signal is connected to the startProgram() slot. So when the button is pressed, startProgram() is called.
Qt has a meta-object system for enabling the use of signals and slots, among other things. This is the reason why ProgramStarter inherits QObject and why the Q_OBJECT macro is in the header. I highly suggest reading this if you want to understand how signals and slots and the meta-object system work in Qt: http://doc.qt.io/qt-5/signalsandslots.html
It's a little long to explain but you connect the signal from the button to the slot with the function.
connect(button, SIGNAL(triggered()), this, SLOT(doSomething()));
You really need to read and understand signals and slots before trying to write code
edit: You are trying to call functions in a compiled separate running program?
Are you confusing Qt Signals with C/Unix signals? You could start the separate app from within your Qt app, get its PID and then send it a 'c' signal.
Or if you have the source of the app you could include it's code into your Qt app and then call any of the functions by simply wrapping them with a Qt slot handler.
In order to access the functions defined in the console application, you need to include the headers (.h/.hpp) that declare these functions, and link their implementation. I you have the source code of the implementations (.cpp), you need to add them to the sources to be compiled (they will be compiled in object files (.o) that will be linked in the final executable). If the console application is a front-end to a set of functions defined in a library (.lib, .a), you need to link this library.
After that, you can use Qt's signal/slot mechanism to connect the "button clicked" signal to a slot calling the function you want to execute, as described in other answers.
It seems like your problem is not really to do with Qt or GUI programming at all, but rather a general design issue. Others have told you how to be notified when the button is pressed - it's up to you to hook that notification up to whatever code you want to call. Some possibilities:
If you just need to call a function then include the appropriate header file and call it.
If you have an object you want to call a method on, then you will need to provide a reference to that object to the class which receives the notification.
If your console app is a separate program, you can use QProcess to launch and communicate with it.
I guess your problem is that you cannot call the console app's function, because it hasn't been defined to be a slot. So here is how you might do it.
Let's assume you have a console app class which might look like this:
class ConsoleApp
{
public:
void run()
{
// Code which you want to trigger by a button in your UI.
}
}
Then we implement a Qt based wrapper class which inherits from QObject class and, hence, is able to send and receive signals. This class simply provides a custom slot which delegates execution to the ConsoleApp class.
class ConsoleAppWrapper : public QObject
{
Q_OBJECT
public slots:
void startProgram()
{
m_consoleApp.run();
}
private:
ConsoleApp m_consoleApp;
}
Ok, and now we want a button which, when pressed, will trigger a call of the 'run()' method.
int main(int argc, const char** argv)
{
QApplication app(argc, argv);
// Define wrapper for console app.
ConsoleAppWrapper wrapper;
// Define button and connect its 'clicked()' signal
// to the wrapper's 'startProgram()' slot.
QPushButton startProgramButton("Start Program");
connect(&startProgramButton, SIGNAL(clicked()), &wrapper, SLOT(startProgram()));
// Show the button.
startProgramButton.setVisible(true);
// Start Qt's event loop.
app.exec();
}
This should give you a 'Start Program' button which, when pressed, will call ConsoleApp::run() method.
While this example is not Qt Designer based I hope this example helps you hunderstand how signals and slots work in Qt.
Cheers,
Jonny
If you have a button created with the Designer you can use on_myButton_clicked() slot in the parent widget class, just like in WinForms.