Qt moving gui widget to gui thread - qt

So Qt has to have all GUI applications running in the Main GUI thread. I have a non-gui cpp that initiates a QDialog, and when I try to interact with the Widget I get QObject::startTimer: Timers cannot be started from another thread. That is what led me to discover that I needed to move the widget to the main thread. My question is how do I move no_id_wheel_screen to the main thread.
cpp of my non-gui thread
#include "gui_image_node.h"
gui_image_node::gui_image_node()
{
}
bool gui_image_node::init(int argc, char** argv)
{
m_pThread = new QThread();
m_no_id_wheel_screen = new no_id_wheel_screen;
this->moveToThread(m_pThread);
connect(m_pThread, &QThread::started, this, &gui_image_node::run);
connect(m_no_id_wheel_screen, &no_id_wheel_screen::ReadyHollander, this, &gui_image_node::HolPub);
hollander_pub = nh.advertise<std_msgs::String>("/hollander_chat", 1);
hol_trigger = nh.subscribe("awaiting_hollander", 1, &gui_image_node::Hollander_Screen_trigger_callback, this);
m_pThread->start();
return true;
}
//Where I start the widget
void gui_image_node::Hollander_Screen_trigger_callback(const std_msgs::String::ConstPtr& msg)
{
std::string steve = msg->data;
no_id_wheel_screen midscreen;
midscreen.exec();
}

#Frank Osterfeld Had the right idea. I just made another connection to MainWindow, then MainWindow executed my widget.

Related

Qt: using QWidgets in non GUI threads

I'm trying to understand what is and isn't allowed when it comes to QWidget and Qt concurrency. I've created a Widget which has a slow_function and I'm considering three cases:
Run the slow_function on the GUI thread. This results in the expected behaviour; the GUI becomes unresponsive while waiting for the function to return.
Use QtConcurrent::run(this, &Widget::slow_function). I was surprised to see that this didn't block the GUI. I've confirmed that the thread affinity of my instance is still the GUI thread, nevertheless, the function seems to be executing on a separate thread. Is such an approach allowed and is this the expected behaviour (documentation link would be really helpful)? Is such an approach safe if I can guarantee that slow_function is thread-safe?
Create a subclass of QThread which holds a pointer to my widget. Override the run method to call slow_function. The behaviour is the same as Case 2. This is also surprising as the thread affinity is still the GUI thread (besides, we are not even allowed to use moveToThread on a QWidget). Why is this running on a separate thread? Is moveToThread meant to be useful only when we are interested in calling slots via signals sent from another thread?
Thank you for reading. Here is the relevant code starting with my the header file:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QDebug>
#include <QPushButton>
#include <QLayout>
#include <windows.h>
#include <QtConcurrent/QtConcurrent>
#include <QThread>
#include <QApplication>
class Widget;
class Thread: public QThread
{
public:
Thread(Widget* widget)
: m_widget(widget){}
protected:
void run() override;
private:
Widget* m_widget;
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget* parent = nullptr)
: QWidget(parent)
, m_thread(this){
auto layout = new QVBoxLayout(this);
auto button = new QPushButton("Case 1: Run on gui thread");
auto button2 = new QPushButton("Case 2: Run with qtconcurrent");
auto button3 = new QPushButton("Case 3: Run with qthread");
connect(button, &QPushButton::clicked, this, &Widget::slow_function);
connect(button2, &QPushButton::clicked, this, &Widget::use_concurrent);
connect(button3, &QPushButton::clicked, this, &Widget::use_qthread);
layout->addWidget(button);
layout->addWidget(button2);
layout->addWidget(button3);
}
~Widget()
{
m_thread.quit();
m_thread.wait();
}
public slots:
void slow_function()
{
qDebug() << "Starting";
auto gui_thread = QApplication::instance()->thread();
auto this_thread = thread();
qDebug() << "Thread affinity is" << (gui_thread == this_thread ? "gui_thread" : "non_gui_thread");
Sleep(5000);
qDebug() << "Finished";
}
void use_concurrent()
{
QtConcurrent::run(this, &Widget::slow_function);
}
void use_qthread()
{
m_thread.start();
}
private:
Thread m_thread;
};
#endif // WIDGET_H
and the main.cpp file:
#include "widget.h"
#include <QApplication>
void Thread::run()
{
m_widget->slow_function();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
You should not do any UI things in nonmain threads. The UI means
widget interaction
model
Run the slow_function on the GUI thread
That's not allowed, nothing should block the GUI thread.
Is such an approach safe if I can guarantee that slow_function is thread-safe?
It's okay to run slow function in a different thread. But...
what happens when the user closes the application, but the function still executes?
I have done this, but I prefer to encapsulate it in a separate class.
Create a subclass of QThread which holds a pointer to my widget.
You should only subclass QThread if the subclass is a thread. Like, subclassing an animal class will give you an animal, not a chair with four legs.
Is moveToThread meant to be useful only when we are interested in calling slots via signals sent from another thread?
moveToThread changes the affinity of the object. So, slots are always executed in the correct thread, when you call invokeMethod, the method is executed in the correct thread. When an event is delivered, the event handler is always called in the appropriate thread.
The whole purpose of QtConcurrent::run() is to make running heavy workloads in a thread easier. If your use-case allows for it, great!
Pair it with a QFutureWatcher to retrieve the result after your slow_function is finished.
Using QThread is another option, but it makes more sense when you have a long-lived object. Instead of subclassing, I find it easier to use the worker model: create a worker class with signals/slots, call moveToThread on it with a vanilla QThread object, connect/subscribe, start the thread
QWidget is a way to create GUI functionality in Qt. While it may be possible to use such objects in non-gui threads, it's best you separate your compute workloads from GUI (don't put your slow_function into widget classes).

