Event handling : Accessing another widget's property - qt

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;
}

Related

Memory leak from Promoted class of QDoubleSpinBox

I am promoting the QDoubleSpinBox class as i want to catch the mouseDoubleClick Event.
This is the Promoted class.
class SumDoubleBox : public QDoubleSpinBox
{
Q_OBJECT
public:
explicit SumDoubleBox(QWidget* parent = nullptr);
void setSingleStep(double val);
double singleStep() const;
void stepBy(int steps) override;
protected:
virtual void focusInEvent(QFocusEvent *e) override;
public slots:
void setZero();
void setOne();
signals:
int signalUndoRedo();
private:
double m_defaultStep = 1.0;
double m_CurrentStep;
bool m_stepUp;
};
SumDoubleBox::SumDoubleBox(QWidget* parent) : QDoubleSpinBox(parent)
{
SumLineEdit* lineEdit = new SumLineEdit(this);
setLineEdit(lineEdit);
setMinimum(0.0);
setMaximum(99999.0);
}
Since i am creating a pointer in the Constructor of the SumDoubleBox Class.
SumLineEdit* lineEdit = new SumLineEdit(this);
Do i need to Explicitly delete this in the Destructor ?
/////////////////////////////////////////////////////////////////
The Defination of the SumLineEdit class.
class SumLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit SumLineEdit(QWidget* parent = nullptr) { };
protected:
void mouseDoubleClickEvent(QMouseEvent* event) override;
};
void SumLineEdit::mouseDoubleClickEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
selectAll();
event->accept();
return;
}
QLineEdit::mouseDoubleClickEvent(event);
}
Since you've parented the new lineedit to the SumDoubleBox, it should get deleted with its parent.
There is, however, a more Qt-centric way to handle this case that I would strongly recommend you use or at least look into: Installing an event filter on the built-in line edit and handling the events for it. I've made many spinbox variants, and this approach usually works out best:
SumDoubleBox::SumDoubleBox( QWidget *parent ) :
QDoubleSpinBox( parent )
{
lineEdit()->installEventFilter( this );
}
bool SumDoubleBox::eventFilter( QObject *object, QEvent *event )
{
if( object == lineEdit() && event->type() == QEvent::MouseButtonDblClick )
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>( event );
if( mouseEvent->button() == Qt::LeftButton )
{
selectAll();
event->accept();
return true; //swallow the event
}
}
return false; //let the event through to the lineedit
}
This makes the custom class entirely redundant. It's nice because inheritance is a big hammer that easily gets out of control as a codebase scales up, and if there are other simpler ways to achieve your goal, it's often worth considering.

How to connect to mousePressEvent slot Qt

In Class Buttons, I have a btnRightClicked signal and a mousePressEvent slot:
void Buttons::mousePressEvent(QMouseEvent *e)
{
if(e->button() == Qt::RightButton) {
emit btnRightClicked();
}
}
And in mainwindow.cpp, I connect the btnRightClicked signal to onRightClicked slot like this:
connect(&mButtons, SIGNAL(btnRightClicked()), this, SLOT(onRightClicked()));
The onRightClicked slot is like this:
void MainWindow::onRightClicked()
{
qDebug() << "right clicked";
}
But I ran this program, nothing happened. I guess the reason is because I did not connect to the mousePressEvent slot. I am kind of new to Qt, I do not know if I am right or not.
I set up some buttons on the central widget, I want them to have the right clicked event when right click each of them. So how can I make this work?Thanks
Edit:
in button.h:
class Buttons : public QObject
{
Q_OBJECT
public:
Buttons();
QVector<QPushButton*> buttons;
void setButtons(int totalBtns) {
for(int i = 0; i < totalBtns; i++) {
buttons[i]->setObjectName(QString::number(i));
buttons[i]->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
}
}
public slots:
void mousePressEvent(QMouseEvent *e) {
if(e->button() == Qt::RightButton) {
emit btnRightClicked();
}
}
signals:
void btnRightClicked();
};
To get the mouse right click on your widget, you need to implement your own button widget.
class MyButton : public QPushButton
{
Q_OBJECT
public:
MyButton(QWidget *parent = Q_NULLPTR);
private slots:
void mousePressEvent(QMouseEvent *e);
signals:
void btnRightClicked();
};
cpp
MyButton:MyButton(QWidget * parent) :
QPushButton(parent)
{
}
void MyButton::mousePressEvent(QMouseEvent *e)
{
if(e->button()==Qt::RightButton)
emit btnRightClicked();
//this forwards the event to the QPushButton
QPushButton::mousePressEvent(e);
}
In your buttons class change the button vector to
QVector<MyButton*> buttons;
Then register the right click event of your MyButton to your signal in Buttons class then forwared the signal to your mainWindow
connect(&mButtons, &Buttons::btnRightClicked,
this, &MainWindow::onRightClicked);

