PDA

View Full Version : Move Rectangle on mouse Move



vermarajeev
9th May 2007, 14:01
Hi guys,
Well, I have some objects on QFrame in form of rectangles( either one or more ). I have options to copy and paste the objects. I say CTRL+C to copy the object and say CTRL+V to paste. While moving the mouse after pressing CTRL+V, I want the rectangular object be seen whereever I move the mouse ( giving a feeling to user that something is about to paste inside QFrame).
In qt3 I could do this with no efforts as there was RasterOp and painting could be done from anywhere but in Qt4 I'm having problem to achieve the same result.

I have written a sample program which will help to get what I mean.

In the sample program, Click on paste button, a rectangle should appear at the tip of mouse pointer. Now if I move mouse, the rectangle should move along with the mouse pointer. I tried but could not succeed.

Here qApp->enter_loop() is QT3 support class as I couldnt find the alternative in Qt4.


RubberBand::RubberBand(QWidget *parent, Qt::WFlags flags)
: QWidget(parent, flags){
setAutoFillBackground( true );
QPalette palette;
palette.setColor(backgroundRole(), QColor(Qt::white) );
setPalette(palette);

QPushButton* btnPaste = new QPushButton("Paste", this);
btnPaste->setGeometry( 250, 250, 30, 40 );
connect( btnPaste, SIGNAL( clicked() ), this, SLOT( onPasteBtn() ) );
m_Rubberband = 0;
}

RubberBand::~RubberBand()
{}

void RubberBand::onPasteBtn(){
qApp->installEventFilter( this );
qApp->enter_loop();
}

bool RubberBand::eventFilter( QObject *obj, QEvent *e ){
if ( e->type() == QEvent::KeyPress && ((QKeyEvent *)e)->key() == Qt::Key_Escape ){
// remove this event filter and exit the current event loop
qApp->removeEventFilter( this );
qApp->exit_loop();
delete m_Rubberband;
m_Rubberband = 0;
return TRUE;
}
if( e->type() == QEvent::MouseMove ){
m_pasteRect.setTop( ((QMouseEvent *)e)->pos().x() );
m_pasteRect.setLeft( ((QMouseEvent *)e)->pos().y() );

m_pasteRect.setBottom( ((QMouseEvent *)e)->pos().x() + 50 );
m_pasteRect.setRight( ((QMouseEvent *)e)->pos().y() + 50 );

if( !m_Rubberband )
m_Rubberband = new QRubberBand(QRubberBand::Rectangle, this);

//qDebug() << ((QMouseEvent *)e)->pos().x() << ((QMouseEvent *)e)->pos().y();

m_Rubberband->setGeometry( m_pasteRect );
m_Rubberband->show();

return TRUE;
}
else if( e->type() == QEvent::MouseButtonRelease ){
// remove this event filter and exit the current event loop
qApp->removeEventFilter( this );
qApp->exit_loop();
delete m_Rubberband;
m_Rubberband = 0;
return TRUE; // eat event
}
return TRUE; // block standard event processing
}

Thanks

marcel
9th May 2007, 14:22
Instead of enter_loop, use processEvents() although you don't need it.

I didn't get to test your example, but take a look at:


void Widget::mousePressEvent(QMouseEvent *event)
{
origin = event->pos();
if (!rubberBand)
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(origin, QSize()));
rubberBand->show();
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
rubberBand->setGeometry(QRect(origin, event->pos()).normalized());
}

void Widget::mouseReleaseEvent(QMouseEvent *event)
{
rubberBand->hide();
// determine selection, for example using QRect::intersects()
// and QRect::contains().
}

This is how your code should look(more or less)! Use specialized event handlers, it is easier and more clear!

I will test it as soon as I get the chance.

Anyway, are you trying to display the rubber band outside it's parent? If so, it will not work. Try passing NULL for parent when you create the QRubberBand.

regards

vermarajeev
9th May 2007, 14:46
Hi marcel,
I know that example.

I think I was not clear what I said.

I dont want to draw a rubberband on mouseClick and enlarge the rubberband on mouse Release. I dont know how to explain but I'll try.

