How can I make a QWidget semitransparent to events? - qt

I've seen similar questions but no answers that fit what I need. I want an invisible widget that lives on top of my whole application (no problems here). I want this widget to catch events so that I can print stuff about them, record them, whatever. I currently have an event filter hooked up that does this just fine. Then I want it to let the event go through to whatever is behind the widget. So for instance, if I try to push a button, the invisible widget should notice that a press happened on that spot, and then the button should actually be pressed. Can this be done in a simple way, or am I going to have to write code to simulate all the events beneath the invisible widget?

From all the information you disclosed in the comments, I suggest you filter the event as previously discussed, and then use QCoreApplication::sendEvent to forward the desired events to the invisible widget. It will then propagate the event accordingly to its children.
EDIT: OK, here is quick example that includes a QObject based event filter, that will filter the events for a widget, if the event is mouse event, it will be left for the widget to handle and print the output, if the event is a key event, it will be filtered and not forwarded back to the widget:
The event filter class:
class EventInfo : public QObject {
Q_OBJECT
public:
explicit EventInfo(QObject *parent = 0) : QObject(parent) {}
bool eventFilter(QObject *, QEvent *e) {
if (e->type() == QEvent::MouseButtonRelease){
qDebug() << "click event not filtered";
return false;
}
if (e->type() == QEvent::KeyRelease) {
QKeyEvent *event = static_cast<QKeyEvent *>(e);
if (event) qDebug() << "key" << event->key() << "filtered";
return true;
}
return false;
}
};
The widget:
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = 0) : QWidget(parent) {}
protected:
void mouseReleaseEvent(QMouseEvent *e) {
qDebug() << "widget clicked at position" << e->pos();
}
void keyReleaseEvent(QKeyEvent *e) {
qDebug() << "pressed key" << e->key();
}
};
main.cpp:
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
Widget w;
EventInfo info;
w.installEventFilter(&info);
w.show();
return a.exec();
}
Testing output to show that keyboard events are filtered and mouse press events are forwarded to the widget:
click event not filtered
widget clicked at position QPoint(352,230)
key 70 filtered
click event not filtered
widget clicked at position QPoint(405,163)
key 87 filtered

Related

Can't catch TapAndHoldGesture

I grabGesture()ed one of my buttons:
buttons[0]->grabGesture(Qt::TapAndHoldGesture);
in the constructor, and declared:
bool event(QEvent *event);
in protected slots, and implemented it like this:
bool MyClass::event(QEvent *event)
{
if (event->type() == QEvent::Gesture){
QGestureEvent *gestevent = static_cast<QGestureEvent *>(event);
if (QGesture *gest = gestevent->gesture(Qt::TapAndHoldGesture)){
QTapAndHoldGesture *tapgest = static_cast<QTapAndHoldGesture *>(gestevent->gesture(Qt::TapAndHoldGesture));
cout << "grabbed a gesture event" << endl;
}
return true;
}
cout << "not a gesture event" << endl;
return QWidget::event(event);
}
and I keep getting "not a gesture event" printed to screen however I press (normal press / long press / ... )
What I'm trying to do is a long key press (from the keyboard)
It's said in the Qt Documentation:
A gesture could be a particular movement of a mouse, a touch screen
action, or a series of events from some other source. The nature of
the input, the interpretation of the gesture and the action taken are
the choice of the developer.
So I suppose also a keyboard can trigger QGesture events.
If the class handling the grap (MyClass) event is not the class where the gesture is detected on (QPushButton assuming buttons[0] is a QPushButton), then you need and event filter:
buttons[0]->grabGesture(Qt::TapAndHoldGesture);
buttons[0]->installEventFilter( myClass ); // myClass being a MyClass instance
Now, myClass object will be forwarded all events from buttons[0], this is done using QObject::eventFilter virtual function:
bool MyClass::eventFilter(QObject *obj, QEvent *event)
{
if ( event->type() == QEvent::Gesture && obj == buttons[0] )
{
QGestureEvent *gestevent = static_cast<QGestureEvent *>(event);
if (QGesture *gest = gestevent->gesture(Qt::TapAndHoldGesture)){
QTapAndHoldGesture *tapgest = static_cast<QTapAndHoldGesture *>(gestevent->gesture(Qt::TapAndHoldGesture));
cout << "grabbed a gesture event" << endl;
return true;
}
}
// standard event processing
return Parent::eventFilter(obj, event); // Parent being MyClass parent type, maybe QDialog or QWidget
}

