PDA

View Full Version : Overlay



ToddAtWSU
15th May 2006, 14:23
In my video player, I want to add an overlay so that I can click on the screen and put a red mark for each place I click on the screen. I originally thought a QCanvas could be used for this, but have discovered it has been taken out of Qt 4 and its replacement is not back in. So what can I use to do this? A QPixmap? Is this easy to do? Thanks for your help!

wysota
15th May 2006, 15:47
It doesn't matter what will you choose to paint your marks. Eventually you'll have to use QPainter anyway. You can read in Qt Quarterly about different composition modes and how do they work.

ToddAtWSU
15th May 2006, 20:42
I know I want to use the QPainter class, but I can just make the QPixmap object transparent and use the QPainter object to draw my points onto my overlay, right? Also, how do I place the QPixmap over the desired spot that I want. I do not see a constructor that lets me associate it with a parent widget and I don't see a function to allow me to set its location. Thanks again for your help!

wysota
16th May 2006, 00:59
You don't. You have to draw it using QPainter::drawPixmap().

ToddAtWSU
17th May 2006, 13:35
All I want to do is draw a point over my video scene. I want the user to click on a spot on the video scene and draw a red dot over it. Can I create a transparent QPixmap to place over the video and draw this, and then call QPainter::drawPoint to draw the point, or do I somehow have to associate the point onto the QPixmap and then redraw the QPixmap? Thanks again!!

wysota
17th May 2006, 15:00
Just prepare your pixmap and draw it. You can either use a premade pixmap or draw on it in your application.

jpn
17th May 2006, 15:16
Your video player is inside a QAxWidget right?
Depending a bit on the purpose of the "red points", I would probably subclass the QAxWidget, add a list of points as a member variable and reimplement a few methods:
- mouse press/release event: for adding the clicked point to the list
- paint event: for drawing all the points in the list, AFTER calling the base class implementation

ToddAtWSU
17th May 2006, 16:43
Yes my videoplayer is inside a QAxWidget. The users will only be putting 1-2 red dots on per frame, and when I move frames I need to reset the overlay to transparent and add any existing red points already clicked on for that frame. I am using a vector to keep track of the points that are being clicked on since I have no idea how long the video may be or how many times the user may click on one frame. So I am capturing the mouse clicks already. But I don't understand how I draw. Wysota mentioned the drawPixmap() but I am confused about this. I don't know if a QPixmap is the way to go, it was just something I saw and thought might work. I created a pointer to a QPixmap and initialized it and set its fill to Qt::transparent, but then should the QPainter object be initialized as new QPainter( this ) or QPainter( QPixmapObject )? I thought I could then just use QPainter's drawPoint function to add the new point that was clicked on. So am I misunderstanding something or what? :o As you can tell, I am very confused on how to go about this stuff. I have no file that contains a transparent image for QPixmap. I am just wanting to create a brand new transparent file instead of referencing one by a path. Thanks again! :confused:

ToddAtWSU
17th May 2006, 18:05
Any ideas why I get this error in VS.Net VC++ 7?

In my .h file I say


QPixmap *mpOverlay;

In my .cpp file I say this to initialize my stuff:


mpOverlay = new QPixmap( 561, 351 );
mpOverlay->fill( Qt::transparent );

mpVideoPainter = new QPainter( this );
mpVideoPainter->setPen( Qt::red );

QRectF target( 10.0, 41.0, 561.0, 351.0 );
QRectF source( 0.0, 0.0, 551.0, 310.0 );

mpVideoPainter->drawPixmap( target, &mpOverlay, source );


But I get this error when I compile: error C2664: 'void QPainter::drawPixmap(const QRectF &,const QPixmap &,const QRectF &)' : cannot convert parameter 2 from 'QPixmap **__w64 ' to 'const QPixmap &'

I have googled for solutions but cannot seem to find any. I believe my arguments are correct. Thanks!

jpn
17th May 2006, 18:13
But I get this error when I compile: error C2664: 'void QPainter::drawPixmap(const QRectF &,const QPixmap &,const QRectF &)' : cannot convert parameter 2 from 'QPixmap **__w64 ' to 'const QPixmap &'
That should be:

mpVideoPainter->drawPixmap( target, *mpOverlay, source );

jpn
17th May 2006, 18:22
Here's a one solution for you to consider:



class Player: public QAxWidget
{
...
private:
// a 2-dimensional vector; "a vector of points for each frame"
QVector< QVector<QPoint> > framePoints;
};