How to close GLFW window when we close QT application

When i close the QT GUI i want the GLFW window to be closed accordingly.
For glfw we can query if window is closed or not by glfwWindowShouldClose function.
Do we have anything like that in QT where we can keep Querying if the application GUI is closed.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TreeModel model;
QTApplication w(model);
int return_code = 0;
QTApplication.show();
glfwInit();
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);
glfwWindowHint(GLFW_SAMPLES, 4);
window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Renderer", nullptr, nullptr); // Create the render window
if (window == NULL)
{
QMessageBox msgBox;
msgBox.setText("Not able to create GL Window");
msgBox.exec();
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
GLenum GlewInitResult;
glewExperimental = GL_TRUE;
GlewInitResult = glewInit();
if (GLEW_OK != GlewInitResult) // Check if glew is initialized properly
{
glfwTerminate();
}
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
while (!glfwWindowShouldClose(window))
{
// Do Rendering here
}
return_code = a.exec();
glfwTerminate();
return return_code;
}
}
There is a signal for that, emitted by the application:
QGuiApplication::lastWindowClosed()
It looks like what you want to do is intermangle the Qt event loop with your own loop. Here is a hint about how to do that from the Qt docs:
To make your application perform idle processing (i.e. executing a special function whenever there are no pending events), use a QTimer with 0 timeout. More sophisticated idle processing schemes can be achieved using processEvents().
So this is how I would approach this:
Setup a QTimer that connects to a method that does rendering (if window should not close) and restarts the timer
Call a.exec()
In the method called by the timer, if window should close, call a.quit()

Taking screenshot from inside fullscreen Qt Quick Application

This was my original question:
I just want to take a screenshot (using the Print key) of my fullscreen QtQuick 2 application. But all I get is a black or sometimes white screenshot. When the application is not started in fullscreen it works.
SOLUTION
I thought I post a really nice solution here,
even though it does not solve the original problem of taking the screenshot with an external tool.
Starting with the suggestion from the accepted answer I did the following:
First I added a signal to my QML main class (in main.qml)
signal takeScreenShot()
This signal is emmited by pressing a certain button.
Then I wrote a C++ / QT class autside the QML code to handle this signal:
class QMLSupplement : public QObject
{
Q_OBJECT
public:
QQuickView* view;
public:
QMLSupplement(QObject* parent = 0);
public slots:
void takeScreenShot();
};
The reference to QQuickView is used to take the screenshot.
void QMLSupplement::takeScreenShot()
{
QString file;
file = QDateTime::currentDateTime().toString("yyyy-MM-dd_hhmmss");
file += ".png";
qDebug() << "taking screenshot, saving here:" << file;
view->grabWindow().save(file);
}
Finally I connect the signal and the slot in main.cpp:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
view.setSource(QUrl::fromLocalFile("./qml/main.qml"));
view.setResizeMode(QQuickView::SizeRootObjectToView);
QObject* rootObject = (QObject*) view.rootObject();
QMLSupplement supplement;
supplement.view = &view;
QObject::connect(rootObject, SIGNAL(takeScreenShot()),
&supplement, SLOT(takeScreenShot()));
view.show();
// view.showFullScreen();
return app.exec();
}
That's a limitation of the platform where you're running. If you care about this working, you need to implement the functionality yourself. Qt provides you with enough to get the contents of the Qt Quick 2 window and post it to the clipboard as an image.
In your print key handler, if you detect that the window is full-screen, you need to pass the QQuickWindow instance to a helper function:
void grabAndCopy(QQuickWindow * window) {
QApplication::clipboard()->setImage(window->grabWindow());
}
...
if (window->windowState() == Qt::WindowFullScreen) grabAndCopy(window);

