QGraphicsView and eventFilter - qt

This has been bugging me for more than two days now, so i thought i should ask. I am using Qt 4.5.3 (compiled with VC2008) on Win7.
I have MyGraphicsView (inherits QGraphicsView) and MyFilter (inherits QObject) classes.
When i install the MyFilter object as an event filter to MyGraphicsView, Mouse events are delivered to MyFilter after they are delivered to MyGraphicsView whereas Key events are delivered to MyFilter before they are delivered to MyGraphicsView.
In the second case, i install the MyFilter object as an event filter to MyGraphicsView->viewport() (which is a standart QGLWidget), Mouse events are delivered to MyFilter before they are delivered to MyGraphicsView, whereas Key events are delivered to only MyGraphicsView.
The events are supposed to be delivered to event filters before they are delivered to the actual object, so why is this happening? What should i do to ensure this order?
Thanks in advance.
Best Regards.

QGraphicsView is a subclass of QAbstractScrollArea which is the cause of these behaviors.
In the first case, the QAbstractScrollArea adds itself as a event filter to the MyGraphicsView when setViewport() is called. The QAbstractScrollArea's event filter captures the mouse event, first sends it through viewportEvent(), and then to the QWidget event handling which propagates to the MyGraphicsView mouse event handlers. Only after this is the QAbstractScrollArea's event filter finished and MyFilter gets to run.
In the second case, key events are delivered only to the MyGraphicsView because in setViewport() the QAbstractScrollArea sets itself as the focus proxy. If the focus proxy is reset with the following code, the key events will be delivered.
w.viewport()->setFocusProxy(0);
An alternative is to install the event filter on both the graphics view and its viewport, but modify the filter to only process key events from one object and mouse events from the other.
Change MyFilter.h
QObject *keyObj;
QObject *mouseObj;
public:
MyFilter(QObject *keyObj, QObject *mouseObj, QObject *parent = NULL);
Change MyFilter.cpp
MyFilter::MyFilter(QObject *keyObj, QObject *mouseObj, QObject *parent /*= NULL*/ ) : QObject(parent), keyObj(keyObj), mouseObj(mouseObj)
and
if (obj == keyObj && e->type() == QEvent::KeyPress)
{
qDebug()<<"Key Event recieved by MyFilter";
}
else if (obj == mouseObj && e->type() == QEvent::MouseButtonPress)
{
qDebug()<<"Mouse Event recieved by MyFilter";
}
Change main.cpp
MyFilter *filter = new MyFilter(&w, w.viewport(), &w);
// Use this line to install to the viewport
w.viewport()->installEventFilter(filter);
//Use this line to install to MyGraphicsView
w.installEventFilter(filter);

How about to try not using filter but reimplement necessary QEvent handlers at MyGraphicsView like here:
void MyGraphicsView::mousePressEvent(QMouseEvent* pe)
{
if (pe->buttons() & Qt::LeftButton)
{
this->setCursor(Qt::CrossCursor);
zoomOrigin = pe->pos();
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(zoomOrigin, QSize(0,0)));
rubberBand->show();
}
if (pe->buttons() & Qt::MidButton)
{
panOrigin = pe->pos();
this->setCursor(Qt::ClosedHandCursor);
}
}

Related

How to fire a signal with QMetaObject::activate

I found an interesting article on how to impement QObject with dynamic properties (see C++ class DynamicObject). The code from the article works fine, the properties of DynamicObject are get and set successfully from both C++ and QML, but the only thing I cannot figure out is how to fire dynamic signals.
I tried to fire "nameChanged()" signal with the following code:
bool DynamicObject::emitDynamicSignal(char *signal, void **arguments)
{
QByteArray theSignal = QMetaObject::normalizedSignature(signal);
int signalId = metaObject()->indexOfSignal(theSignal);
if (signalId >= 0)
{
QMetaObject::activate(this, metaObject(), signalId, arguments);
return true;
}
return false;
}
myDynamicObject->emitDynamicSignal("nameChanged()", nullptr);
the index of the signal is found and signalId is assigned to 5, but the signal is not fired. But if I do, for example,
myDynamicObject->setProperty("name", "Botanik");
the property is changed and the signal is fired successfully.
What is wrong in my code? What should I pass as 'arguments' parameter of QMetaObject::activate ?
EDIT1:
The full source code is temporarily available here.
A signal is also a method. You can invoke it from the meta object.
So, replace your line QMetaObject::activate(...) by:
metaObject()->method(signalId).invoke(this);
And let Qt handles the call to activate().
There is also an issue in DynamicObject::qt_metacall(): you are handling only QMetaObject::ReadProperty and QMetaObject::WriteProperty calls.
You have to add QMetaObject::InvokeMetaMethod if you want to emit your signal.