void Player::paintEvent(QPaintEvent* e)
{
// let the activex control to draw itself underneath
QAxWidget::paintEvent(e);

// initialize a painter
QPainter painter(this);
painter.setPen(QPen(Qt::red)); // red

// draw all the points stored for the current frame directly on the widget
foreach (QPoint pt, framePoints.at(currentFrame))
painter.drawPoint(pt);
}

ToddAtWSU
17th May 2006, 18:22
I am not sure why putting the * in front helps. To me that turns the pointer into a pointer to a pointer, but for some reason it works. I thought the function wanted my argument de-referenced, but I guess not. So why when I change the Pixmaps fill color to say green do I not see a green rectangle appear over my video player? Thanks again!

ToddAtWSU
17th May 2006, 18:37
When I call the drawPoint function, I see nothing appear on my screen. I clicked around 15-20 times on one frame just to see if anything would appear and I do not see anything. Mine looks slightly like this where mpVideoPlayer is my QAxWidget and AzElPoint is a struct with an xPos, yPos, and frameNumber and azElPoints is a vector of these:


/*
* Event handler for the any widget using the installEventFilter function
*/
bool VideoViewerWindow::eventFilter( QObject *obj, QEvent *event )
{
if( obj == mpVideoPlayer )
{
// Mouse Button Release handlers
if( event->type() == QEvent::MouseButtonRelease )
{
AzElPoint point;
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);

point.frameNumber = getCurrentFrame( );
point.xPos = mouseEvent->x( );
point.yPos = mouseEvent->y( );

if( mouseEvent->button() == Qt::LeftButton )
{
// If left button, place mark on overlay to indicate position
// and store position in array at the current frame index
azElPoints.push_back( point );
printf( "x=%d, y=%d\n", point.xPos, point.yPos );
mpVideoPainter->drawPoint( point.xPos, point.yPos );

return true;
}
else if( mouseEvent->button() == Qt::RightButton )
{
// If right button, remove any point located in current frame
azElPoints.push_back( point );
printf( "x=%d, y=%d\n", point.xPos, point.yPos );

return true;
}
}
}

if( event->type() == QEvent::Paint || event->type( ) == QEvent::Move )
{
if( pMediaSeeking != NULL )
{
repaintWindow( );
QPainter painter( mpVideoPlayer );
painter.setPen( QPen( Qt::red ) );
for( int i = 0 ; i < azElPoints.size( ) ; i++ )
{
if( azElPoints[i].frameNumber == getCurrentFrame( ) )
{
painter.drawPoint( azElPoints[i].xPos, azElPoints[i].yPos );
}
}
}
}

return QObject::eventFilter( obj, event );
}

The repaintWindow( ) function looks like this but it is mainly ActiveX functions and calls so I will not include it since it has nothing to do with Qt. pMediaSeeking being NULL means a video is not playing and we do not need to worry about repainting it or collecting data on it. I also haven't worried about implementing the right-button events yet assuming it will be fairly similar to the left-click events. Thanks once again for your help!!

ToddAtWSU
22nd May 2006, 13:46
Is using an eventFilter the wrong way to go about this? Should I create a separate PaintEvent function? Thanks again. I thought this should draw the point, but unfortunately it does not.

ToddAtWSU
22nd May 2006, 13:57
I added an update( ) call to the if-statement that says:


if( mouseEvent->button() == Qt::LeftButton )
{ // If left button, place mark on overlay to indicate position
// and store position in array at the current frame index
azElPoints.push_back( point );
printf( "x=%d, y=%d\n", point.xPos, point.yPos );
mpVideoPainter->drawPoint( point.xPos, point.yPos );
return true; }

But now my video repaints and then right after that the points redraw, but not over the video. The video disappears. Any ideas how to keep the video showing while also showing the newly clicked point? Thanks for your help!