QLabel click event using Qt?

I'm new in Qt and have a question.
I have QLabel and QLineEdit objects, and when QLabel text is clicked on, I want to set this text in QLineEdit.
Also I have read that QLabel has not clicked signal.
Can you explain how can I do this and write code for me ?!
Either style another type of QWidget such as a specific QPushButton to look like a QLabel and use its clicked() signal or inherit QLabel yourself and emit your own clicked() signal.
See this example:
https://wiki.qt.io/Clickable_QLabel
If you choose the latter option you can pass the text in the signal. Then connect the necessary signals/slots up between the QLabel and the QLineEdit like so:
QObject::connect(&label, SIGNAL(clicked(const QString& text)),
&lineEdit, SLOT(setText(const QString& text)));
A simple way to accomplish that, without a need for any subclassing, is a signal source that monitors the events on some object and emits relevant signals:
// main.cpp - this is a single-file example
#include <QtWidgets>
class MouseButtonSignaler : public QObject {
Q_OBJECT
bool eventFilter(QObject * obj, QEvent * ev) Q_DECL_OVERRIDE {
if ((ev->type() == QEvent::MouseButtonPress
|| ev->type() == QEvent::MouseButtonRelease
|| ev->type() == QEvent::MouseButtonDblClick)
&& obj->isWidgetType())
emit mouseButtonEvent(static_cast<QWidget*>(obj),
static_cast<QMouseEvent*>(ev));
return false;
}
public:
Q_SIGNAL void mouseButtonEvent(QWidget *, QMouseEvent *);
MouseButtonSignaler(QObject * parent = 0) : QObject(parent) {}
void installOn(QWidget * widget) {
widget->installEventFilter(this);
}
};
The emit keyword is an empty macro, Qt defines it as follows:
#define emit
It is for use by humans as a documentation aid prefix only, the compiler and moc ignore it. As a documentation aid, it means: the following method call is a signal emission. The signals are simply methods whose implementation is generated for you by moc - that's why we have to #include "main.moc" below to include all the implementations that moc has generated for the object class(es) in this file. There's otherwise nothing special or magical to a signal. In this example, you could look in the build folder for a file called main.moc and see the implementation (definition) of void MouseButtonSignaler::mouseButtonEvent( .. ).
You can then install such a signaler on any number of widgets, such as a QLabel:
int main(int argc, char ** argv) {
QApplication app(argc, argv);
MouseButtonSignaler signaler;
QWidget w;
QVBoxLayout layout(&w);
QLabel label("text");
QLineEdit edit;
layout.addWidget(&label);
layout.addWidget(&edit);
signaler.installOn(&label);
QObject::connect(&signaler, &MouseButtonSignaler::mouseButtonEvent,
[&label, &edit](QWidget*, QMouseEvent * event) {
if (event->type() == QEvent::MouseButtonPress)
edit.setText(label.text());
});
w.show();
return app.exec();
}
#include "main.moc"
You need to create one Custom Label class, which will inherit QLabel. Then you can use MouseButtonRelease event to check clicking of Label and emit your custom signal and catch in one SLOT.
Your .h file will be as below:
class YourLabelClass : public QLabel{
signals:
void myLabelClicked(); // Signal to emit
public slots:
void slotLabelClicked(); // Slot which will consume signal
protected:
bool event(QEvent *myEvent); // This method will give all kind of events on Label Widget
};
In your .cpp file, your constructor will connect signal & slot as below :
YourLabelClass :: YourLabelClass(QWidget* parent) : QLabel(parent) {
connect(this, SIGNAL(myLabelClicked()), this, SLOT(slotLabelClicked()));
}
Remaining event method and SLOT method will be implemented as below:
bool YourLabelClass :: event(QEvent *myEvent)
{
switch(myEvent->type())
{
case(QEvent :: MouseButtonRelease): // Identify Mouse press Event
{
qDebug() << "Got Mouse Event";
emit myLabelClicked();
break;
}
}
return QWidget::event(myEvent);
}
void YourLabelClass :: slotLabelClicked() // Implementation of Slot which will consume signal
{
qDebug() << "Clicked Label";
}
For Changing a Text on QLineEdit, you need to create a Custom Class and share object pointer with custom QLabel Class. Please check test code at this link
In the above example the header needs Q_OBJECT:
class YourLabelClass : public QLabel{
Q_OBJECT
signals:

Drag and drop of QGraphicsItem between two pairs of QGraphicsScene/QGraphicsView

I have an application with a mainwindow in which I create a QGraphicsScene like this:
DiagramWindow::DiagramWindow()
{
scene = new QGraphicsScene(0, 0, 600, 500);
Then on the same program, I'm calling another window with another (different)QGraphicsScene. Both of these scenes have their respective QGraphicsViews and I use the same custom class to draw QGraphicsItem in each scene/window.
Now I'm trying to implement drag and drop between the two scenes/windows using this and I'm getting an effect that I think is similar/the same as in this SO question
. Basically, when I drag a QGraphicsItem from the second window/scene to the main window, it does not trigger the event on the scene, BUT it does trigger in in the main window's toolbar/ borders.
My event handling functions are:
void DiagramWindow::dragEnterEvent(QDragEnterEvent *event)
{
qDebug() << "I'm on the main window!";
event->acceptProposedAction();
}
and
void DiagramWindow::dropEvent(QDropEvent *event)
{
event->acceptProposedAction();
qDebug() << "got a drop!";
}
According to the answers there, I would have to setAcceptDrops() in a QGraphicsScene (which is not possible), so the trick seems to be to overload the QGraphicsScene::dragMoveEvent(). Since I don't have a specific class for my QGraphicsScene (just for it's parent DiagramWindow), I don't know how I can write a function to target the scene's specific dragMoveEvent().
QUESTION 1 I was hoping I could do something like:
DiagramWindow->scene::dragMoveEvent()
{
...
}
But of course this is not possible. I'm really new to C++/Qt and the general workflow/syntax dynamics still ellude me. How can I target the QGraphicsScene inside my MainWindow to write the event handling function?
QUESTION 2 Also, I noticed that by rewriting these event handling functions, I (obviously) lost most of the funcionality I had in the main window - selecting and moving around the QGraphicsItems no longer works. Is there anyway I can make these events trigger only if the events are being originated in the second window? I have looked at QDrag->source() but I'm not getting how it works either - something like, if the events originate in the second window, do this, else, keep doing what you were doing before - which I don't actually know what is... :)
Question1
If the event is received by the diagramWindow, and want it receive by the scene which is currently displayed by a view, then what you should do is pass the event to the view, that will convert it to a QGraphicsSceneDragDropEvent and redirect it to the scene:
void DiagramWindow::dragMoveEvent(event)
{
view->dragMoveEvent(event);
}
Question 2
Don't know much about Drag event so can't help, but to get the previous behaviour depending on a if statement, you should do:
void MyDerivedClass::myEvent(event)
{
if(...)
// do specific behaviour
else
QBaseClass::myEvent(event); // default behaviour
}
assuming your class MyDerivedClass(in your case, DiagramWindow) inherits from the Qt class QBaseClass (in your case, QMainWindow?), and the event method you want to override is myEvent() (in your case, dragMoveEvent).
Working example:
I don't know exacly what your class DiagramWindow is, but here is a working example that should give you all the necessary ideas to make it work in your case. I suggest you start from this working example, and modify it to get what you need.
main.cpp:
#include <QApplication>
#include "Scene.h"
#include "View.h"
int main(int argc, char * argv[])
{
QApplication app(argc,argv);
Scene * scene1 = new Scene(1);
View * view1 = new View(scene1, 1);
Scene * scene2 = new Scene(2);
View * view2 = new View(scene2,2);
view1->show();
view2->show();
return app.exec();
}
Scene.h:
#ifndef SCENE_H
#define SCENE_H
#include <QGraphicsScene>
#include <QGraphicsEllipseItem>
class Item;
class Item: public QGraphicsEllipseItem
{
public:
Item(int x,int y);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
};
class Scene: public QGraphicsScene
{
public:
Scene(int i);
protected:
virtual void dragEnterEvent ( QGraphicsSceneDragDropEvent * event );
virtual void dragLeaveEvent ( QGraphicsSceneDragDropEvent * event );
virtual void dragMoveEvent ( QGraphicsSceneDragDropEvent * event );
virtual void dropEvent ( QGraphicsSceneDragDropEvent * event );
int i;
};
#endif
Scene.cpp:
#include "Scene.h"
#include <QtDebug>
#include <QGraphicsSceneMouseEvent>
#include <QDrag>
#include <QMimeData>
Item::Item(int x, int y) : QGraphicsEllipseItem(x,y,50,50) {}
void Item::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "item mouse press";
// Create the mime data that will be transfered from one scene
// to another
QMimeData * mimeData = new QMimeData;
// In our case, the data will be the address of the item.
//
// Note: This is UNSAFE, and just for the sake of example. The
// good way to do it is to create your own mime type, containing
// all the information necessary to recreate an identical Item.
//
// This is because drag and drop is meant to work between
// applications, and the address of your item is not accessible
// by other applications (deferencing it would produce a
// segfault). It works fine in this case since you perform a
// drag and drop between different windows of the same
// application.
Item * item = this;
QByteArray byteArray(reinterpret_cast<char*>(&item),sizeof(Item*));
mimeData->setData("Item",byteArray);
// start the event
QDrag * drag = new QDrag(event->widget());
drag->setMimeData(mimeData);
drag->start();
}
Scene::Scene(int i) : i(i)
{
Item * item = new Item(100+100*i,100);
addItem(item);
}
void Scene::dragEnterEvent ( QGraphicsSceneDragDropEvent * event )
{
qDebug() << "scene" << i << "drag enter";
}
void Scene::dragLeaveEvent ( QGraphicsSceneDragDropEvent * event )
{
qDebug() << "scene" << i << "drag leave";
}
void Scene::dragMoveEvent ( QGraphicsSceneDragDropEvent * event )
{
qDebug() << "scene" << i << "drag move";
}
void Scene::dropEvent ( QGraphicsSceneDragDropEvent * event )
{
qDebug() << "scene" << i << "drop";
// retrieve the address of the item from the mime data
QByteArray byteArray = event->mimeData()->data("Item");
Item * item = *reinterpret_cast<Item**>(byteArray.data());
// add the item to the scene (automatically remove it from the
// other scene)
addItem(item);
}
View.h:
#ifndef VIEW_H
#define VIEW_H
#include <QGraphicsView>
#include "Scene.h"
class View: public QGraphicsView
{
public:
View(Scene * scene, int i);
protected:
virtual void dragEnterEvent ( QDragEnterEvent * event );
virtual void dragLeaveEvent ( QDragLeaveEvent * event );
virtual void dragMoveEvent ( QDragMoveEvent * event );
virtual void dropEvent ( QDropEvent * event );
private:
Scene * scene_;
int i;
};
#endif
View.cpp:
#include "View.h"
#include <QtDebug>
View::View(Scene * scene, int i) :
QGraphicsView(scene),
scene_(scene),
i(i)
{
}
void View::dragEnterEvent ( QDragEnterEvent * event )
{
qDebug() << "view" << i << "drag enter";
QGraphicsView::dragEnterEvent(event);
}
void View::dragLeaveEvent ( QDragLeaveEvent * event )
{
qDebug() << "view" << i <<"drag leave";
QGraphicsView::dragLeaveEvent(event);
}
void View::dragMoveEvent ( QDragMoveEvent * event )
{
qDebug() << "view" << i << "drag move";
QGraphicsView::dragMoveEvent(event);
}
void View::dropEvent ( QDropEvent * event )
{
qDebug() << "view" << i << "drop";
QGraphicsView::dropEvent(event);
}
In your case, if you need to explicitly call view->someDragDropEvent(event) from your DiagramWindow, then you just have to change the protected: to public:. But I don't think it is necessary, just try without reimplementing the drag and drop event in DiagramWindow, and it should automatically call the one of your view.

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)