Intercepting Tab key press to manage focus switching manually

I want to intercept Tab key press in my main window to prevent Qt from switching focus. Here's what I've tried so far:
bool CMainWindow::event(QEvent * e)
{
if (e && e->type() == QEvent::KeyPress)
{
QKeyEvent * keyEvent = dynamic_cast<QKeyEvent*>(e);
if (keyEvent && keyEvent->key() == Qt::Key_Tab)
return true;
}
return QMainWindow::event(e);
}
This doesn't work, event isn't called when I press Tab. How to achieve what I want?
The most elegant way I found to avoid focus change is to reimplement in your class derived from QWidget the method bool focusNextPrevChild(bool next) and simply return FALSE. In case you want to allow it, return TRUE.
Like other keys you get now also the key Qt::Key_Tab in keyPressEvent(QKeyEvent* event)
Reimplementing virtual bool QApplication::notify(QObject * receiver, QEvent * e) and pasting the code from my question there works.
You can achieve by using setFocusPolicy( Qt::NoFocus) property of QWidget. You can set Focus policy on widget which doesn't require tab focus. I think the reason why event handler is not calling, because Tab is managed by Qt framework internally. Please see QWidget::setTabOrder API, which is static.
You'll need to install an event filter on your main window in order to receive the events. You can use installEventFilter method for this.
Another option is to override the keyPressEvent method to handle the key presses.

Unable to Handle Tap, Swipe, Pan, Pinch events for Qt Poppler Library example