Just run the sample program. Click 'paste'. Now what I want is a rectangle. The top left of rectangle should be on mousePointer. Now when I move the mouse I want the rectangle too to move along with the mousePointer. I use eventFilter coz it is a continous loop until the user releases the mouse where I exit the loop.

Ok I ask you one question. If your user copies some objects (say a polygon) on screen then he/she wants to paste that object some other place on same screen. How do you make sure that the object is copied and is now ready for paste (giving a feeling to user that something is about to paste inside screen)?????Such that when he/she releases the mouse the object gets pasted

marcel
9th May 2007, 14:54
I know what you are trying to do. But you don't do it correctly.
You could modify that example to fit your need.
In mouseMoveEvent, just set the correct geometry.



I use eventFilter coz it is a continous loop until the user releases the mouse where I exit the loop.
It is a loop anyway, and mouseMoveEvent will get called repeatedly.

Returning TRUE always in event() is your biggest mistake. You block all the events, like paint() - This is why the QRubberBand doesn't gets painted - it's parent doesn't get a paint event, therefore neither does the rubber band.

Use the standard event handlers and stop using enter_loop and exit_loop - the Qt event loop works fine, you don't need to force it in doing things:).

Try my suggestion and it will work!

regards

vermarajeev
9th May 2007, 15:32
How do I fire a MouseMove Event from inside a slot?? Meaning


void slotOnPasteButton(){
if( ! computePasteRect() ){ //Gives the reactangular area for object to be pasted
return;
}
// fire or call MouseMove Event
// If the <esc> key is pressed, cancel the pasting operation which I need to check like this
if ( e->type() == QEvent::KeyPress && ((QKeyEvent *)e)->key() == Qt::Key_Escape ) {
//user cancelled paste operation
//clear pasteRect
}
// We have now returned from the mouseMoveEvent, with a new position for the
// bounding box of the objects to be pasted
if( !pasteRect.isNull() )
//do stuffs to paste the object at new position

}

marcel
9th May 2007, 15:41
You do not activate the events.
A mouse move event is received whenever you move the mouse over the widget.

So, mouse move events are always received, given that the mouse is hovered over your widget ( RubberBand ).

In slotOnPasteButton() all you have to do is create the rubber band( QRubberBand ) and set its initial geometry and show it( like it is done in the mousePressEvent form the example ). The mouseMoveEvent has to test is if the QRubberBand is NULL - if not NULL, then set it's geometry, otherwise, do nothing.

To test for modifiers use QMOuseEvent::modifiers().

marcel
9th May 2007, 15:46
Actually, in mouseMoveEvent, it is better to use QRubberBand::move( e->pos() ) instead of setGeometry.

How do you know where the user pastes an object? Does he/she have to press a key, or something?
Because you say "when we return from the mouse move event", but as long as your mouse moves over the widget, you keep getting mouse move events.

vermarajeev
9th May 2007, 16:00
How do you know where the user pastes an object? Does he/she have to press a key, or something?

He/she has to release the mouse to get new position of object being pasted.

What if the user wants to cancel the paste operation while moving the mouse or when when the mouse is at still position???

I'll try out how you said and let you know the status, but do reply me for my questions above coz the user might want to cancel the paste operation anytime....

marcel
9th May 2007, 16:21
He/she has to release the mouse to get new position of object being pasted.

This means that you actually drag the object ( While holding the left button pressed ), so you can use the mouseReleaseEvent to stop dragging and figure out the position where the user dropped the object.

For this you will have to use keyPressEvent();

vermarajeev
10th May 2007, 05:08
This means that you actually drag the object ( While holding the left button pressed ), so you can use the mouseReleaseEvent to stop dragging and figure out the position where the user dropped the object.

Not exactly...But something similar. I dont actually drag....