Need help on Event handling to signalling in QTcreator

So basically what I am trying to do is the following:
I want to create a directional arrows pad on the screen. When the user presses the up or 8 key, the UI should react as if I clicked the up button. I have googled and searched all over, but as I just started using QTCreator (and C++), I am very inexperienced and any help would be appreciated.
So far I have
class GamePadWidget : public QWidget
{
public:
GamePadWidget(QWidget *parent = 0);
protected:
virtual void keyPressEvent(QKeyEvent *event);
};
GamePadWidget::GamePadWidget(QWidget *parent)
: QWidget(parent)
{
int buttonWidth = 75;
int buttonHeight = 75;
QPushButton *down = new QPushButton(("Y-"), this);;
down->setGeometry(100, 200, 100, 100);
QIcon downicon;
downicon.addFile(QString::fromUtf8("C:/arrows/Aiga_downarrow.png"), QSize(),QIcon::Normal, QIcon::Off);
down->setIcon(downicon);
down->setIconSize(QSize(buttonWidth,buttonHeight));
down->setFocusPolicy(Qt::NoFocus);
QPushButton *up = new QPushButton(("Y+"), this);;
up->setGeometry(100, 50, 100, 100);
QIcon upicon;
upicon.addFile(QString::fromUtf8("C:/arrows/Aiga_uparrow.png"), QSize(),QIcon::Normal, QIcon::Off);
up->setIcon(upicon);
up->setIconSize(QSize(buttonWidth,buttonHeight));
up->setFocusPolicy(Qt::NoFocus);
}
void GamePadWidget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_8 || event->key() == Qt::Key_Up ) {
printf("key event in board");
}
else if (event->key() == Qt::Key_9 || event->key() == Qt::Key_Down ) {
qApp->quit();
}
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
GamePadWidget widget;
widget.show();
return app.exec();
}
with my current code, if I press down or 2, the app exits as expected, yet here is part in which I am stuck at.
I want the same functionality as if I pressed the down (or up key), the pushbutton should light up briefly then shoot off a signal to who knows where
I realize it should have something to do with connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));
but cannot quite wrap my mind / find it.
Thank you for your time.
You can call a slot on an object as if it was a normal method (which it is as far as C++ is concerned). Obv you'll need to make your pushButton a member though, so you have access to it outside of the constructor.
Then yes, just connect the button's clicked() signal to the app's quit() slot. The code below should work for you (not tested though):
GamePadWidget::GamePadWidget(QWidget *parent)
: QWidget(parent)
{
...
mDownButton = new QPushButton(("Y-"), this);;
...
connect(mDownButton, SIGNAL(clicked()), qApp, SLOT(quit()));
}
void GamePadWidget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Down ) {
qDebug() << "Down key pressed";
mDownButton.click();
}
}

Resources