PDA

View Full Version : another setMouseTracking(true) not working; QWidget



vonCZ
2nd September 2007, 10:10
I am trying to enable mouseTracking in a widget subclassed from QWidget. Here's the subclassed QWidget



//.h
#include <QWidget>
#include <Inventor/Qt/SoQt.h>

class Model;
class QMouseEvent;

class CoinWidget : public QWidget
{
Q_OBJECT

protected:
void mouseMoveEvent(QMouseEvent *event);
...

//.cpp
CoinWidget::CoinWidget(QWidget *parent) : QWidget(parent)
{
SoQt::init(this);
model = new Model(this);
this->setMouseTracking(true); //first attempt to setMouseTracking(true)
}

void CoinWidget::mouseMoveEvent(QMouseEvent *event)
{
this->setMouseTracking(true); //second
qDebug("%d %d", event->x(), event->y());
}

and the constructor for the main window which instantiates the CoinWidget object:



myWindow::myWindow()
{
coinWidget = new CoinWidget;
coinWidget->setMouseTracking(true); //third
this->setMouseTracking(true); //forth
.
.
.

um, as you can see I've tried to setMouseTracking(true) 4 different ways, none of which is successful. I saw in the setMouseTracking/QCanvasView posts, you need to do it via the viewport(). But what about my example: if the widget in which you want to enable mouseTracking is subclassed from QWidget?

marcel
2nd September 2007, 10:57
Are you trying to receive mouse move events on the coin widget?
Have you installed any event filters on it? Because usually it is not required to do this. It receives mouse move events by default...

Regards

vonCZ
2nd September 2007, 11:47
Are you trying to receive mouse move events on the coin widget?

yes, only on the CoinWidget. I want to pass event->x() and event->y() on to another part of the program. The only way I know how to get their values is to include a QMouseEvent. The problem is: I can only get the values when the mouse button is pushed. Are you suggesting there's another way to get these values, whether or not a mouse button is pushed?



Have you installed any event filters on it? Because usually it is not required to do this.

hmmm, there are no event filters on it (unless QMouseEvent is considered a "filter").

marcel
2nd September 2007, 12:10
So mouseMoveEvent does not get called. That is not normal.
Are you sure there is nothing covering the widget?

Perhaps more code would help.

Regards

vonCZ
2nd September 2007, 12:55
mouseMoveEvent *is* getting called... but only when the button is pushed. I think I'm not using setMouseTracking(true) properly.

vonCZ
2nd September 2007, 13:09
my bad (perhaps): i *do* have an event filter for the myWindow class (the main Window, in which CoinWidget is instantiated), something like:



bool Window::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::Enter)
{
if(obj == button_with_popup_widget[0])
{
panel[0]->show();
return QWidget::eventFilter(obj, event);
}
else
{
return QWidget::eventFilter(obj, event);
}
}
else if(event->type() == QEvent::Leave)
{
if(if(obj == panel[0])
{
panel[0]->hide();
return QWidget::eventFilter(obj, event);
}
else
{
return QWidget::eventFilter(obj, event);
}
}
else
{
return QWidget::eventFilter(obj, event);
}
}

...I wouldn't think this is the problem, though, since mousing over the CoinWidget window shouldn't be caught by this filter (which catches some mousing over specific buttons not in the CoinWidget window).

marcel
2nd September 2007, 13:22
No, here must be it. What class is the main window? Is it QWidget or QMainWindow?
Because you should call QMainWindow::eventFilter not QWidget.

And the common practice is to test first for the target object and then for the event type, not the other way around. Take a look at the eventFilter documentation. There is an example there.

Regards

vonCZ
3rd September 2007, 09:40
No, here must be it. What class is the main window? Is it QWidget or QMainWindow?
Because you should call QMainWindow::eventFilter not QWidget.

actually, the main window class is QWidget, not QMainWindow. (I have a couple questions about this that I'll post in another thread later.) Anyway, since the main Window is a QWidget, I assume it's correct to call QWidget::eventFilter?


And the common practice is to test first for the target object and then for the event type, not the other way around. Take a look at the eventFilter documentation. There is an example there.

I'll look more into this. The myWidget example here (http://doc.trolltech.com/4.2/eventsandfilters.html) tests event type first, then object, but perhaps this is a special case. But this doesn't seem to be the problem, anyway: when I comment the eventFilter out of my Main Window, the mouseEvent is still behaving the same way. In other words, this function:



void CoinWidget::mouseMoveEvent(QMouseEvent *event)
{
this->setMouseTracking(true);
qDebug("%d %d", event->x(), event->y());
}

is only called when I press/hold a mouseButton *before* moving the mouse. Thanks for helping me narrow it down, though...

Gopala Krishna
3rd September 2007, 20:16
I feel you still have something wrong in your eventFilter code. Did you comment out the function both in declaration and definition or just commented function body ? Try calling return QWidget::eventFilter(obj,e); in the body of event filter defined by you in the beginning itself instead of just commenting.

If everything is proper may be you have installed event filter for QApplication, did you check for this ?

The following example works fine for me

#include <QtGui>

QTextEdit *te;

class Widget : public QWidget
{
public:
Widget() : QWidget()
{
setMouseTracking(true);
}
void mouseMoveEvent(QMouseEvent *e)
{
te->append(QString("%1,%2\n").arg(e->pos().x()).arg(e->pos().y()));
QWidget::mouseMoveEvent(e);
}
};

int main(int argc, char *argv[])
{
QApplication app(argc,argv);
Widget w;
w.show();
te = new QTextEdit;
te->show();
return app.exec();
}


EDIT: Actually your event filter code seems to be proper(even without commenting). So my guess is there is one more event filter(may be installed on qapp) which is causing malfunction in your case.

vonCZ
5th September 2007, 13:02
...this has become hugely frustrating. Thanks, GK- yeah, your example worked for me, too. I tinkered w/the HelloGL, as well, and it works. I even included the glwidget.h/.cpp file in my own application (i replaced coinWidget with the glWidget), turned on MouseTracking, at it works.

But nothing I do with coinWidget will work! I even tried commenting the moveMouseEvent feature in the mainWindow class, then editing the mainWindow eventFilter like so:



mainWindow::mainWindow()
{
coinWidget = new CoinWidget;
coinWidget->setMouseTracking(true);
coinWidget->installEventFilter(this);
.
.
.

bool mainWindow::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::MouseMove)
{
qDebug("eventFilter: hello");
return QWidget::eventFilter(obj, event);
}
else
{
return QWidget::eventFilter(obj, event);
}
}

but still only see "eventFilter: hello" if I click/drag the mouse button (on coinWidget). Other widgets with both installEventFilter() and setMouseTracking(true), however, work as expected.

So: the problem has to be my coinWidget class. This is a container widget for a coin3d scenegraph. Perhaps the SoQt widget embedded in the coinWidget is grabbing the moveMouseEvent even before Qt can? But I wouldn't think this could happen. Anyway, I'm posting the coinWidget code below. See anything???



//+++++++++++ coinwidget.h
#ifndef COINWIDGET_H
#define COINWIDGET_H

#include <QWidget>
#include <Inventor/Qt/SoQt.h>


class Model; // the class defining the coin3d geometry

class CoinWidget : public QWidget
{
Q_OBJECT

public:
CoinWidget(QWidget *parent = 0);
~CoinWidget();

QSize minimumSizeHint() const;
QSize sizeHint() const;

public slots:

void lyBG(int value);
void vuBG(int value);
void lyImgSL(int objID, int value);
void setComp(int value);
void zXagSL(int objID, int value);
void setRotAxis();
void vuSterSL();
void vuSterAcSL(int value);
void vuDrawAcSL(int value);
void vuOrthoSL();
void vuModLocSL();


signals:


protected:

// void mouseMoveEvent(QMouseEvent *event);
// above: deleted. Now only included in the mainWindow class

Model *model;

};
#endif


//+++++++++ coinwidget.cpp
#include <QtGui>
#include <QtCore>
#include <Inventor/Qt/SoQt.h>
#include <Inventor/Qt/viewers/SoQtExaminerViewer.h>

#include "coinwidget.h"
#include "model.h"

CoinWidget::CoinWidget(QWidget *parent) : QWidget(parent)
{
SoQt::init(this);

model = new Model(this);
model->setDefaultScene();

// this->setMouseTracking(true); // no longer included here;
// i do this in the mainWindow class

}

CoinWidget::~CoinWidget() { }

QSize CoinWidget::minimumSizeHint() const {
return QSize(200, 200);
}

QSize CoinWidget::sizeHint() const {
return QSize(400, 400);
}


void CoinWidget::vuBG(int value)
{
model->vuBG(value);
}


void CoinWidget::lyBG(int value)
{
model->lyBG(value);
}


void CoinWidget::setComp(int value)
{
model->setComp(value);
}


void CoinWidget::lyImgSL(int objID, int value)
{
model->lyImgSwi(objID, value);
}


void CoinWidget::zXagSL(int objID, int value)
{
model->set_zXag(objID, value);
}


void CoinWidget::setRotAxis()
{
model->setRotAxis();
}


void CoinWidget::vuSterSL()
{
model->vuSter();
}


void CoinWidget::vuSterAcSL(int value)
{
model->vuSterType(value);
}


void CoinWidget::vuDrawAcSL(int value)
{
model->vuDrawType(value);
}


void CoinWidget::vuOrthoSL()
{
model->vuOrtho();
}


void CoinWidget::vuModLocSL()
{
model->vuModLoc();
}

/*
void CoinWidget::mouseMoveEvent(QMouseEvent *event)
{

qDebug("CoinWidget:: %d %d", event->x(), event->y());

}
*/

marcel
5th September 2007, 13:06
This is a container widget for a coin3d scenegraph. Perhaps the SoQt widget embedded in the coinWidget is grabbing the moveMouseEvent even before Qt can?

Well, you didn't mention this before.

This is the reason. It happened to me lots of times.
The fix is to install an event filter on the child wiidget. Do what you want with the event, but make sure to forward it to the child, in case it needs it.

Regards

vonCZ
5th September 2007, 13:33
I was all set to donate a few kopeks to the Ceauseascu Center for Historical Revisionism in your honor... when I realized I don't know what you're talking about (yet).

When you say


install an event filter on the child wiidget.
...make sure to forward it to the child

I tried that (i think), above: "coinWidget->installEventFilter(this);" ...or what am I missing?

marcel
5th September 2007, 13:47
Forget Ceausescu :).
What you're missing (I think) is that you have to install an event filter for the widget contained within the coin widget(I don't know what is called), because that is the one that's eating all the mouse move events for your widget.