Ok so far so good. I have got what I want and Marcel it is becuase of your help.Thanks
Now I have two questions.
1) As the size of RubberBand rectangle increases, the display becomes very slow. I think the time is delayed because of painting of rubberBand class. Is there a way I can reduce this delay such that I dont have problem moving the rectangle as it becomes large.
2) As I have other functionalities defined in MouseMove, MousePress, MouseRelease events, not it looks very messy. The idea to include eventFilter was to block all events other than those processed for pasting(mouse moves and clicks). Do you think it was a bad idea????? I know, you think:rolleyes: from above post but can you please explain why???

marcel
10th May 2007, 05:36
1) As the size of RubberBand rectangle increases, the display becomes very slow. I think the time is delayed because of painting of rubberBand class. Is there a way I can reduce this delay such that I dont have problem moving the rectangle as it becomes large.
How big are we talking about? And how does the code look now? Maybe it is because of that.



As I have other functionalities defined in MouseMove, MousePress, MouseRelease events, not it looks very messy. The idea to include eventFilter was to block all events other than those processed for pasting(mouse moves and clicks). Do you think it was a bad idea????? I know, you think:rolleyes: from above post but can you please explain why???
Yes, of course it was a bad idea, because those events are needed.
You cannot block them, because your application will stop functioning properly.
For example, when you move the rubberband, your widget also gets repaint events, that tell it to repaint the regions occupied by the rubberband.
If they are accepted by the event filter, then the rubberband doesn't get any repaint events. Hope you understand now.

You could scan for all the event types in the eventfilter and print out their name, and then you will actually see how many they are.

vermarajeev
10th May 2007, 06:49
How big are we talking about? And how does the code look now? Maybe it is because of that.
Big??? Depends on number of objects and the spacing between them. If there are two rectangles one at left end and other at right end, then it occupies the entire area of screen, then there is a dealy.
I did exactly this
" In slotOnPasteButton() all you have to do is create the rubber band( QRubberBand ) and set its initial geometry and show it( like it is done in the mousePressEvent form the example ). The mouseMoveEvent has to test is if the QRubberBand is NULL - if not NULL, then set it's geometry, otherwise, do nothing."


Yes, of course it was a bad idea, because those events are needed.
You cannot block them, because your application will stop functioning properly.
For example, when you move the rubberband, your widget also gets repaint events, that tell it to repaint the regions occupied by the rubberband.
If they are accepted by the event filter, then the rubberband doesn't get any repaint events. Hope you understand now.

Please see the first example above I have done some modifications.

So what do you say about above changes

vermarajeev
10th May 2007, 06:50
How big are we talking about? And how does the code look now? Maybe it is because of that.
Big??? Depends on number of objects and the spacing between them. If there are two rectangles one at left end and other at right end, the RubberBand rectangle occupies the entire area on screen, then there is a delay.
I did exactly this
" In slotOnPasteButton() all you have to do is create the rubber band( QRubberBand ) and set its initial geometry and show it( like it is done in the mousePressEvent form the example ). The mouseMoveEvent has to test is if the QRubberBand is NULL - if not NULL, then set it's geometry, otherwise, do nothing."


Yes, of course it was a bad idea, because those events are needed.
You cannot block them, because your application will stop functioning properly.
For example, when you move the rubberband, your widget also gets repaint events, that tell it to repaint the regions occupied by the rubberband.
If they are accepted by the event filter, then the rubberband doesn't get any repaint events. Hope you understand now.

I have this modification in my first post

if( (e->type() == QEvent::Paint) || (e->type() == QEvent::Resize) ||
(e->type() == QEvent::FocusIn) || (e->type() == QEvent::FocusOut) )
{
return FALSE;
}
This is the first condition in eventFilter.
No I just want to know whether using eventFilter is really dangerous???

vermarajeev
10th May 2007, 07:00
Something like this