How do I quit a qt console application? Are there criterions for the QApplication::quit() slot to work?

I have a weird qt problem: My application doesn't quit in some configurations.
The idea is to have a program, which can be started as a program with a GUI (through myWindow) or as a pure console application (controlled via myConsole, which runs its own loop in its thread to record keyboard inputs). Either way quitting is done by calling the myObject slot quitMyObject, which in turn cleans up some objects and emits the signal signalQuitapplication, which is connected to the QApplication (app) quit slot.
Unfortunately the application only quits when the the window is enabled and the quit command is entered in the console (although the slotQuitMyObject of myObject is always called). So I wonder what criterions Qt has to actually quit the main event loop and exit the program.
The code looks like this:
int main(int argc, char *argv[])
{
bool enableWindow = false;
QApplication app(argc, argv, enableWindow);
MyUiAbstract* myConsole = new ConsoleUi(); // ConsoleUi inherits from MyUiAbstract, which inherits from QThread
MyWindow* myWindow = NULL; // MyWindow inherits from QMainWindow
if(enableWindow)
{
myWindow = new MyWindow();
myWindow->show();
}
MyObject* myObject = new MyObject(myConsole, myWindow, ...);
QObject::connect(myObject, SIGNAL(signalQuitQApplication()), &app, SLOT(quit()), Qt::QueuedConnection);
QObject::connect(myConsole, SIGNAL(signalQuitMyObject()), myObject, SLOT(slotQuitMyObject()), Qt::QueuedConnection);
QObject::connect(myWindow, SIGNAL(signalQuitMyObject()), myObject, SLOT(slotQuitMyObject()), Qt::QueuedConnection);
QObject::connect(myWindow, SIGNAL(signalQuitConsoleUI()), myConsole, SLOT(slotQuitMyUi()), Qt::QueuedConnection);
return app.exec();
}
Try to use that code in your MyConsole class:
#include <QApplication>
...
qApp->quit();
Also you need to close all the event loops & threads before quit.

How to exit my Qt console program through a function with parent?

/EDIT: solved, see my comment in the 1st answer!/
I am currently building an application which only has a tray icon displayed, so it doesn't have any windows.
Well, in the tray icon I've included a QAction so as to close the application. The thing is, that I get seg fault when I call exit(0); from that function. This is some example code:
//I have a reason for setting it to be a QTimer, please don't even comment on this
class Boot_Timer : public QTimer {
Q_OBJECT
public:
explicit Boot_Timer(QObject *parent = 0) : QTimer(parent) {
}
public Q_SLOTS:
void set_up_command_line_tray(){
//Setting up the tray Icon.
QSystemTrayIcon *trayIcon_cmd = new QSystemTrayIcon(this);
trayIcon_cmd->setIcon(QIcon(":/icons/Pictures/myapp.png"));
trayIcon_cmd->setToolTip("My tray tooltipp");
QMenu *changer_menu = new QMenu;
QAction *Quit_action = new QAction(tr("&Quit"), this);
Quit_action->setIconVisibleInMenu(true);;
connect(Quit_action, SIGNAL(triggered()), this, SLOT(close_application()));
changer_menu->addAction(Quit_action);
trayIcon_cmd->setContextMenu(changer_menu);
trayIcon_cmd->show();
}
void close_application(){
//HERE I GET SEG FAULT
exit(0);
}
};
Boot_Timer boottimer;
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//making some checks (code omitted)
...
boottimer.set_up_command_line_tray()
return app.exec();
}
So, the tray icon is shown normally and perfectly, but when I choose to Quit the application using the menu I've added to the tray icon, I get a seg fault. I guess that I cannot quit the application using exit(int state) outside main() function and its functions that don't have a parent...
What is the correct way to quit my application, then?
Thanks in advance for any answers!
Try to call
qApp->quit(0);
instead of
exit(0);
Remember to #include <QApplication>.
Thanks, that didn't solve it. For some reason, the thing that solved it was to do the following: QSystemTrayIcon *trayIcon_cmd = new QSystemTrayIcon(0); instead of QSystemTrayIcon *trayIcon_cmd = new QSystemTrayIcon(this)

Resources