So, what do you have inside the coin widget?

Regards

vonCZ
5th September 2007, 14:06
the coinWidget constructor is basically:



CoinWidget::CoinWidget(QWidget *parent) : QWidget(parent)
{

SoQt::init(this); // initializing the coin3d render window

model = new Model(this); // the coin3d scenegraph
model->setDefaultScene(); // set up default 3d scene

}

and the constructor for Model:


Model::Model( QWidget *parent, const char * name, const SbBool embed)
: SoQtExaminerViewer( parent, name, embed, SoQtFullViewer::BUILD_POPUP, SoQtViewer::BROWSER, TRUE)
{

QWidget *widget = this->buildWidget(this->getParentWidget());
this->setBaseWidget(widget);

}

so you're suggesting that I add an eventFilter to the Model class to intercept the mouseMovements? I'll try this. It does seem odd that I can intercept the mouseMovements at the application/mainWindow level, but only if the mouse-button is pushed/dragged.

ok, we'll bag Ceauseascu for now. ...maybe Vlad?;) I just listened to a podcast about the famed Impaler a couple days ago. tisk tisk: everyone knows the humane way to slaughter folks is from a B-52.:rolleyes: (sorry, gettin' off-topic here)

marcel
5th September 2007, 14:12
Install the event filter on the baseWidget of Model.
Does it have a getter for it?