void RubberBand::onPasteBtn(){
qApp->installEventFilter( this );
qApp->enter_loop();
}
void RubberBand::mousePressEvent(QMouseEvent *event)
{
//other stuffs
}
void RubberBand::mouseMoveEvent(QMouseEvent *event)
{
//other stuffs
}
void RubberBand::mouseReleaseEvent(QMouseEvent *event)
{
//other stuffs
}
bool RubberBand::eventFilter( QObject *obj, QEvent *e ){

if( (e->type() == QEvent::Paint) || (e->type() == QEvent::Resize) ||
(e->type() == QEvent::FocusIn) || (e->type() == QEvent::FocusOut) )
{
return FALSE;
}
if ( e->type() == QEvent::KeyPress && ((QKeyEvent *)e)->key() == Qt::Key_Escape ){
// remove this event filter and exit the current event loop
qApp->removeEventFilter( this );
qApp->exit_loop();
delete m_Rubberband;
m_Rubberband = 0;
return TRUE;
}
if( e->type() == QEvent::MouseMove ){
m_pasteRect.setTop( ((QMouseEvent *)e)->pos().x() );
m_pasteRect.setLeft( ((QMouseEvent *)e)->pos().y() );

m_pasteRect.setBottom( ((QMouseEvent *)e)->pos().x() + 50 );
m_pasteRect.setRight( ((QMouseEvent *)e)->pos().y() + 50 );

if( !m_Rubberband )
m_Rubberband = new QRubberBand(QRubberBand::Rectangle, this);

m_Rubberband->setGeometry( m_pasteRect );
m_Rubberband->show();

return TRUE;
}
else if( e->type() == QEvent::MouseButtonRelease ){
// remove this event filter and exit the current event loop
qApp->removeEventFilter( this );
qApp->exit_loop();
delete m_Rubberband;
m_Rubberband = 0;
return TRUE; // eat event
}
return TRUE; // block standard event processing
}

Now in the above code there are both events and eventFilter. So do you still I'm doing something wrong above. Please bear with me. I want to clear my fundas...
Thanks

marcel
10th May 2007, 08:17
OK.
The eventFilter function does exactly what it's name suggests - it FILTERS events - meaning that if you don't want a certain event to reach certain widget, you catch it it.

The function that handles all events is QObject::event(). Here all events are passed and they are forwarded to the more specialized event handlers ( like mousePressEvent, keyPressEvent, etc ).

The events go first through event filter, then in the event().
So, you should not use eventFilter, unless you want to filter some events.
I believe you should take a look in Assistant at eventFilter() and event().

Anyway, accepting the mouse events in the event filter makes the event handlers you created useless because they will not be called.

I assume that you are coming from a long programming period in MFC :).
You shouldn't care about other events, other than mouse move, etc. If you don't need an event, then leave it alone, because you risk having unpredicted behavior in your app ( even if it works well a few times ).

