I'm trying to build an application that can spawn a window on a separate thread. Let me explain a simple version. My main creates an object that has a window, let's call this object a menu. From the menu you can select what to do, for example open up an image to a new window. This whole object, or the object's "game loop" needs to be on a separate thread so that I can still keep interacting with the menu. I also need to interact with the image viewer.
My question is, what is the proper way of doing this?
I haven't really used threads a lot before. But from what I understand I need to detach the thread to create a daemon thread.
I tried to play around with the thread to create this but I kept getting these errors:
Failed to activate the window's context
Failed to activate OpenGL context: The requested resource is in use.
I'm not certain what causes this, all objects, like my windows are different instances. The application will still run fine even with these errors.
My application is quite big so here's an extremely simplified version of the code I've tried.
int main()
{
Menu menu; // this spawns a window
menu.run(); // let's say for simplicity this doesn't do anything else other than
// create a new window (the image viewer)
}
...
void caller(Image_view *img_view)
{
img_view->run();
}
void Menu::run()
{
Image_view *img_view = new Image_view(); // This creates the window
this->thread = new std::thread(caller, img_view);
this->thread->detach();
while (1); // This is here to keep the application running,
// in a real application this method would look different.
// This whole thread call would be in an event handler instead,
// but for this example I tried to make it as simple as possible
}
...
void Image_view::run()
{
while (running)
{
update(); // Event handler and whatever
render(); // Renders the image and whatever
}
this->window->close();
}
I mostly want to know if I'm using the thread correctly or not in an application like this. Also if you have any insight as to what the error message means, explaining it would be greatly appreciated. I should also mention that I'm using SFML for rendering and creating the window instance.
The tutorials I found about the threads are always something extremely simple which doesn't involve any window or anything that could for example cause that error message. So I figured someone smarter here might know the proper use of the thread in my case.
Thanks in advance!
Related
I implemented a widget/dialog, which is just a gif animation inside (CircularProgressDlg). Our application consists of Login dialog part and MainWindow part. Between login is accepted and MainWindow is fully loaded there are 5 to 10 seconds of huge init() function.
What I need is to launch my CircularProgressDlg when login is accepted, and I need it to animate it's gif non-blocking. I can't use "update" or "processEvents" inside this huge "init" function, cause it is very complex inside and it won't be smooth enough.
It looks very needful that this type of dialogs, detached from main event loop, exist! So how to achieve that?
I understand that I can create a detached Process with this CircularProgressDlg, but let's leave this way to the end.
What are other ways?
We use QtWidgets, but maybe I can implement small QML dialog for such cases if, of course, QML will solve my problem. Will it?
Which options do I have?
Use the Qt Concurrent library, specifically its QConcurrent::run method to spawn a thread for your expensive task. You get back a QFuture that you can watch with QFutureWatcher.
The QT documentation has an asynchronous image scaling example.
I'm adding selected parts below.
Creating a QFutureWatcher to be signaled when a future is done:
imageScaling = new QFutureWatcher<QImage>(this);
connect(imageScaling, &QFutureWatcher<QImage>::resultReadyAt, this, &Images::showImage);
connect(imageScaling, &QFutureWatcher<QImage>::finished, this, &Images::finished);
Constructing the future:
std::function<QImage(const QString&)> scale = [imageSize](const QString &imageFileName) {
QImage image(imageFileName);
return image.scaled(QSize(imageSize, imageSize), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
};
QFuture<QImage> fut = QConcurrent::run(scale, "test.png");
Attaching the future to the futurewatcher:
imageScaling->setFuture(fut);
Thanks to everyone. We decided to make things right and refactor loading and updating our widgets in a non-blocking way. With moving all possible logic in separate processes.
I have a child class of QWidget, and I'm trying to fix a bug where the the window that it is in cannot be programmatically hidden/closed using the QWidget::hide() or close() methods.
Here are some of the things that I tried:
if(widget->isFullScreen())
{
widget->showNormal(); //Makes the window normal-sized before closing it
widget->hide();
}
Here's another way I have tried:
if(widget->isFullScreen())
{
widget->setWindowState(Qt::WindowMinimized);
widget->hide();
}
I also tried setting up a slot/signal system:
if(netcam->isFullScreen())
{
connect(this, SIGNAL(fullScreenExited()),
this, SLOT(onFullScreenExited()));
widget->showNormal();
this->fullScreenExited(); //just hides the widget (or closes it)
}
else
{
widget->hide();
}
The result every time is that the window freezes and must be closed by hand. My suspicion is that the first showNormal() is happening asynchronously, and the second close()/hide() never successfully executes.
I also tried this, in hopes that it would complete showNormal() before going on to hide()/close():
if(widget->isFullScreen())
{
widget->showNormal();
QApplication::processEvents();
widget->hide();
}
THE MAIN QUESTION:
Does anybody have any suggestions for how to deal with closing a full screen QWidget from Qt code?
Question that could also help:
Is there a way to ensure that things run synchronously?
Thanks!
EDIT:
The only way that I got this to work was to call showNormal() further up in the process, which prevents overlap in the execution of showNormal() and hide(). I'll try to remember to come back later and give a good, basic example with a regular QWidget.
I should also add that the window is put into the fullscreen state with the + (full screen) button, which is located at the top of each window in OS X.
This is a known bug.
The workarounds of showNormal() or showMinimized() are not working because the window state change is not synchronous. And a single processEvent() is not enough. You need to wait for the corresponding QEvent::WindowStateChange event to know when the window has fully moved out of fullscreen and can receive a new window state change.
I have learned about setting up separate rendering thread for Qt QGLWidget here ,here and here .
I also managed to get a kind of "working" setup: clearing color in the viewport.Seems to be ok.But I am getting the following warning:
QOpenGLContext::swapBuffers() called with non-exposed window, behavior
is undefined
I first create a widget that inherits from QGLWidget.Where I also setup OpenGL Format:
In the Widget constructor:
QGLFormat format;
format.setProfile(QGLFormat::CompatibilityProfile);
format.setVersion(4,3);
format.setDoubleBuffer(true);
format.setSwapInterval(1);
setFormat(format);
setAutoBufferSwap(false);
Then I init the rendering thread in the same Widget:
void GLThreadedWidget::initRenderThread(void){
doneCurrent();
context()->moveToThread(&m_renderThread);
m_renderThread.start();
}
and from that point the whole rendering is done inside that thread:
RenderThread constructor:
RenderThread::RenderThread(GLThreadedWidget *parent)
:QThread(),glWidget(parent)
{
doRendering = true;
}
RenderThread run() method:
void RenderThread::run(){
glWidget->makeCurrent();
GLenum err = glewInit();
if (GLEW_OK != err) {
printf("GLEW error: %s\n", glewGetErrorString(err));
} else {
printf("Glew loaded; using version %s\n", glewGetString(GLEW_VERSION));
}
glInit();
while (doRendering){
glWidget->makeCurrent();
glClear(GL_COLOR_BUFFER_BIT );
paintGL(); // render actual frame
glWidget->swapBuffers();
glWidget->doneCurrent();
msleep(16);
}
}
Anyone can point out where is the issue?And if that message can be discarded? Also a straightforward and concise explanation on render thread setup in Qt would be extremely helpful.Using Qt 5.2 (Desktop OpenGL build)
With what you've shown, it looks like that message handler warning you were getting was because you started triggering buffer swaps "too soon" in the window setup sequence, either directly through QGLContext::/QOpenGLContext::swapBuffers() or indirectly through a number of possible ways, none of which are really detectable outside of manual debugging. What I mean by too soon is before the widget's parent window was marked "exposed" (before it was being displayed by the windowing system).
As far as whether the message can be discarded, it can...but it's not safe to do, as in it's possible to get undefined behavior for the 1st few frames or so where you do it and the window's not ready (especially if you're immediately resizing to different extents at startup than your .ui file specifies). Qt documentation says that before your window's exposed, Qt has to basically tell OpenGL to paint according to what are effectively non-trustworthy extents. I'm not sure that's all that can happen though personally.
With the code you showed, there's an easy fix--avoid even starting your render logic until your window says it's exposed. Detecting exposure using QGLWidget isn't obvious though. Here's an example roughly like what I use, assuming your subclass from QGLWidget was something like 'OGLRocksWidget', it was a child of a central widget, and that central widget was a child of your implementation of QMainWindow (so that your widget would have to call parentWidget()->parentWidget() to get at its QMainWindow):
OGLRocksWidget::paintGL()
{
QMainWindow *window_ptr =
dynamic_cast<QMainWindow *>(parentWidget() ? parentWidget()->parentWidget() : 0);
QWindow *qwindow_ptr = (window_ptr ? window_ptr->windowHandle() : 0);
if (qwindow_ptr && qwindow_ptr->isExposed())
{
// don't start rendering until you can get in here, just return...
// probably even better to make sure QGLWidget::isVisible() too
}
}
Of course you don't have to do this in your implementation of QGLWidget::paintGL(), but in your particular setup you're better off not even starting your render thread until your window tells you it's exposed.
It looks like you have might have slightly bigger problems than that though. You weren't hooking the right GL activity into the right places in your code vs QGLWidget's intent. I feel for the position you were in because the documentation on this is a little spotty and scattered. For that part, QGLWidget's detailed description down where it says "Here is a rough outline of how a QGLWidget subclass might look" is a good place to start getting the idea. You'll want to override any of the key virtuals in there that you have related code for and move them into those calls.
So for example, your widget's constructor is doing setup work that is probably safer to put in an initializeGL() override, since QGLWidget's intent is to signal you when it's safely time to do that through that call. What I mean by safer whenever I say that here is that you won't get seemingly random debug exceptions (that in release builds can silently wreak havok on your runtime stability).
Side advice: install Qt source, point your debugger at it, and watch your code run, including into Qt. Your setFormat() call, last time I watched it, actually deletes the current underlying QOpenGLContext. That's probably good to know because you'll want to create a new one soon after or at least test out your options.
The risk of instability is why I'm trying to put together at least some kind of answer here a year later. I just learned this through a lot (too much) debugging. I love what the Qt team's done with it, but Qt will be much better off when they finish migrating everything over to QOpenGL* calls (or wherever they see a final proper place for their OpenGL support including permanent considerations for it and windowing support together).
A QOpenglWidget comes with its own context. If you want a background thread to do the rendering, you have to pass a shared context to the thread and do a few steps correct.
Details in: https://stackoverflow.com/a/50368372/3082081
I'm kind of stuck on something; regarding spawning multiple forms in OOP.
The message loop most of the time is (wxWidget's case) window->show();
bool MyApp::OnInit()
{
MainWindow *oWindow = new MainWindow(wxT("My Window"));
oWindow->Show(true);
return true;
}
Others have oWindow->run(), but anyway my question is:
I've created a second thread with the exact same structure of the function above and called the message loop method. The problem is that the window appears and dissapears suddenly which doesn't make sense to me. If however I call:
MainWindow *oWindow = new MainWindow(wxT("My Window"));
oWindow->Show(true);
MainWindow *oWindow2 = new MainWindow(wxT("My Window"));
oWindow2->Show(true);
It will work, but I don't want that as I will need to keep track of the windows I create and have them on separate threads. What can I do?
You cannot run wxWidgets windows in anything other than the main thread.
"GUI calls, such as those to a wxWindow or wxBitmap are explicitly not safe at all in secondary threads and could end your application prematurely. This is due to several reasons, including the underlying native API and the fact that wxThread does not run a GUI event loop similar to other APIs as MFC."
http://docs.wxwidgets.org/2.8/wx_wxthread.html
BTW, I cannot imagine any situation where what you want to do is a good idea. There is never any need to tun windows in more than one thread.
A windows program is event-driven. You can as have as many top level windows as you want, but there should be just one event queue so that the events on two windows do not end up in contention for the same resource. This is why wxWidgets prevents you trying to create two threads both handling windows events.
Because I´m writting a "generic" application behaving completely different when facing other configurations, I´m forced to show gtk windows even if I dont yet know them at startup. There might also be the requirement that multiple windows need to be visisble (not modal dialogs but standalone windows) at the same time. But, it would be great if one can simply start one gtk event loop at startup.
Is is somehow possible to add windows to that loop after it has been started?
While I found the Gtk::Application class which seems to support exactly the indented behaviour I´m restricted to use the Gtk::Main class.
There's only a single Gtk::Main object allowed. Widgets should be created in the same thread the main event loop is being run in. To work around this limitation you need to develop a way to pass your window creation commands to the gtk thread.
The simplest way is to use Glib::Dispatcher
struct WindowBuilder {
/**/
Glib::Dispatcher* signal_create;
void create_window() {
//From main thread...
signal_create->emit();
}
}
void create_mainWnd() {
new Ui::MainWnd();
}
//From Gtk thread...
builder->signal_create->connect(sigc::ptr_fun(create_mainWnd));
Gtk::Main::run();
Glib::Dispatcher doesn't take any arguments with it, so next step is to figure out how to pass arguments around between threads.
For different types of windows you can just use different disptachers.
boost::asio::io_service can help you pass messages around.
while(!exit) {
io_service.reset();
io_service.poll();
while(Gtk::Main::events_pending())
Gtk::Main::iteration();
Sleep(0);
}