Regards

vonCZ
5th September 2007, 14:23
I don't understand what the BaseWidget is. It looks to me like in:

QWidget *widget = this->buildWidget(this->getParentWidget());

widget is a pointer to the coinWidget class/object? So

this->setBaseWidget(widget);

is setting baseWidget to the coinWidget? I don't know what a getter is.

vonCZ
5th September 2007, 14:26
void SoQtComponent::setBaseWidget(QWidget * widget) [protected]

Set the core widget for this SoQt component. It is important that subclasses get this correct, as the widget set here will be the widget returned from query methods.

from the SoQtExaminerViewer documentation.


EDIT: isn't "the widget set here" in my case the CoinWidget?


QWidget * SoQtFullViewer::buildWidget (QWidget * parent ) [protected]

This method builds the component contents in the given parent widget. For subclasses adding new user interface items, this method is typically overridden in the following manner:

QWidget * MyOwnViewer::buildWidget(QWidget * parent)
{
QWidget * superw = <superclass>::buildWidget(parent);
// [then move superw within MyOwnViewer framework and add own
// user interface components]
}

marcel
5th September 2007, 14:27
If it has a setter( setBaseWidget) then it also should have a getter( getBaseWidget or baseWidget).

You need a function in the model that returns the widget created in the constructor( widget ). The event filter has to be installed on that widget.