I took the example of the Poppler Library to render PDF on Qt platform. I am trying to write gesture handling for the example.
The example can be downloaded from "doc.qt.digia.com/qq/qq27-poppler.zip" URL.
In a nutshell, the example has a DocumentWidget.cpp which is Derived from "QLabel" which in turn derives from QFrame and QWidget.
Now since QLabel is inherited from QWidget I have started implementing for Gesture Handling.
in DocumentWidget.h i have added the below function
protected:
bool event( QEvent* e );
in DocumentWidget.cpp constructor
grabGesture( Qt::TapGesture )
bool DocumentWidget :: event( QEvent* e )
{
if( e->type() == ( Qt :: TapGesture )
gestureEvent();
}
In the above gestureEvent function I try to check if it is Tap and correspondingly planning to handle the tap event. However, In my example the event function is getting called but the tap gesture is being handled.
I have included all the necessary header files. I have forward declared the classes too.
Can you please tell me where am I going wrong. Can't we handle these gestures for QLabel.
Thank You in advance.
Chand.M
You are already checking for TagGesture in the event handler. So you need not check it once again in the gestureEvent() function. Moreover after consuming the event you have to return true; stating that the even has been consumed.
If the even is not a TapGesture then pass it to the QWidget itself like this.
if( e->type() == ( Qt :: TapGesture )
gestureEvent();
else
return QWidget::event(e);
You can find documentation on even handling here.

Generating a QMouseEvent manually within the keyPressEvent in Qt

I have overridden the contentsMousePressEvent in my listview like this.
void AppListView::contentsMousePressEvent(QMouseEvent *e)
{
AppGenericListView::contentsMousePressEvent(e);
if (e->button() == Qt::RightButton)
emit rightClicked();
emit multiSelection();
}
Here is my keyPressEvent.
void AppListView::keyPressEvent(QKeyEvent * e)
{
AppGenericListView::keyPressEvent(e);
if ((e->key() == Qt::Key_Up) || (e->key() == Qt::Key_Down))
{
QListViewItem * item = currentItem();
if (item)
{
const QRect rect = itemRect(item);
QMouseEvent mEvt(QEvent::MouseButtonPress, rect.center(), Qt::LeftButton, Qt::LeftButton);
contentsMousePressEvent(&mEvt);
}
}
}
For now, this code working fine. Please note that i'm not creating a dynamic QMouseEvent object. What i want to know is will this cause a crash in future ? Does contentMousePressEvent need a dyanamic object ? Qt doc doesn't say much about it. Any ideas ....
It won't crash, because you are not using event loop. But i think you should, for two reasons:
You are simulating mouse press event but you are not serving it to the object as a such. For example, you don't serve your fake mouse event to mousePressEvent(). So for "natural" mouse press events and your simulated one, the application will behave differently. And you may forget why is that and you may get inexplicable crashes when your code evolves.
The original system keypress event handling will be blocked by handling a mouse press event. You can't know who (what thread) will connect() to your signals emitted from overriden mouse event handler. Don't be surprised if you get a deadlock.
Such a half baked shortcuts are good only as temporary solutions. In the long run, they will shoot at your back. If you really want a shortcut, stop pretending it's a mouse event and implement a special separate method which will be called also from the "real" mouse event. If you want a real mouse event, handled properly, create a dynamic QMouseEvent and enqueue it at the event loop.
QMouseEvent* evt = new QMouseEvent(QEvent::MouseButtonPress,
rect.center(),this->mapToGlobal(rect.center()),
Qt::LeftButton, Qt::LeftButton);
QCoreApplication::postEvent(this,evt);
The event handlers don't take ownership of the events they receive. So your current code is fine.
It would be deleted automatically (and cause a crash), if you were passing it to QCoreApplication::postEvent to be sent asynchronously.

Qt force QCheckBox to emit signal on setChecked

If I called QCheckBox::setChecked( x ) the toggled signal is only emitted if x is not the same as the current checkbox state. I understand the logic behind this, to avoid signaling if nothing has changed. However, in some situations where I have a more complicated widgets setup, I need the signal to always be emitted. This ensures anybody who has connected to the checkbox will receive the first state.
Is there a way to have QCheckBox::setChecked(bool) emit a signal regardless of whether the state has changed?
My simple workaround now is to just force the checkbox into multiple states by doing setChecked(!x) and setChecked(x). I was hoping for a more correct way of doing this.
Looking into the QAbstractButton implementation, I found the following lines of code:
if (!d->checkable || d->checked == checked) {
if (!d->blockRefresh)
checkStateSet();
return;
}
where checkStateSet is a virtual function. QCheckBox overrides this and emits a stateChanged() signal only if the state changed.
I haven't tested this, but I think d->blockRefresh is set to false if you call QCheckBox::setChecked( ... ) directly.
If this is the case, it means you could subclass QCheckBox and override the checkStateSet() method to something like this:
void MyCheckBox::checkStateSet()
{
QCheckBox::checkStateSet();
if( m_oldState == checkState() )
{
// emit the signal here, as QCheckBox::checkStateSet() didn't do it.
emit stateChanged( m_oldState );
}
else
{
// don't emit the signal as it has been emitted already,
// but save the old state
m_oldState = checkState();
}
}
where the header file contains
private:
Qt::CheckState m_oldState;
which must be initialised to Qt::Unchecked in the constructor.
Here is another solution which may or may not be possible for your case:
If you can be 100% sure that your signals and slots are connected before the checkbox has a chance to change its state, every connected class can initialize itself safely assuming the checkbox is not checked. This is because checkboxes are always unchecked upon construction.
This way you might not have to call setChecked() after connecting the signals.
However, This approach does not work if there is a chance a signal gets connected after the checkbox has already changed. I'm not 100% fond of this approach myself but it might be an option for you nevertheless.
One way would be to subclass QCheckBox and implement the emitting of signals in that where you need it, for example :
class MyCheckBox : public QCheckBox
{
Q_OBJECT
public:
MyCheckBox(QWidget *parent = 0) : QCheckBox(parent) {};
virtual void setChecked(bool checked) {
QCheckBox::setChecked(checked); emit checkWasSet(checked);
};
signals:
void checkWasSet(bool value);
};
Now use this class instead of the regular QCheckBox class, and you can connect to the checkWasSet() signal for whenever the check state is set.
You could emit the signal with the current state yourself:
checkbox.stateChanged.emit(checkbox.checkState())

Resources