Need to be able to draw on the screen from a thread other than main. Cannot use UIkit since it's not thread safe. Would Quartz 2D work in a thread? If so, how do I get to the screen to place the graphics? Clearly I'm a real newbie with graphics and any help would be appreciated.
Yes Quartz would work in thread. However you cannot create a context in other thread. You have to pass the context from main thread. I believe create a CGContextRef in -drawRect method and pass it to thread. Take precaution that context dont go out of scope or just retain its reference.
How to get a screen to put graphics.. well as I said you need some place to create a context, may be some derived class of NSVIew. Then in -drawRect method make a context and pass it to thread.
CGContextRef contextref = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
Cheers!
Related
I am trying to do some opengl 1.0 animation in a CWnd window with 60 fps. I create a sub class of CWnd:
class COpenGLControl : public CWnd
{
...
}
I found if I use the build-in timer "setTimer()" and set it to fire on every 1000/60 ms, all the opengl commands were able to render correctly. However, If I implement my own timer using a separate thread, nothing is drawn. All I got was a black screen.
Is there a way to issue opengl commands from a different thread?
Even if you are not intending to issue GL calls from multiple threads, you have to take OpenGL's rules for threading into account: An OpenGL context can only be used by at most one thread at a time. (And, per thread, there can be at most one active GL context at any time). That does not mean that you cannot use the same context in multiple threads, or create it in one and use it in another, you just have to explicitely "hand over" the context from one thread to another.
I don't know if you use some further librariy for GL context handling, so I'm assuming you are using the native API of your OS - in this case wgl. The relevant function is wglMakeCurrent(). So, to hand over a context which is "current" in thread A to thread B, thread A must first call wglMakeCurrent(NULL,NULL) before thread B can get the context via wglMakeCurrent(someDC, myGLCtx). You can of course switch around a GL context as many times you like, but that will introduce a huge synchronization overhead and should be avoided.
From your comments:
Would work If I create the context also in the timer thread?
Yes, it would.
Just a side note: Creating is not the issue here at all, since creating a GL context does not automatically make it "current" to a thread - so you can just create it in thread A and afterwards make it current directly to thread B.
I've got a specific target: to draw a road-net. So i have a number of dots (x,y) and I'd like to connect them (using drawLine function). Because of their amount (about 2-3 millions) I need to do in in another thread, so there a problem how should i do it ? I have a special area for drawing - QLabel. I've tried to do it through QPixmap in main thread and everything is fine, but when I try to do it through signal/slot in another thread no image appear :(
Actually, when I transform my coordinates into GUI-coordinates they become fractional so I don't know how to paint them, because drawLine functions has integer argument: (int x1, int y1, int x2, int y2).
This is how i create another thread (I need to run only one function, so it is the best way i think)
QtConcurrent::run(this,&MainWindow::parseXML)
Hope you will help me, because I will become mad %)
P.S I've read that QPixmap is not supported in multi-threading drawing. So now i have no idea how to do that.
QPainter can be used in a thread to paint onto QImage, QPrinter, and QPicture paint devices. Painting onto QPixmaps and QWidgets is not supported. On Mac OS X the automatic progress dialog will not be displayed if you are printing from outside the GUI thread.
If you need to do your painting in a thread other than the Qt GUI thread, do this:
In your non-GUI thread, create a QImage object
Use a QPainter to paint into the QImage object
Use QApplication::postEvent or a queued signal/slot connection to pass the QImage object over to the main thread in a thread-safe manner
The main thread can now convert the QImage object into a QPixmap (this will be relatively quick to do) and then display it as usual.
You are apparently looking for a QGraphicsView (or preferably QQuickView if you care about performance and are working with Qt5). That's the solution which Qt offers for exactly this purpose.
To your question -- there is no way in Qt to do the painting in a separate thread; any widget class cannot be touched from another thread. The proposed invokeMethod call is actually an asynchronous callback which gets queued for execution in the main thread. You could generate a QImage, pass it to the GUI thread and let the GUI use it, but I'd seriously suggest working with the scene graph (the QGraphicsView) because it was designed and optimized for precisely this purpose.
Though it's really bad practice - to update GUI thread from within worker thread and you should really do it via signal-slot (with connection type -queued) , you still can update GUI via QMetaObject::invokeMethod()
You have to run every function in a worker thread, that updates GUI through invokeMethod(). For example - in your main class, add function like void MainWindow::drawLine(int x1, int y1, int x2, int y2) which will draw line on your QImage. And within your thread you can call that function like this:
QMetaObject::invokeMethod(this,"drawLine", Q_ARG(int,x1), Q_ARG(int,y1), Q_ARG(int,x2), Q_ARG(int,y2));
The simplest approach is to concurrently distribute the drawing across several images, then composite the images (also concurrently), and then finally submit them for painting on the gui.
This can be done using QtConcurrent::map on the sequence of images. The map functor draws into an image that's specific to the current thread - e.g. via QThreadStorage. The reference to that image can also be stored, upon allocation, in a list within the functor. The functor of course has to outlive the call to QtConcurrent::map. Once map returns, the images from the list within the functor can be asynchronously combined pair-wise, until only one image remains. That image is then submitted to the display widget.
If the full-size image compositing is to be avoided, then a similar approach will work, but the lines have to grouped into spatial groups, i.e. those intersecting some rectangles that cover the area to be drawn. To fully utilized all cores, you'd want say 2-3x as many rectangular areas as QThread::idealThreadCount(). Then treat the painting of each of those groups onto its sub-image to be a concurrent task, to be submitted to QtConcurrent::run. When all tasks are done, the images get submitted to the display widget, which paints them on its backing store, in sequence.
The painting of the images on the backing store can also be multi-threaded, see this answer for a complete example. Generally speaking, the images need to have a width the multiple of (CPU cacheline size/4 since we use 32-bit pixels). The painting of those images on the backing store is fully parallelizable.
I'm trying to render webpages in the background and I ran into the following trouble
2012-05-11 12:39:02.086 [77207:1c03] An uncaught exception was raised
2012-05-11 12:39:02.087 [77207:1c03] objc_object* -[WebView initWithFrame:frameName:groupName:](WebView*, objc_selector*, CGRect, NSString*, NSString*) was called from a secondary thread
I understand that webkit classes are not thread-safe, but do they also have to be always used from the main thread? Alternatively, can I create a dummy web view just to initialize webkit and then later use webkit classes from background threads?
WebView is a subclass of NSView. From the Threading Programming Guide:
The NSView class is generally thread-safe, with a few exceptions. You
should create, destroy, resize, move, and perform other operations on
NSView objects only from the main thread of an application. Drawing
from secondary threads is thread-safe as long as you bracket drawing
calls with calls to lockFocusIfCanDraw and unlockFocus.
(Emphasis added.)
I'm less clear on how the AppKit WebView works, but my understanding is that in UIKit there is really only one UIWebView that just gets reused all over for performance reasons, something akin to the field editor in AppKit. If my understanding is correct, and WebView behaves similarly, I could see you having even bigger problems with background threading.
I access the Qt GUI's QLabel's QPixmap in another thread since I will finally use this to display mjpeg stream in QLabel, and I decided to use QLabel since its the easiest way
It should look like 'Live' and not block the UI thus using another (non-gui) thread.
nothing shows up in the QLabel. only the exception QPixmap: It is not safe to use pixmaps outside the GUI thread
any better or correct way to do this ?
here is my PyQt code of another thread: self.theQlabel.setPixmap(QtGui.QPixmap.fromImage(myQimg)
Instead of directly setting the pixmap, make the external thread emit an updatePixmap signal. Then in the GUI thread, listen to the signal and update the pixamp at that time. Something like that should work (in C++):
// In the GUI thread:
class YourWidget: QObject {
public:
YourWidget();
public slots:
void updatePixmap(const QPixmap& pixmap);
}
YourWidget::YourWidget() {
// Connect to the signal here:
QObject::connect(otherThread, SIGNAL(updatePixmap(const QPixmap&)), this, SLOT(updatePixmap(const QPixmap&)));
}
YourWidget::void updatePixmap(const QPixmap& pixmap) {
// Update the pixmap here in a thread-safe way
}
// In the external thread:
// Emit the signal. The GUI thread will receive it and can then update the pixmap
emit updatePixmap(thePixmap);
I think that it might be dangerous to even create a QPixmap in anything other than the GUI thread. You might want to consider passing a QImage and converting it to a QPixmap in the main thread. I can't find a direct reference to support this assertion, but
http://developer.qt.nokia.com/doc/qt-4.8/thread-basics.html
hints that
All widgets and several related classes, for example QPixmap, don't work in secondary threads.
The email thread at
http://lists.trolltech.com/qt-interest/2008-11/msg00534.html
also seems to agree with me.
Create RAW DirectX/OpenGL OS context into this QLabel's winId() and do whatever you want. I think, this is the best way for high performance HD video, and the ONLY way :) sometimes you just HAVE to use RAW things to achieve the maximum performance and touch the iron in your computer :)
According to http://doc.qt.io/qt-5/qpointer.html, QPointer is very useful. But I found it could be inefficient in the following context:
If I want to show label for three times or do something else, I have to use
if(label) label->show1();
if(label) label->show2();
if(label) label->show3();
instead of
if(label) { label->show1();label->show2();label->show3(); }
just because label might be destroyed in another thread after label->show1(); or label->show2();.
Is there a beautiful way other than three ifs to get the same functionality?
Another question is, when label is destroyed after if(label), is if(label) label->show1(); still wrong?
I don't have experience in multi-threaded programs. Any help is appreciated. ;)
I think the only safe way to do it is to make sure you only access your QWidgets from within the main/GUI thread (that is, the thread that is running Qt's event loop, inside QApplication::exec()).
If you have code that is running within a different thread, and that code wants the QLabels to be shown/hidden/whatever, then that code needs to create a QEvent object (or a subclass thereof) and call qApp->postEvent() to send that object to the main thread. Then when the Qt event loop picks up and handles that QEvent in the main thread, that is the point at which your code can safely do things to the QLabels.
Alternatively (and perhaps more simply), your thread's code could emit a cross-thread signal (as described here) and let Qt handle the event-posting internally. That might be better for your purpose.
Neither of your approaches is thread-safe. It's possible that your first thread will execute the if statement, then the other thread will delete your label, and then you will be inside of your if statement and crash.
Qt provides a number of thread synchronization constructs, you'll probably want to start with QMutex and learn more about thread-safety before you continue working on this program.
Using a mutex would make your function would look something like this:
mutex.lock();
label1->show();
label2->show();
label3->show();
mutex.unlock()
As long as your other thread is using locking that same mutex object then it will prevented from deleting your labels while you're showing them.