jpn
22nd May 2006, 14:10
What does repaintWindow() do? Btw, you can paint only in paint events, not in move events.
Assure that the video player gets drawn first, then draw your dots, and return true (so the paint event won't reach the video player).

ToddAtWSU
22nd May 2006, 14:28
My repaintWindow( ) function is a function full of DirectShow/ActiveX calls. This is why I haven't worried about posting it here. It just redraws the current frame of the video into my QAxWidget, but since it is Microsoft-based it takes many calls to get it to work. What do you mean I can't paint in move events. If I move the video player, I want to be able to redraw the video and then redraw the point(s) on top of the video. My eventHandler looks like this:


bool VideoViewerWindow::eventFilter( QObject *obj, QEvent *event )
{
if( obj == mpVideoPlayer )
{
// Mouse Button Release handlers
if( event->type() == QEvent::MouseButtonRelease )
{
AzElPoint point;
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);

point.frameNumber = getCurrentFrame( );
point.xPos = mouseEvent->x( );
point.yPos = mouseEvent->y( );

if( mouseEvent->button() == Qt::LeftButton )
{
// If left button, place mark on overlay to indicate position
// and store position in array at the current frame index
azElPoints.push_back( point );
printf( "x=%d, y=%d\n", point.xPos, point.yPos );
update( );

return true;
}
else if( mouseEvent->button() == Qt::RightButton )
{
// If right button, remove any point located in current frame
azElPoints.push_back( point );
printf( "x=%d, y=%d\n", point.xPos, point.yPos );

return true;
}
}
}

if( event->type() == QEvent::Paint || event->type( ) == QEvent::Move )
{
if( pMediaSeeking != NULL )
{
repaintWindow( );
QPainter* painter;
painter = new QPainter( mpVideoPlayer );
painter->setPen( QPen( Qt::red, 3 ) );
for( int i = 0 ; i < azElPoints.size( ) ; i++ )
{
if( azElPoints[i].frameNumber == getCurrentFrame( ) )
{
painter->drawPoint( azElPoints[i].xPos, azElPoints[i].yPos );
}
}
delete painter;
return true;
}
}

return QObject::eventFilter( obj, event );
}

So what am I doing wrong? To me, it seems to draw the window and then draw the points but erasing the video since I am drawing the points right onto the QAxWidget. So do I need to draw this on a transparent overlay? I quickly see the video and then all I see is the brown background of the main Qt App and the red dot(s) that have been drawn. Thanks!

wysota
22nd May 2006, 15:09
Which Qt4 release are you using?

ToddAtWSU
22nd May 2006, 16:30
I am using version 4.1.0.

jpn
22nd May 2006, 19:00
What do you mean I can't paint in move events. If I move the video player, I want to be able to redraw the video and then redraw the point(s) on top of the video.
From QPainter docs (http://doc.trolltech.com/4.1/qpainter.html#details):

Warning: Unless a widget has the Qt::WA_PaintOutsidePaintEvent attribute set. A QPainter can only be used on a widget inside a paintEvent() or a function called by a paintEvent(). On Mac OS X, you can only paint on a widget in a paintEvent() regardless of this attribute's setting.
You could just call update() to schedule a paint event and draw there. You should actually get a warning output in the console if you try to use a QPainter on a widget outside paint event.

This might need some tweaking but you could try something like this:


if( event->type( ) == QEvent::Move )
{
// just call update() to schedule a paint event
update();
}
else if( event->type() == QEvent::Paint )
{
if( pMediaSeeking != NULL )
{
repaintWindow( );

// remove the event filter during sending
// the paint event to avoid an infinite loop
mpVideoPlayer->removeEventFilter(this);

// send the event and let the video player to draw itself first
QApplication::sendEvent(mpVideoPlayer, event);

// restore the event filter
mpVideoPlayer->installEventFilter(this);

// draw your dots over the video player
QPainter painter( mpVideoPlayer );
painter.setPen(...);
....

// be sure to return true so that the video player doesn't
// receive this paint event and paint itself over again
return true;
}
}

Would've been a bit easier if you had just subclassed in the first place.. ;)

Chicken Blood Machine
22nd May 2006, 21:37
I am not sure why putting the * in front helps.

It helps because you are now giving it the argument type that it is expecting rather than the argument type that it is not expecting.


To me that turns the pointer into a pointer to a pointer

No, that de-references the pointer. See any docs on C/C++ to see the meaning of the '*' operator.


I thought the function wanted my argument de-referenced, but I guess not.

The function wants a reference to a QPixmap object, nothing more. Whether that means de-referencing your variable depends on what kind of object you are dealing with, a QPixmap or a 'pointer to QPixmap'. The following should show you how to correctly deal with both cases.

Example 1 - QPixmap allocated on the stack.


mOverlay = QPixmap( 561, 351 );
...
mpVideoPainter->drawPixmap( target, mOverlay, source );

Example 2 - QPixmap allocated on the heap.


mpOverlay = new QPixmap( 561, 351 );
...
mpVideoPainter->drawPixmap( target, *mpOverlay, source );

ToddAtWSU
24th May 2006, 13:15
I don't know what I was thinking, must have been having a brain freeze at that point. I have been working with C/C++ for 4+ years now, but for some reason I was thinking the & and the * were backwards when I was working on that. Probably too much programming and not enough sunlight.