Regards

vonCZ
5th September 2007, 14:30
gotcha. yeah, there's a getBaseWidget.

pdolbey
5th September 2007, 15:07
I've sort of been watching this thread from a distance. I'm at work a the moment so I can't view the Coin3d, or the code we played with a while back, source, but in the original thread starter on http://www.qtcentre.org/forum/f-newbie-4/t-coin3d-qt-siglnals-and-slots-6895.html you used to have a line


SoQtExaminerViewer *eviewer = new SoQtExaminerViewer(this);

that created a SoQtExaminerViewer within the CoinWidget. This effectively overlays a coin opengl rendering window over your QWidget, and hooks in the Coin model. I've been trying to read the coin source SVN respository but its responding slowly - but its possible that on Windows this is done by subclassing the native WindowProc. The Coin3d / OIV viewers don't inherit from QWidget, so event won't bubble up the heirarchy chain - I'll take look at the coin source tonight on my pc to check how its linked in. But some of the coin examples show classes inherited from SoQt...Viewer as well as QWidget.

This might mean that trying to hook a Qt event filter won't work in this way, and this is what your seeing.

Pete

p.s Missed all the latest posts!

vonCZ
5th September 2007, 16:41
hey pdolbey- thanks for chiming in. True, I used to have that line


SoQtExaminerViewer *eviewer = new SoQtExaminerViewer(this);

before you posted your example of how you do it, which I now follow:



//++ main.cpp
int main(argc, *argv[])
{
Window window;

//++ window.cpp
Window::Window()
{
coinWidget = new CoinWidget;

//++ coinwidget.cpp
CoinWidget::CoinWidget(QWidget *parent) : QWidget(parent)
{
SoQt::init(this);
model = new Model(this);
model->setDefaultScene();

//++ model.cpp
Model::Model(QWQWidget *parent, const char * name, const SbBool embed)
: SoQtExaminerViewer( parent, name, embed, SoQtFullViewer::BUILD_POPUP, SoQtViewer::BROWSER, TRUE)
{
QWidget *widget = this->buildWidget(this->getParentWidget());
this->setBaseWidget(widget);


So, I'm not sure how this affects your take on this. Given this method, is "its possible that on Windows this is done by subclassing the native WindowProc" still something to worry about?

Again, the confusing thing to me: both of the Window class event filters



bool Window::eventFilter(QObject *obj, QEvent *event)
// OR
void Window::mouseMoveEvent(QMouseEvent *event)


*will* recognize the mouse Move event... but only if the button is pressed.

pdolbey
5th September 2007, 18:03
The coin SVN is responding better now. I've discovered some interesting code in
http://source.coin3d.org/viewvc.py/SoQt/trunk/src/Inventor/Qt/SoQtGLWidget.cpp?view=markup
that shows up an eventFilter function that runs in a private implementation widget. Some of the comments indicate that the result your getting is not very suprising.

Do you want to send me another code extract? Have you still got my email address?

Pete

vonCZ
5th September 2007, 18:41
your email is on my infected computer--i got careless w/torrents--so no, I don't have it at the moment. But here are the files. The window, model, and coinwidget .cpp/.h files are probably the only ones relevant, but I included the others in case you want to compile. (though it'll crash at run-time without the .iv files; i need to fix this.)

Anyway, I tried adding an eventFilter to the Model class, but as you said it doesn't inherit from QWidget.... so this:



bool Model::eventFilter(QObject *obj, QEvent *event)
{
return QWidget::eventFilter(obj, event);
}

causes an error when compiling: "QObject::eventFilter : illegal call of non-static member function." Perhaps there's a way around this?

BTW, the reason I want to get the mouse Events in Qt: I'm using the SoQtExaminerViewer class for rendering. I have a function set up so that the user can mouse over the model and get the x,y,z coordinates of that spot on the model. However, it only works in selection mode, e.g.

Model::setViewing(false);

but I want it to work in the other mode, as well. So I figured I'd forward the mousePosition to the Model class from Qt so that the feature would work in both selection and non-selection mode. Perhaps there's an easier/better way?

marcel
5th September 2007, 20:07
But you got me all wrong.
I suggested to install an event filter on the QWidget returned by this->buildWidget(this->getParentWidget());.

The event filter should be the parent widget.

Hope it's clear now.

Regards

vonCZ
6th September 2007, 08:11
Oh, ok. It didn't work. I added:


// model.cpp

QWidget *Model::returnBase()
{
QWidget *widget = this->getBaseWidget();
return widget;
}

to the Model class, and the last 2 lines below:



// coinwidget.cpp
CoinWidget::CoinWidget(QWidget *parent) : QWidget(parent)
{
SoQt::init(this);

model = new Model(this);
model->setDefaultScene();

model->returnBase()->installEventFilter(this);
model->returnBase()->setMouseTracking(true);

}

to the CoinWidget class. I removed all eventFilters except the one in CoinWidget. As before, the eventFilter is called when I do a mouseMove event over it, but only when the mouse Button is pressed.

vonCZ
6th September 2007, 12:02
pdolbey- in a nutshell, I've the following callback in my coin3d code (Model class):



void Model::myMouseMoveCB(void *data, SoEventCallback *eventCB)
{

qDebug("...hello");

SoSeparator *root = (SoSeparator *) data;
const SoEvent *event = eventCB->getEvent();
const SbViewportRegion &myRegion = eventCB->getAction()->getViewportRegion();
showXYZ(root, myRegion, event->getPosition(myRegion));
eventCB->setHandled();

}

The problem is: the callback is only called when

Model::this->setViewing(false);

This is the reason I've been pursuing the Qt route; I thought I could just send the screenPosition coordinates (over the widget) straight from Qt to Coin, but it's looking like this might not be possible.

pdolbey
6th September 2007, 13:09
VonCZ

According to the coin SVN repository, the SoWin viewers do exactly what I expected to see i.e. registering and creating a window with window proc, then creating a OpenGL context, but the SoQt viewers are doing some wierd and wonderful stuff with pimpl classes. If the sample you posted above can reporduce the problem I'll pull it down tonight and try debuging throught the coin code, although I'm trying to sort out some OpenCascade problems ( of my own creation ) at the moment. Otherwise, you can post it to "peter at dolbey dot freeserve dot co dot uk".

Pete

vonCZ
13th September 2007, 10:01
thanks for the solution, pdolbey! Rather than re-write the email he posted, I'll quote it here:


Basically SoQt is a rats nest of widgets, pimpls (er that's private implementations) and miscellaneous filters. Unfortunately your attempts at hacking the filters have taken a bit of a scattergun approach, but I can understand why.

However I've attached a hack which might just work for you, only changing the Model class source files. Basically, the SoQtGLRenderArea class (a parent of SoQtExaminerViewer) sets up an eventfilter on a private widget that it processes with a processEvents method. What I've done with the Model class (even though its not technically a Qt widget) is to over-ride the processEvents method and put in a hook to the Model::mouseMoveEvent. You need to let the event still be processed by the parent classs so it can translate to the SoEvent model, but the method is defined as protected not private so this technique must be "allowed".

and the changes/additions to the Model class are:


//++++ model.h

#include <QEvent>
#include <QMouseEvent>

protected:
void mouseMoveEvent(QMouseEvent *event);
void processEvent(QEvent *event);



//++++ model.cpp

void Model::mouseMoveEvent(QMouseEvent *event)
{
qDebug("Model:: %d %d", event->x(), event->y() );
}

void Model::processEvent(QEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
mouseMoveEvent ((QMouseEvent*) event);
}
SoQtExaminerViewer::processEvent(event);
}

thanks again!