What is the best (as in simplest) way to obtain the pos of a mousePressedEvent in a QLabel? (Or basically just obtain the location of a mouse click relative to a QLabel widget)
EDIT
I tried what Frank suggested in this way:
bool MainWindow::eventFilter(QObject *someOb, QEvent *ev)
{
if(someOb == ui->label && ev->type() == QEvent::MouseButtonPress)
{
QMouseEvent *me = static_cast<QMouseEvent *>(ev);
QPoint coordinates = me->pos();
//do stuff
return true;
}
else return false;
}
However, I receive the compile error invalid static_cast from type 'QEvent*' to type 'const QMouseEvent*' on the line where I try to declare me. Any ideas what I'm doing wrong here?
You could subclass QLabel and reimplement mousePressEvent(QMouseEvent*). Or use an event filter:
bool OneOfMyClasses::eventFilter( QObject* watched, QEvent* event ) {
if ( watched != label )
return false;
if ( event->type() != QEvent::MouseButtonPress )
return false;
const QMouseEvent* const me = static_cast<const QMouseEvent*>( event );
//might want to check the buttons here
const QPoint p = me->pos(); //...or ->globalPos();
...
return false;
}
label->installEventFilter( watcher ); // watcher is the OneOfMyClasses instance supposed to do the filtering.
The advantage of event filtering is that is more flexible and doesn't require subclassing. But if you need custom behavior as a result of the received event anyway or already have a subclass, its more straightforward to just reimplement fooEvent().
I had the same problem
invalid static_cast...
I just forgot to include the header: #include "qevent.h"
Now everything is working well.
Related
In Delphi I often made an OnAfterShow event for the main form. The standard OnShow() for the form would have little but a postmessage() which would cause the OnafterShow method to be executed.
I did this so that sometimes lengthy data loading or initializations would not stop the normal loading and showing of the main form.
I'd like to do something similar in a Qt application that will run on a desktop computer either Linux or Windows.
What ways are available to me to do this?
You can override showEvent() of the window and call the function you want to be called with a single shot timer :
void MyWidget::showEvent(QShowEvent *)
{
QTimer::singleShot(50, this, SLOT(doWork());
}
This way when the windows is about to be shown, showEvent is triggered and the doWork slot would be called within a small time after it is shown.
You can also override the eventFilter in your widget and check for QEvent::Show event :
bool MyWidget::eventFilter(QObject * obj, QEvent * event)
{
if(obj == this && event->type() == QEvent::Show)
{
QTimer::singleShot(50, this, SLOT(doWork());
}
return false;
}
When using event filter approach, you should also install the event filter in the constructor by:
this->installEventFilter(this);
I solved it without a timer using Paint event. Works for me at least on Windows.
// MainWindow.h
class MainWindow : public QMainWindow
{
...
bool event(QEvent *event) override;
void functionAfterShown();
...
bool functionAfterShownCalled = false;
...
}
// MainWindow.cpp
bool MainWindow::event(QEvent *event)
{
const bool ret_val = QMainWindow::event(event);
if(!functionAfterShownCalled && event->type() == QEvent::Paint)
{
functionAfterShown();
functionAfterShownCalled = true;
}
return ret_val;
}
i'm trying to take screenshot with Paint event (WM_Paint) event in QT but i dont know how,
i used this code to take screenshot but it is not really good for do this
it must do screenshot when some changes in desktop not take screenshot every 1000 ms with timer
void MainWindow::shootScreen()
{
originalPixmap = QPixmap(); // clear image for low memory situations
// on embedded devices.
originalPixmap = QPixmap::grabWindow(QApplication::desktop()->winId());
//emit getScreen(originalPixmap);
updateScreenshotLabel();
}
void MainWindow::updateScreenshotLabel()
{
this->ui->label_2->setPixmap(originalPixmap.scaled(this->ui->label_2- >size(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation));
}
Use QObject::installEventFilter on the widgets you are interested in, then check for the proper events. For example, in your MainWindow ui initialization:
void MainWindow::yourUiInitFunc()
{
exampleWidget = new QWidget;
...
exampleWidget->installEventFilter(this);
...
}
Then re-implement eventFilter:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == exampleWidget && event->type() == QEvent::KeyPress)
shootScreen();
return QMainWindow::eventFilter(obj, event);
}
tank you Jon Harper your codes was useful but i changed somethings there
your code just work when some key press event on my project form but i added paint method to your if and now it work on windows events too
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == this && QEvent::Paint == event->type()) {
shootScreen();
}
return false;
}
In Delphi I often made an OnAfterShow event for the main form. The standard OnShow() for the form would have little but a postmessage() which would cause the OnafterShow method to be executed.
I did this so that sometimes lengthy data loading or initializations would not stop the normal loading and showing of the main form.
I'd like to do something similar in a Qt application that will run on a desktop computer either Linux or Windows.
What ways are available to me to do this?
You can override showEvent() of the window and call the function you want to be called with a single shot timer :
void MyWidget::showEvent(QShowEvent *)
{
QTimer::singleShot(50, this, SLOT(doWork());
}
This way when the windows is about to be shown, showEvent is triggered and the doWork slot would be called within a small time after it is shown.
You can also override the eventFilter in your widget and check for QEvent::Show event :
bool MyWidget::eventFilter(QObject * obj, QEvent * event)
{
if(obj == this && event->type() == QEvent::Show)
{
QTimer::singleShot(50, this, SLOT(doWork());
}
return false;
}
When using event filter approach, you should also install the event filter in the constructor by:
this->installEventFilter(this);
I solved it without a timer using Paint event. Works for me at least on Windows.
// MainWindow.h
class MainWindow : public QMainWindow
{
...
bool event(QEvent *event) override;
void functionAfterShown();
...
bool functionAfterShownCalled = false;
...
}
// MainWindow.cpp
bool MainWindow::event(QEvent *event)
{
const bool ret_val = QMainWindow::event(event);
if(!functionAfterShownCalled && event->type() == QEvent::Paint)
{
functionAfterShown();
functionAfterShownCalled = true;
}
return ret_val;
}
Extraneous Information:
I am attempting to build an application using Qt. This application features a QMdiArea and a child-window. My child-window will have a menu which can be integrated into the QMdiArea or segregated and attached to the child itself. Though, this is a bit more detail than needed...
Problem:
I would like my child-widget to have a menu with a shortcut, "CTRL+W." But, because I am using a QMdiArea, the shortcut is already used causing:
QAction::eventFilter: Ambiguous shortcut overload: Ctrl+W
How can I get rid of this shortcut and claim it within my child widget instead?
Update:
Here is what I've tried with no luck:
class MDI : public QMdiArea
{
Q_OBJECT
private:
bool event(QEvent *tEvent)
{
if (tEvent->type() == QEvent::KeyPress)
{
QKeyEvent* ke = static_cast<QKeyEvent*>(tEvent);
if (ke->key()== Qt::Key_W && ke->modifiers() & Qt::ControlModifier)
emit KeyCW();
return true;
}
return QMdiArea::event(tEvent);
}
public:
signals:
void KeyCW();
};
This works if I do something as simple as change Qt::Key_W to Qt::Key_L. The key-combo is received and event is thrown. With W, it just never happens. I've also tried moving event to QMainWindow as well as an eventFilter in the subwindow to QMdiArea. It seems that it is a little overly complicated to do something as simple as remove default key-handlers from within QMdiArea.
You can disable this shortcut like this:
for( QAction *action : subWindow->systemMenu()->actions() ) {
if( action->shortcut() == QKeySequence( QKeySequence::Close ) ) {
action->setShortcut( QKeySequence() );
break;
}
}
You could get rid of the pre-defined close action of the QMdiSubWindow altogether by using Qt::CustomizeWindowHint as additional flag when adding the subwindow.
QMdiSubWindow *subWindow2 = mdiArea.addSubWindow(internalWidget2,
Qt::Widget | Qt::CustomizeWindowHint |
Qt::WindowMinMaxButtonsHint);
Subclass QMdiArea and reimplement keyPressEvent(). That should work.
void keyPressEvent(QKeyEvent* event){
if(event->key() == Qt::Key_W and event->modifiers() & Qt::ControlModifier){
// handle it
}else{
return QMdiArea::keyPressEvent(event);
}
}
You could also use event filters. I don't enough about your class hierarchy, but I hope you get the idea.
bool CustomMdiArea::eventFilter(QObject *object, QEvent *event){
if(object == yourChildWindow && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_W and keyEvent->modifiers() & Qt::ControlModifier) {
//handle it
return true;
}else{
return false;
}
}
return false;
}
From what I can tell, what I am looking for is not possible short of writing my own MDIArea.
The shortcut is set in QMdiSubWindowPrivate::createSystemMenu() during
the construction of a QMdiSubWindow, I doubt that you can remove it
without having to patch Qt libs.
Hopefully at some point someone will disprove this or QT will make changes. Meanwhile, it looks like we will all need to stay away from these pre-assigned shortcuts.
I was able to work around this by setting the shortcut context for my close action. By setting it to Qt::WidgetShortcut, I no longer get the ambiguous shortcut overload. Here is how I'm setting up my close action now:
closeAction = new QAction(tr("&Close"), this);
closeAction->setShortcut(Qt::CTRL|Qt::Key_W);
closeAction->setShortcutContext(Qt::WidgetShortcut);
connect(closeAction, SIGNAL(triggered()), mdiArea, SLOT(closeActiveSubWindow()));
I have a control with several QSpinBox objects inside a QScrollArea. All works fine when scrolling in the scroll area unless the mouse happens to be over one of the QSpinBoxes. Then the QSpinBox steals focus and the wheel events manipulate the spin box value rather than scrolling the scroll area.
I don't want to completely disable using the mouse wheel to manipulate the QSpinBox, but I only want it to happen if the user explicitly clicks or tabs into the QSpinBox. Is there a way to prevent the QSpinBox from stealing the focus from the QScrollArea?
As said in a comment to an answer below, setting Qt::StrongFocus does prevent the focus rect from appearing on the control, however it still steals the mouse wheel and adjusts the value in the spin box and stops the QScrollArea from scrolling. Same with Qt::ClickFocus.
In order to solve this, we need to care about the two following things:
The spin box mustn't gain focus by using the mouse wheel. This can be done by setting the focus policy to Qt::StrongFocus.
The spin box must only accept wheel events if it already has focus. This can be done by reimplementing QWidget::wheelEvent within a QSpinBox subclass.
Complete code for a MySpinBox class which implements this:
class MySpinBox : public QSpinBox {
Q_OBJECT
public:
MySpinBox(QWidget *parent = 0) : QSpinBox(parent) {
setFocusPolicy(Qt::StrongFocus);
}
protected:
virtual void wheelEvent(QWheelEvent *event) {
if (!hasFocus()) {
event->ignore();
} else {
QSpinBox::wheelEvent(event);
}
}
};
That's it. Note that if you don't want to create a new QSpinBox subclass, then you can also use event filters instead to solve this.
Try removing Qt::WheelFocus from the spinbox' QWidget::focusPolicy:
spin->setFocusPolicy( Qt::StrongFocus );
In addition, you need to prevent the wheel event from reaching the spinboxes. You can do that with an event filter:
explicit Widget( QWidget * parent=0 )
: QWidget( parent )
{
// setup ...
Q_FOREACH( QSpinBox * sp, findChildren<QSpinBox*>() ) {
sp->installEventFilter( this );
sp->setFocusPolicy( Qt::StrongFocus );
}
}
/* reimp */ bool eventFilter( QObject * o, QEvent * e ) {
if ( e->type() == QEvent::Wheel &&
qobject_cast<QAbstractSpinBox*>( o ) )
{
e->ignore();
return true;
}
return QWidget::eventFilter( o, e );
}
edit from Grant Limberg for completeness as this got me 90% of the way there:
In addition to what mmutz said above, I needed to do a few other things. I had to create a subclass of QSpinBox and implement focusInEvent(QFocusEvent*) and focusOutEvent(QFocusEvent*). Basically, on a focusInEvent, I change the Focus Policy to Qt::WheelFocus and on the focusOutEvent I change it back to Qt::StrongFocus.
void MySpinBox::focusInEvent(QFocusEvent*)
{
setFocusPolicy(Qt::WheelFocus);
}
void MySpinBox::focusOutEvent(QFocusEvent*)
{
setFocusPolicy(Qt::StrongFocus);
}
Additionally, the eventFilter method implementation in the event filter class changes its behavior based on the current focus policy of the spinbox subclass:
bool eventFilter(QObject *o, QEvent *e)
{
if(e->type() == QEvent::Wheel &&
qobject_cast<QAbstractSpinBox*>(o))
{
if(qobject_cast<QAbstractSpinBox*>(o)->focusPolicy() == Qt::WheelFocus)
{
e->accept();
return false;
}
else
{
e->ignore();
return true;
}
}
return QWidget::eventFilter(o, e);
}
My attempt at a solution. Easy to use, no subclassing required.
First, I created a new helper class:
#include <QObject>
class MouseWheelWidgetAdjustmentGuard : public QObject
{
public:
explicit MouseWheelWidgetAdjustmentGuard(QObject *parent);
protected:
bool eventFilter(QObject* o, QEvent* e) override;
};
#include <QEvent>
#include <QWidget>
MouseWheelWidgetAdjustmentGuard::MouseWheelWidgetAdjustmentGuard(QObject *parent) : QObject(parent)
{
}
bool MouseWheelWidgetAdjustmentGuard::eventFilter(QObject *o, QEvent *e)
{
const QWidget* widget = static_cast<QWidget*>(o);
if (e->type() == QEvent::Wheel && widget && !widget->hasFocus())
{
e->ignore();
return true;
}
return QObject::eventFilter(o, e);
}
Then I set the focus policy of the problematic widget to StrongFocus, either at runtime or in Qt Designer.
And then I install my event filter:
ui.comboBox->installEventFilter(new MouseWheelWidgetAdjustmentGuard(ui.comboBox));
Done. The MouseWheelWidgetAdjustmentGuard will be deleted automatically when the parent object - the combobox - is destroyed.
Just to expand you can do this with the eventFilter instead to remove the need to derive a new QMySpinBox type class:
bool eventFilter(QObject *obj, QEvent *event)
{
QAbstractSpinBox* spinBox = qobject_cast<QAbstractSpinBox*>(obj);
if(spinBox)
{
if(event->type() == QEvent::Wheel)
{
if(spinBox->focusPolicy() == Qt::WheelFocus)
{
event->accept();
return false;
}
else
{
event->ignore();
return true;
}
}
else if(event->type() == QEvent::FocusIn)
{
spinBox->setFocusPolicy(Qt::WheelFocus);
}
else if(event->type() == QEvent::FocusOut)
{
spinBox->setFocusPolicy(Qt::StrongFocus);
}
}
return QObject::eventFilter(obj, event);
}
With help from this post we cooked a solution for Python/PySide. If someone stumbles across this. Like we did :]
class HumbleSpinBox(QtWidgets.QDoubleSpinBox):
def __init__(self, *args):
super(HumbleSpinBox, self).__init__(*args)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
def focusInEvent(self, event):
self.setFocusPolicy(QtCore.Qt.WheelFocus)
super(HumbleSpinBox, self).focusInEvent(event)
def focusOutEvent(self, event):
self.setFocusPolicy(QtCore.Qt.StrongFocus)
super(HumbleSpinBox, self).focusOutEvent(event)
def wheelEvent(self, event):
if self.hasFocus():
return super(HumbleSpinBox, self).wheelEvent(event)
else:
event.ignore()
This is my Python PyQt5 port of Violet Giraffe answer:
def preventAnnoyingSpinboxScrollBehaviour(self, control: QAbstractSpinBox) -> None:
control.setFocusPolicy(Qt.StrongFocus)
control.installEventFilter(self.MouseWheelWidgetAdjustmentGuard(control))
class MouseWheelWidgetAdjustmentGuard(QObject):
def __init__(self, parent: QObject):
super().__init__(parent)
def eventFilter(self, o: QObject, e: QEvent) -> bool:
widget: QWidget = o
if e.type() == QEvent.Wheel and not widget.hasFocus():
e.ignore()
return True
return super().eventFilter(o, e)
Just to help anyone's in need, it lacks a small detail:
call focusInEvent and focusOutEvent from QSpinBox :
void MySpinBox::focusInEvent(QFocusEvent* pEvent)
{
setFocusPolicy(Qt::WheelFocus);
QSpinBox::focusInEvent(pEvent);
}
void MySpinBox::focusOutEvent(QFocusEvent*)
{
setFocusPolicy(Qt::StrongFocus);
QSpinBox::focusOutEvent(pEvent);
}