Is there a way to catch resize events for docks in Qt? I need to be able to detect when a dock is resized (and not only when its location or 'features' change). It looks like there is no 'resized' signal for QDockWidget.
If you do not wish to subclass to just get resize event control, you can installEventFilter
Small example would look like (MainWindow.h): (MainWindow holds DockWidget Here)
protected:
bool eventFilter(QObject *obj, QEvent *event);
and in (MainWindow.cc):
MainWindow::MainWindow(QWidget* parent)
: QWidget(parent) {
ui_.setupUi(this);
ui_.dockWidget->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
if (event->type() == QEvent::Resize && obj == ui_.dockWidget) {
QResizeEvent *resizeEvent = static_cast<QResizeEvent*>(event);
qDebug("Dock Resized (New Size) - Width: %d Height: %d",
resizeEvent->size().width(),
resizeEvent->size().height());
}
return QWidget::eventFilter(obj, event);
}
Related
I want a text to be displayed persistently on the curser event when the cursor is moving, not depending on the cursor position. I used Qtooltip for this purpose. This is the code to show the text:
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
// ...
}
bool Widget::event (QEvent *ev)
{
if (event->type() == QEvent::ToolTip) {
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(ev);
QToolTip::showText(helpEvent->globalPos(), "Something got it");
return false;
}
return QWidget::event(ev);
}
But when I run this code the text is not displayed consistently and it shows up only sometimes, disappears while moving the cursor, and the whole window flickers.
You can probably achieve what you want by intercepting mouse move events rather than the tool tip notifications...
class tooltip_event_filter: public QLabel {
using super = QLabel;
public:
tooltip_event_filter ()
{
setWindowFlags(windowFlags()
| Qt::BypassWindowManagerHint
| Qt::FramelessWindowHint
);
}
protected:
virtual bool eventFilter (QObject *obj, QEvent *event) override
{
if (event->type() == QEvent::MouseMove) {
/*
* Note the QPoint(1, 0) offset here. If we don't do that then the
* subsequent call to qApp->widgetAt(QCursor::pos()) will return a
* pointer to this widget itself.
*/
move(QCursor::pos() + QPoint(1, 0));
if (const auto *w = qApp->widgetAt(QCursor::pos())) {
setText(QString("widget#%1").arg((qulonglong)w));
show();
} else {
hide();
}
}
return super::eventFilter(obj, event);
}
};
Then install an instance of tooltip_event_filter on the application instance...
tooltip_event_filter tooltip_event_filter;
qApp->installEventFilter(&tooltip_event_filter);
The example shown simply displays the address of the widget under the mouse pointer as it's moved.
OK, this should be easy.
I tried to handle drop event onto a QGraphicsView widget. Incoming data dragged from a QTreeView widget. For that, I re-implemented these methods:
void QGraphicsScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
event.accept();
}
void QGraphicsScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
event.accept();
}
void QGraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
event.accept();
}
void QGraphicsView::dropEvent(QDropEvent *event)
{
QPixmap pixmap(event->mimedata()->urls()[0].toString().remove(0,8));
this.scene()->addPixmap(pixmap);
}
This works fine; but how can I change another graphicsview scene within this widget's drop event? That is:
void QGraphicsView::dropEvent(QDropEvent *event)
{
QPixmap pixmap(event->mimedata()->urls()[0].toString().remove(0,8));
// I cannot access ui; and cannot access my widgets...:
ui->anotherview->scene()->addPixmap(pixmap);
}
What about making a custom signal in your QGraphicsView like void showPixmap(QPixmap p) and connecting it to a slot in your main gui class where you can access ui elements. You can then call emit showPixamp(pixmap) in the dropEvent.
Subclassing QGraphicsView
//header file
class CustomView : public QGraphicsView
{
public:
CustomView(QGraphicsScene*, QWidget*=NULL);
~CustomView();
signals:
void showPixmap(QPixmap p);
protected:
virtual void dropEvent(QDropEvent *event);
};
//cpp file
CustomView::CustomView(QGraphicsScene *scene, QWidget* parent)
:QGraphicsView(scene, parent)
{
//if you need to initialize variables, etc.
}
void CustomView::dropEvent(QDropEvent *event)
{
//handle the drop event
QPixmap mPixmap;
emit showPixmap(mPixmap);
}
Using event filters in your main GUI class
void GUI::GUI()
{
ui->mGraphicsView->installEventFilter(this);
}
bool GUI::eventFilter(QObject *object, QEvent *event)
{
if (object == ui->mGraphicsView && event->type() == QEvent::DropEnter) {
QDropEvent *dropEvent = static_cast<QDropEvent*>(event);
//handle the drop event
return true;
}
else
return false;
}
I'm using Qt Creator to create a gui for a mineseeper game.
How can I know a QpushButton clicked with rightclick? for flag in the game.
In other word, which signal used for rightclick?
Create your own button with filter at mousePressEvent slot.
qrightclickbutton.h
#ifndef QRIGHTCLICKBUTTON_H
#define QRIGHTCLICKBUTTON_H
#include <QPushButton>
#include <QMouseEvent>
class QRightClickButton : public QPushButton
{
Q_OBJECT
public:
explicit QRightClickButton(QWidget *parent = 0);
private slots:
void mousePressEvent(QMouseEvent *e);
signals:
void rightClicked();
public slots:
};
#endif // QRIGHTCLICKBUTTON_H
qrightclickbutton.cpp
#include "qrightclickbutton.h"
QRightClickButton::QRightClickButton(QWidget *parent) :
QPushButton(parent)
{
}
void QRightClickButton::mousePressEvent(QMouseEvent *e)
{
if(e->button()==Qt::RightButton)
emit rightClicked();
}
Now connect like this
QRightClickButton *button = new QRightClickButton(this);
ui->gridLayout->addWidget(button);
connect(button, SIGNAL(rightClicked()), this, SLOT(onRightClicked()));
Create a slot in MainWindow.cpp.
void MainWindow::onRightClicked()
{
qDebug() << "User right clicked me";
}
It works for me!
I think QPushButton is internally implemented to listen to left mouse clicks only. But you can easily extend QPushButton and re-implement let's say the mouse release event and do your thing if the right mouse button was pressed, e.g. emit a custom rightClicked() signal for example:
signals:
void rightClicked();
protected:
void mouseReleaseEvent(QMouseEvent *e) {
if (e->button() == Qt::RightButton) emit rightClicked();
else if (e->button() == Qt::LeftButton) emit clicked();
}
... or you can create an overload of the clicked signal that forwards the mouseEvent pointer so you can do the same check outside of the button.
signals:
void clicked(QMouseEvent *);
protected:
void mouseReleaseEvent(QMouseEvent *e) {
emit clicked(e);
}
Then you do the check in the slot you connect the button's clicked(QMouseEvent *) signal to and proceed accordingly.
I just wrote this little helper adapter to make any existing button right-clickable with no need to subclass it:
class CRightClickEnabler : public QObject
{
public:
CRightClickEnabler(QAbstractButton * button): QObject(button), _button(button) {
button->installEventFilter(this);
};
protected:
inline bool eventFilter(QObject *watched, QEvent *event) override {
if (event->type() == QEvent::MouseButtonPress)
{
auto mouseEvent = (QMouseEvent*)event;
if (mouseEvent->button() == Qt::RightButton)
_button->click();
}
return false;
}
private:
QAbstractButton* _button;
};
Usage:
connect(ui->pushButton, &QPushButton::clicked, [](){qDebug() << "Button clicked";});
new CRightClickEnabler(ui->pushButton);
From now on, the clicked signal will be triggered by the right click as well as left click. There's no need to delete this object - it uses ui->pushButton as parent and will be auto-deleted by Qt when the parent is destroyed.
Obviously, you can write 2 lines of code (literally) to declare a new signal here and emit that signal upon right click instead of clicked, if desired.
I'd like to suggest this option as well, without need for event filter/other stuffs...
self.button.released.connect(self.doStuff)
self.button.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.button.customContextMenuRequested.connect(partial(self.doStuff, False))
def doStuff(self,state=True,p=QPoint()):
print("True for left, False for right!",state)
I have a widget class subclass of QMainWindow, and it has a central widget(QWidget), and in the overriden paintEvent function, can I create an instance of QPainter on this central widget? Code like:
void MyMainWindow::paintEvent(QEvent *event)
{
QPainter painter(_theCentralWidget);
//drawing...
return QMainWindow::paintEvent(event);
}
I don't want to create a new c++ class subclass of QWidget and override its paintEvent function and then replace the original central widget with this new one...
(I did like above but an error occured saying the painter is not active...)
Well. If you really, really, really don't want to sub-class the central widget, you can install a event filter to it and handle the paint event for it.
http://qt-project.org/doc/qt-4.8/qobject.html#installEventFilter
You can use C++11 lambdas to solve this problem. Create a new generic "event for anything" QObject that passes its filtering through a lambda you specify. Then add this generic object to your display widget with the desired logic. For example:
generic-qevent-filter.hpp:
#pragma once
class GenericQEventFilter : public QObject
{
Q_OBJECT
public:
GenericQEventFilter(QObject *parent, std::function<bool (QObject *obj, QEvent *event)> event_filter_f);
std::function<bool (QObject *obj, QEvent *event)> event_filter_f;
protected:
bool eventFilter(QObject *obj, QEvent *event);
};
generic-qevent-filter.cpp:
#include "generic-qevent-filter.hpp"
GenericQEventFilter::GenericQEventFilter(QObject *parent,
std::function<bool (QObject *obj, QEvent *event)> event_filter_f)
: QObject(parent), event_filter_f(event_filter_f)
{
}
bool GenericQEventFilter::eventFilter(QObject *obj, QEvent *event)
{
return this->event_filter_f(obj, event);
}
And you use this in your code as follows:
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
{
ui = new Ui_MainWindow();
ui->setupUi(this); // Initialise widgets
this->wire_up_gui(); // Connect signals and slots
ui->displayWidget->installEventFilter(new GenericQEventFilter(this, [&] (QObject *obj, QEvent *event) {
if(event->type() == QEvent::Paint) {
paint_display_widget(obj, event);
return true;
}
return false;
}));
}
"Warning: When the paintdevice is a widget, QPainter can only be used inside a paintEvent() function or in a function called by paintEvent(); that is unless the Qt::WA_PaintOutsidePaintEvent widget attribute is set. On Mac OS X and Windows, you can only paint in a paintEvent() function regardless of this attribute's setting."
From: QT Docu
I am trying to get the scene coordinates of a both a mousepress and a mouserelease event by means of an event filter.
It works fine for the mousepress, but the mouserelease seems not to be registering.
What am I missing here?
Please note I am using Qt Creator 2.2.0(based on Qt 4.7.4)
class mouseEater : public QObject{
// Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event);
};
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mouseEater *eater = new mouseEater();
QGraphicsScene *scene;
scene = new QGraphicsScene();
ui->view->installEventFilter(eater);
ui->view->setScene(scene);
}
bool mouseEater::eventFilter(QObject *obj, QEvent *event){
QMouseEvent *mEvent = static_cast<QMouseEvent *>(event);
QPoint point = mEvent->pos();
cout<<"type:"<<event->type()<<endl;
switch(event->type()){
case QEvent::MouseButtonPress:
cout<<"mousepress at "<<point.x()<<","<<point.y()<<endl;
return true;
break;
case QEvent::MouseButtonRelease:
cout<<"mouserelease at "<<point.x()<<","<<point.y()<<endl;
return true;
break;
default:
//standard event processing
return QObject::eventFilter(obj, event);
break;
}
return 0;
}
The quick answer is that QGraphicsView doesn't seem to allow other QObjects to eat its mouseReleaseEvents. I'm not sure why just yet, but I think it's something to do with how mouseReleaseEvent() is reimplemented in QGraphicsView. A little hacking gives a workaround though:
HEADER:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
virtual bool eventFilter(QObject *, QEvent *);
private:
Ui::MainWindow *ui;
};
class cDerived : public QGraphicsView
{
Q_OBJECT
public:
cDerived(QWidget *parent) : QGraphicsView(parent) {}
protected:
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
};
SOURCE:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGraphicsView *View = new cDerived(ui->centralWidget);
QGraphicsScene *Scene = new QGraphicsScene(ui->centralWidget->rect(), ui->centralWidget);
View->setScene(Scene);
View->installEventFilter(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
switch(event->type())
{
case QEvent::MouseButtonPress:
qDebug()<<"eaten mousepress";
return true;
break;
case QEvent::MouseButtonRelease:
qDebug()<<"eaten mouserelease";
return true;
break;
default:
//standard event processing
return QObject::eventFilter(obj, event);
break;
}
}
void cDerived::mousePressEvent(QMouseEvent *event)
{
qDebug()<<"mousepress";
event->ignore();
}
void cDerived::mouseReleaseEvent(QMouseEvent *event)
{
qDebug()<<"mouserelease";
event->ignore();
}
Try commenting out the 'event->ignore()' calls in the reimplemented mouse event catchers and it becomes clear how the mouseReleaseEvent() in QGraphicsView might be going wrong...
If you can reimplement QGraphicsView as a very thin class with just the constructor and mouseReleaseEvent() { event->ignore() } then you should be able to get around this bug (though, while this hack seems to be stable - suggesting that your problem might be a bug in QGraphicsView, without exhaustively looking through the QGraphicsView source we can't be sure this isn't in fact an intended feature and therefore that this hack won't break anything down the line!)
-Sam
Use QGraphicsSceneMouseEvent and the likes instead of QMouseEvent. As I found out in my question, QGraphicsScene does not dispatch normal mouse events.
Best regards