Conclusion: eventFilter is used to filter( read select, catch ) certain events.
event() & friends ( mouseMoveEvent, etc ) is used to actually do something when that certain event is sent to your widget. You may choose to accept it( don't forward it to children ) or reject it.
However, the recommended approach is to use the specialized event handlers. Every event has a specialized event handler.

Regards

patrik08
10th May 2007, 08:39
Something like this
Now in the above code there are both events and eventFilter. So do you still I'm doing something wrong above. Please bear with me. I want to clear my fundas...
Thanks

RubberBand is a hard stuff..

I wait now here more as 60 Day http://www.qtforum.de/forum/viewtopic.php?t=3885 from a QT user a simple minimal demo on RubberBand how draw and move CTRL + Mouse move a rectangle to crop image .... only to become QRect.

I solved , on draw a QWidget.... and is running ok.




void Image_Operator::paintEvent(QPaintEvent *e)
{

if (display.isNull()) {
return;
}
QString selectionText;
/* color to pen */
QColor textColor = Qt::black;
QColor fillrectcolor = Qt::red;
QColor shapepicture = Qt::white;

/////////////////////qDebug() << "### paintEvent go starter...... ";
Load_Actual_Desktop(); /* widget size setting e resolution X11 */
int hi_now = widgetSize.height();
int wi_now = widgetSize.width();
picscaled = display.scaled(wi_now,hi_now,Qt::KeepAspectRatio); /* scaled to widget displayer */
QSize actual_result = picscaled.size(); /* get */
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.drawPixmap(0,0,picscaled);

/*
QFontMetrics fm( this->font() );
int stringWidth = fm.width(selectionText);
int stringHeight = fm.ascent();
*/

const int TEXT_MARGIN = 4;

int bordershade = 26;

int textX = 0;
int textY = 0;

int Top_startX = QMIN(mousePRESSPoint.x(), mouseRELEASEPoint.x());
int Top_startY = QMIN(mousePRESSPoint.y(), mouseRELEASEPoint.y()) ;

int Bot_endX = QMAX(mousePRESSPoint.x(), mouseRELEASEPoint.x());
int Bot_endY = QMAX(mousePRESSPoint.y(), mouseRELEASEPoint.y());

/* display rect mesure image crop !!! */
QPoint topLeft( Top_startX , Top_startY );
QPoint bottomRight( Bot_endX , Bot_endY );
QRect selectionIMAGE( topLeft, bottomRight );

/* display rect mesure image crop !!! */
/* display rect mesure text crop !!! */
QPoint topLeftT( Top_startX , Top_startY - 20 );
QPoint bottomRightT( Bot_endX - TEXT_MARGIN , Bot_endY - TEXT_MARGIN );
QRect selectionTEXT( topLeftT, bottomRightT );
/* display rect mesure text crop !!! */
/* shade border */
QPoint topLeftout( Top_startX - (bordershade / 2) , Top_startY - (bordershade / 2) );
QPoint bottomRightout( Bot_endX + (bordershade / 2) , Bot_endY + (bordershade / 2) );
QRect selectionOutIMAGE(topLeftout,bottomRightout);
/* shade border */


/////////qDebug() << "### Top_startX->" << Top_startX << " Bot_endX->" << Bot_endX;
///////////qDebug() << "### Top_startY->" << Top_startY << " Bot_endY->" << Bot_endY;
int HighteResult = selectionIMAGE.height(); /* correct */
int LargeResult = selectionIMAGE.width(); /* correct */
qreal cento;
qreal ratio;
cento = 100.0;
ratio = (actual_result.width()*cento) / origImageSize.width();
///////////qDebug() << "### originale foto " << origImageSize.width() << "x" << origImageSize.height();
////////////qDebug() << "### a video misura " << actual_result.width() << "x" << actual_result.height();
//////////qDebug() << "### taglio uguale " << LargeResult << "x" << HighteResult;
////////////qDebug() << "### ratio " << ratio;

int LargeReal = ( LargeResult / ratio ) * cento;
int HighteReal = ( HighteResult / ratio ) * cento;
selectionText = QString("%1x%2 px (ratio \%%3)")
.arg(LargeReal)
.arg(HighteReal)
.arg(ratio);

if ( LargeResult < 9 ) {
currentDragMode = NO_EFFECT;
}


QPen pen;
pen.setStyle( Qt::SolidLine );


///////////// qDebug() << "### currentDragMode -> " << currentDragMode ;


if ( currentDragMode == DRAW_SELECTION || currentDragMode == DRAW_LINE ) {
OneWorkImage = selectionIMAGE;
pen.setWidth( bordershade );
pen.setColor( shapepicture );
painter.setPen( pen);
painter.drawRect(selectionOutIMAGE);
/* display rect to crop image !!! */
pen.setWidth( 2 );
pen.setColor( fillrectcolor );
painter.setPen( pen);
painter.drawRect(selectionIMAGE);
/* display rect to crop image !!! */
/* display text image ratio text on crop image !!! */
pen.setColor( fillrectcolor );
painter.setPen( pen);
/* display text image ratio text on crop image !!! */
painter.drawText(selectionTEXT,selectionText);
}
//////// Show_Actual_Params(); /* debug display coordinate all */
}

vermarajeev
10th May 2007, 08:39
The events go first through event filter, then in the event().
So, you should not use eventFilter, unless you want to filter some events.
Anyway, accepting the mouse events in the event filter makes the event handlers you created useless because they will not be called.


Exactly. I accept mouse events in the event filter. Call event_loop to enter in event filter till the user either releases the mouse or press <esc>.Till then all other events are on halt/useless so that I can perform operations only regarding pasting or dragging. Once done pass the control to other event handlers to perform other tasks as in usual manner using
qApp->removeEventFilter( this );
qApp->exit_loop();.

So, again do you think using eventFilter in the above scenario is wrong.
Waiting for a reply desperately.....

marcel
10th May 2007, 08:48
Yes, this approach is wrong because you are using the wrong function.
For what you want to do you must use either event() or the specialized event handlers.
You do not need to filter events and stop certain events.



Till then all other events are on halt/useless so that I can perform operations only regarding pasting or dragging.

As I said before each and every "useless" event is necessary for the app to work properly.
They won't get in your way, so you can just handle the events that interest you.



Call event_loop to enter in event filter till the user either releases the mouse or press <esc>

The eventFilter is not a loop, neither is event & friends. The are called repeatedly for every event that the application event loop receives.
You can easily verify the non-loop affirmation. Use qDebug in mouseMoveEvent() ( or even in eventFilter if you wish ), and print something to the console.
For mouseMoveEvent you will see the message printed over and over as long as your mouse moves over the widget.

Therefore there are no loops at this level. The loop is in the application event loop itself. From there are called event filters and the specialized event handlers.

I hope you understand now what the event work flow is.

Regards

marcel
10th May 2007, 08:50
To Patrick:


I wait now here more as 60 Day http://www.qtforum.de/forum/viewtopic.php?t=3885 from a QT user a simple minimal demo on RubberBand how draw and move CTRL + Mouse move a rectangle to crop image .... only to become QRect.


Why don't you post it here too. I'm sure someone will be able to help you.
I looked at that post but I don't understand German :).

Regards

vermarajeev
10th May 2007, 09:20
Ok, I agree with you...But you didnt tell anything about the lag. Why is there so much delay to paint the RubberBand rect???

marcel
10th May 2007, 09:31
I don't know about that. Maybe if you use move() instead of setGeometry() will make it move faster.

First try rewriting it as we discussed, and see if the lag still appears.

Regards

vermarajeev
10th May 2007, 11:01
I don't know about that. Maybe if you use move() instead of setGeometry() will make it move faster.

First try rewriting it as we discussed, and see if the lag still appears.

Regards

Yes I have used move instead of setGeometry() but still there is lag. I have comlpetely removed the previous logic and is using new functionality as we discussed but still there is lag....

marcel
10th May 2007, 11:07
Can you show me again the code?

vermarajeev
10th May 2007, 11:33
Can you show me again the code?


void myClass::mouseMoveEvent( QMouseEvent *e ){
if ( !readonly ){
if( band ){
band->move( e->pos()- mouseRectOffset );
return;
}
//some other stuffs regarding application
}
}
void myClass::mousePressEvent( QMouseEvent *e ){
if( e->button() == LeftButton ){
if ( !readonly ){
if(canvasMode == CM_DragDropMode ){
hilitObject->hilight( this, FALSE );
dragSelection( e->pos() );
leftDown = TRUE;
return;
}
//some other stuffs regarding application
}
}
}

void myClass::mouseReleaseEvent( QMouseEvent *e ){
if( leftDown ){
leftDown = FALSE;
if( band ){
cacluateNewObjectPosition(e->pos());
delete band;
band = 0;

// If the pasteList is not empty (meaning we didn't cancel the
// paste operation), then do the following:
if( ! pasteList.isEmpty() ){
// Update the pixel coordinates
}
update();
return;
}
//some other stuffs regarding application
}
void myClass::pasteObjects(){
//first calculate pasteRect
if( !band )
band = new QRubberBand(QRubberBand::Rectangle, this);
band->setGeometry( pasteRect );
band->show();
}
void myClass::dragSelection( QPoint clickedP ){
//first calculate pasteRect
if( !band )
band = new QRubberBand(QRubberBand::Rectangle, this);
band->setGeometry( pasteRect );
band->show();
}

vermarajeev
14th May 2007, 06:34
Ok, I'm back again...Can anyone please tell me why there is a lag when I move QRubberBand rectangle???