I want to call the rendering routine out without calling PaintGL, the reason is that I'm trying to render a multipass effect using the modern routines of Qt OpenGL implemementation so the pseudo code should be something like:
for i=0 i<npasses i++
glwidget->renderlayer i
glwidget->repaint //this calls PaintGL
The problem is that if I call renderlayer out of the PaintGL function everything goes mad and it gets drawn over my entire app instead on my glwidget (that inherits from QOpenGLWidget) in the other hand renderlayer funcion is ok since being called only from inside PaintGL it works like it was expected.
Any tip on this?
Thank you in advance
You can create a QOffscreenSurface like this:
QOpenGLWidget* widget = ...;
QOpenGLContext* ctx = widget->context();
QOffscreenSurface surface;
surface.setFormat(ctx->format());
surface.setScreen(ctx->screen());
surface.create();
Then re-target your GL context to that offscreen surface, do your FBO rendering, and finally re-target the GL context back.
ctx->makeCurrent(&surface);
// Bind FBO, do the rendering
widget->makeCurrent();
Related
I am new in QT and my problem is to refresh the page in a loop to make a move on QWidget.
In detail, I have too many points (It is the path which will be followed by an ellipse and they will be drawn as line) and I have an ellipse which will move on the screen according to given two points. During its move, the path is changed. So lines will be drawn again according to new path and the ellipse should follow the new path. What I did as follows:
void MainWindow::paint(...){
painter.drawEllipse(circle) //circle is QRectF
//Also I need to draw lines according to pathPlanned
}
bool MainWindow::replan(){
//it calculates the planned path and if the ellipse does not reached the destination it can change the planned path here
}
void MainWindow::execute(){
while(replan()){
for (it = plannedPath->begin(); it != plannedPath->end(); it++){
//Lines should be redraw according to new pathPlanned
}
circle(...) // new position of ellipse is changed here
// I tried to put QThread::msleep(10) but I learned that it blocks GUI and then deleted it.
}
}
My problem is that loop is working so fast (as usual) and it can not refresh the page until it finishes everything. Then Immediately ellipse is drawn on the destination. I can not see the moves of ellipse.
How can I fix that?
Instead of using QThread::msleep(10), use following
QEventLoop loop;
QTimer::singleShot(100, &loop, SLOT(quit()));
loop.exec();
This will process events after each redraw of ellipse so UI will get updated
You need to use Qt animation framework for that. There are a lot of samples in official documentation. In this case you will not block main event loop and your animations will be smooth.
If you use custom drawing, don't forget to call QWidget::repaint() or QWidget::update() to refresh widget content.
Don't use long time loops in main thread. Use timers + slots.
I've got a Qt app that needs to call an expensive non-Qt function (e.g. to unzip a ~200MB zip file), and since I'm calling that function from the main/GUI thread, the Qt GUI freezes up until the operation completes (i.e. sometimes for 5-10 seconds).
I know that one way to avoid that problem would be to call the expensive function from a separate thread, but since there isn't much the user can do until the unzip completes anyway, that seems like overkill. I can't add processEvents() calls into the expensive function itself, since that function is part of a non-Qt-aware codebase and I don't want to add a Qt dependency to it.
The only thing I want to change, then, is to have a little "Please wait" type message appear during the time that the GUI is blocked, so that the user doesn't think that his mouse click was ignored.
I currently do that like this:
BusySplashWidget * splash = new BusySplashWidget("Please wait…", this);
splash->show();
qApp->processEvents(); // make sure that the splash is actually visible at this point?
ReadGiantZipFile(); // this can take a long time to return
delete splash;
This works 95% of the time, but occasionally the splash widget doesn't appear, or it appears only as a grey rectangle and the "Please wait" text is not visible.
My question is, is there some other call besides qApp->processEvents() that I should also do to guarantee that the splash widget becomes fully visible before the lengthy operation commences? (I suppose I could call qApp->processEvents() over and over again for 100mS, or something, to convince Qt that I'm really serious about this, but I'd like to avoid voodoo-based programming if possible ;))
In case it matters, here is how I implemented my BusySplashWidget constructor:
BusySplashWidget :: BusySplashWidget(const QString & t, QWidget * parent) : QSplashScreen(parent)
{
const int margin = 5;
QFontMetrics fm = fontMetrics();
QRect r(0,0,margin+fm.width(t)+margin, margin+fm.ascent()+fm.descent()+1+margin);
QPixmap pm(r.width(), r.height());
pm.fill(white);
// these braces ensure that ~QPainter() executes before setPixmap()
{
QPainter p(&pm);
p.setPen(black);
p.drawText(r, Qt::AlignCenter, t);
p.drawRect(QRect(0,0,r.width()-1,r.height()-1));
}
setPixmap(pm);
}
Moving to another thread is the correct way to go but for simple operations, there's a much less complicated way to accomplish this without the pain of managing threads.
BusySplashWidget splash("Please wait…", this);
QFutureWatcher<void> watcher;
connect(&watcher, SIGNAL(finished()), &splash, SLOT(quit()));
QFuture<void> future = QtConcurrent::run(ReadGiantZipFile);
watcher.setFuture(future);
splash.exec(); // use exec() instead of show() to open the dialog modally
See the documentation about the QtConcurrent framework for more information.
I usually put my drawing in WM_PAINT to draw on screen but if you need to figure out this at runtime would you still use GDI drawing APIs?
Example-
//In WndProc
case WM_PAINT:
{
hdc = GetWindowDC (hwnd) ;
//draw here using hdc
ReleaseDC (hwnd, hdc) ;
}
Instead of putting the drawing in WM_PAINT, can you draw using function and still get the functionality of WM_PAINT to redraw?
WM_PAINT is asynchronous, meaning its called when the OS decides it's time to repaint the window. You can also draw synchronously by calling GetDC()/ReleaseDC() outside of a WM_PAINT message handler.
http://msdn.microsoft.com/en-us/library/dd145126(v=vs.85).aspx
Also, when handling WM_PAINT messages, you should use BeginPaint()/EndPaint() and return 0. I've seen some strange side-effects when this does not happen.
I am working on QT GUI project. In this application I have a QWidget as main window. I make the cursor from data coming from some source. When I set the cursor of widget. It gives me the following error.
QPixmap: It is not safe to use pixmaps outside the GUI thread
My code is as follows
void ImageWindow::setMouseCursor(unsigned char* data,unsigned char* maskbits,unsigned int length,int xHotSpot, int yHotSpot)
{
QBitmap bitmapData;
QBitmap bitmapMaskData;
bitmapData.loadFromData(data,length);
bitmapMaskData.loadFromData(maskbits,length);
this->setCursor(QCursor(bitmapData,bitmapMaskData,xHotSpot,yHotSpot));
this->update();
}
Function setMouseCursor is called from other class, which set the data of cursor.
ImageWindow is my customized QWidget class.
Apparently, the object which calls setMouseCursor lives outside the GUI thread as far as i can tell. In order to avoid this, make setMouseCursor a slot. Do not call the slot directly, instead emit a signal from the caller object, and connect that signal to setMouseCursor slot using Qt::QueuedConnection.
See : ConnectionType
Two problems:
don't use a QBitmap outside the GUI-thread
don't call gui objects setCursor outside the GUI-thread
Creating a Paint Device
One advantage of using QImage as a
paint device is that it is possible to
guarantee the pixel exactness of any
drawing operation in a
platform-independent way. Another
benefit is that the painting can be
performed in another thread than the
current GUI thread.
I am building a graphic board like project where i am facing a design issue.
Main Class is Board which is a canvas responsible for handling mouse events when drawing shapes. It also has context variables such as currentShape or snapFlag to activate grid magnetism.
To handle the moving / resizing / rotating of the shapes, they inherit from a third party open source tool called ObjectHandles (flex).
I have a baseShape extending ObjectHandles main class to override some of its internal functions, like the onMove function.
When creating a shape (mouse down, move, mouse up) this is handle by the Board and it knows about his own snap flag.
var mouseUpPoint:Point = boardCanvas.globalToLocal(new Point(event.stageX, event.stageY));
var snapMouseUpPoint = snapPoint(mouseUpPoint.x, mouseUpPoint.y);
In my overidden onMove method i would like the shape to be aware of the Board snap flag and when its changing. How do i do this ?
Do i pass the Board as a parameter in my basicShape constructor so that i can check snap ?
Do i pass the flag as a parameter and somehow make all shapes listen for change ?
What is the cleanest solution ?
Thanks a lot.
I would approach this from a slightly different angle. I assume that the Board object traps mouse events first, so that it can decide which shape has been clicked on. I would have the board trap mouse movements as well, passing the correct (snapped or unsnapped) coordinates "down" to the selected Shape object, rather than letting the shape object figure it out.
This leaves the grid snap handling to the Board, and keeps your Shape object onMove method free of clutter.
Not knowing your app:
Is it ever possible for a Shape to have it's own 'snap' behavior? That is, could a Shape be excluded from snapping while others aren't? If so, make snapFlag a member of Shape. When snapFlag is set on the Board, iterate through your Shapes and set or don't set according to your rules.
If snapping behavior applies to all Shapes on the Board, consider an event-driven model (if it's available - I'm a Flex noob). When a Shape moves, have it raise an OnMove event. The Board can then respond and decide to 'snap' the Shape into place if it's appropriate.
If snap behavior applies to all Shapes and events aren't available, I'd just say the hell with loose coupling in this case - make the Shapes Board-aware. It sounds like you're saving a bunch of code by using the ObjectHandle. That benefit may out-weigh the cost of coupling your UI elements.
Just trying to think together with you..
I see no big deal in Shapes having IBoard interface.
Though, I don't like the idea that they have to check the flag on the board...
How would you pass the flag as parameter? In OnMove() method? didn't understood this quite well...could you expand?
Though..
If you try to think a bit about SRP - single responsibility principle...what is the responsibility of Shape classes?
Yea, this is what eJames wrote already.
It feels to me that their main responsibility is probably NOT handling mouse events...here need to know more about your application, but my general feeling is why not someone else get this mouse down and then figure out what the shape should do with it and for instance call Draw() on the Shape with new coordinates?
Let's say you want to apply something like Composite pattern (Shapes inside shapes...) and you want them to be able to handle those mouse events themselves...but then
Then it would be logical if they perceived this mouse event in their local coordinates, but then I think you should provide all the information through this event (local coordinates, mouse status...) so that they don't have to ask for "global" variables on the board...
Passing the flag as a parameter for the shape constructor. But it wont be good since flag is going to change and i have to make each shape to update their flag copy on change.
Its true that shape responsibility is not to know how to handle mouse events. But thats what ObjectHandles do: react to events, update height width rotation parameter of the shape.
Maybe i should transfer some of the library code in my board class to handle shape selection and movement / resizing / rotation.
OnMouseMove ObjectHandles
protected function onMouseMove(event:MouseEvent) : void
{
if( ! visible ) { return; }
if( ! event.buttonDown )
{
setMouseCursor( event.stageX, event.stageY );
return;
}
if(parent == null )
{
return;
}
var dest:Point = parent.globalToLocal( new Point(event.stageX, event.stageY) );
var desiredPos:Point = new Point();
var desiredSize:Point = new Point();
var desiredRotation:Number = 0;
... plenty more
then
if( wasMoved ) { dispatchMoving() ; }
if( wasResized ) { dispatchResizing() ; }
if( wasRotated ) { dispatchRotating(); }
So i can not listen for move event and tell the board to snap it since the shape is already moving freely. I should add snap here:
var dest:Point = parent.globalToLocal( new Point(event.stageX, event.stageY) );
All shapes follow the snap rule there can not be one snapping and the other free.
Solved it this way:
Since i overridde onMouseMove in my baseShape class and i am using PureMVC framework, i just made baseShape aware of my boardMediator.
override protected function onMouseMove(event:MouseEvent) : void
{
[...]
// added on override
var board:BoardMediator = ApplicationFacade.getInstance().retrieveMediator(BoardMediator.NAME) as BoardMediator;
Then
desiredPos = board.snapPoint(desiredPos.x, desiredPos.y);
Maybe not super pretty but it works, o
Overridding the globalToLocal method in my board view did work too but some more calculations were done inside onMouseMove resulting in an out of alignment snap move.
Use ObjectHandles Version 2, and then create a constraint to do what you want.