How to create a SIGNAL for QTableWidget from keyboard?

I have a table and move around inside with left, right, up, down buttons. Now I need to create a SIGNAL when I stay in a certain cell and press SPACE button. This SIGNAL should bring also the coordinate of that cell. I tried with standard signals of QTableWidget but it does not work. How can I solve this?
Create a separate header file i.e. "customtable.h" and then in the Designer you can Promote the existing QTableWidget to this class.
class customTable:public QTableWidget
{
Q_OBJECT
public:
customTable(QWidget* parent=0):QTableWidget(parent){}
protected:
void keyPressEvent(QKeyEvent *e)
{
if(e->key()==Qt::Key_Space)
{
emit spacePressed(this->currentRow(),this->currentColumn());
}
else { QTableWidget::keyPressEvent(e); }
}
signals:
spacePressed(int r, int c);
};
You can use an event filter to do this:
class TableSpaceWatcher : public QObject {
Q_OBJECT
bool eventFilter(QObject * receiver, QEvent * event) override {
auto table = qobject_cast<QTableWidget*>(receiver);
if (table && event->type() == QEvent::KeyPress) {
auto keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Space)
emit spacePressed(table->currentRow(), table->currentColumn());
}
return false;
}
public:
using QObject::QObject;
Q_SIGNAL void spacePressed(int row, int column);
void installOn(QTableWidget * widget) {
widget->installEventFilter(this);
}
};
QTableWidget table;
TableSpaceWatcher watcher;
watcher.installOn(&table);

Qt rightclick QPushButton

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)

MousePressEvent in view and items in Qt

I have a custom QGraphicsView and a custom QGraphicsItem. I want the Item to handle my click if I click in the item, else I want the click to be handled by the View.
But when I click on the item, the item handles the click. This is ok. But if I click somewhere else the click isn't handled at all. All the code in my classes that have anything to do with mouseEvents is below.
class CustomView : public QGraphicsView
{
Q_OBJECT
public:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
};
void CustomView::mousePressEvent(QGraphicsSceneMouseEvent *event){
cout << "pressing in view";
}
class CustomItem : public QGraphicsItem
{
public:
CustomItem(CustomView* widget)
void mousePressEvent(QGraphicsSceneMouseEvent *event);
};
CustomItem::CustomItem(CustomView* widget){
setFlag(ItemIsSelectable);
setFlag(ItemIsMovable);
}
void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
cout << "pressing in item";
}
It seems that when I remove the mousePressEvent function from the CustomItem class and change in the CustomView the mousePressEvent function to:
void CustomView::mousePressEvent(QMouseEvent *event){
cout << "pressing in view";
}
the CustomView handles all the mouseEvents.
How can I let the CustomItem handles the clicks in the items and the CustomView handle all the other clicks?
Thank you.
EDIT
So now I have changed it to:
class CustomView : public QGraphicsView
{
Q_OBJECT
public:
};
class CustomScene : public QGraphicsScene
{
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
};
void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event){
cout << "pressing in scene";
}
class CustomItem : public QGraphicsItem
{
public:
CustomItem(CustomView* widget)
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
};
CustomItem::CustomItem(CustomView* widget){
setFlag(ItemIsSelectable);
setFlag(ItemIsMovable);
}
void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
cout << "pressing in item";
}
A Click in the scene but not in an item get handled by the scene. But clicks in the items
dont get handled by the items itself, instead it get handled by the scene. Unless if you click 2 times really fast on the items it gets handled by the scene and the item.
Any ideas?
QGraphicsView isn't really a good place to handle scene-specific events. Instead, you'll need to override QGraphicsScene::mousePressEvent. I would recommend something like this:
void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (itemAt(event) == NULL)
{
// do stuff if not clicked on an item
}
else
{
QGraphicsScene::mousePressEvent(event); // this forwards the event to the item
}
}
Note that QGraphicsScene::itemAt might give you difficulties if you have items with the ItemIgnoresTransformations flag set to true. Look to the docs for QGraphicsScene::itemAt for how to resolve this.

Resources