C++ Qt newbe here. I work with a QDial object that is intended to be controlled with a mouse wheel, it works fine as such, emitting valueChanged() signals when necessary.
I would like to put a semi-transparent QToolButton on top of it, allowing users to click on the button (and set QDial value to a pre-defined number) while maintaining the ability to use the mouse wheel to control QDial as usual.
I experimented a bit with the TransparentForMouseEvents attribute:
ui->toolButton_Example->setAttribute(Qt::WA_TransparentForMouseEvents);
The problem is - the above code switches off all events, including the ability to emit the clicked() signal.
Is there a way to make a QToolButton transparent selectively for MouseWheelEvents while preserving the ability to respond to MouseClick events? Or would this require rewriting the event filter from scratch?
EDIT: Just to clarify - This question is about making QToolButton transparent to MouseWheel EVENTS while still allowing it to respond to MouseClick EVENTS. It is not about making the button transparent in the graphical sense.
SOLUTION
OK, problem solved the traditional way by subclassing QDial and overriding MousePressEvent and MouseReleaseEvent:
#include <QDial>
#include <QMouseEvent>
class QSuperDial : public QDial {
public:
QSuperDial (QWidget *parent = nullptr) : QDial(parent) {
}
virtual void mousePressEvent (QMouseEvent *event) override {
emit sliderPressed();
}
virtual void mouseMoveEvent (QMouseEvent * event) override {
}
virtual void mouseReleaseEvent (QMouseEvent *event) override {
}
};
Promoting QDial to QSuperDial results in a QDial object that 'behaves' like a button when pressed, emitting sliderPressed signal, while still being responsive to MouseWheelEvent (like a normal QDial).
I think this is the simplest and the most 'Qt-like' solution, but please do correct me if I'm mistaken.
You can use QObject::installEventFilter to have the parent object filter the events before they reach the tool button. Then, override the parent's QObject::eventFilter to handle/ignore the event.
I create an example below:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QToolButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
bool eventFilter(QObject *watched, QEvent *event) override;
private:
QToolButton tool_button_ignored_;
QToolButton tool_button_handled_;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QDebug>
#include <QEvent>
#include <QHBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
tool_button_ignored_.setObjectName("tool_button_ignored_");
tool_button_ignored_.setText("Ignored button");
tool_button_ignored_.installEventFilter(this);
tool_button_handled_.setObjectName("tool_button_handled_");
tool_button_handled_.setText("Handled button");
tool_button_handled_.installEventFilter(this);
QWidget *central_widget = new QWidget{this};
QHBoxLayout *layout = new QHBoxLayout{central_widget};
layout->addWidget(&tool_button_ignored_);
layout->addWidget(&tool_button_handled_);
this->setCentralWidget(central_widget);
}
MainWindow::~MainWindow()
{
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if (watched != &tool_button_ignored_ || event->type() != QEvent::Wheel)
{
qDebug() << event->type() << watched->objectName() << "handled";
return QMainWindow::eventFilter(watched, event);
}
else
{
qDebug() << event->type() << watched->objectName() << "ignored";
return true; // stop being handled further
}
}
Related
I have a QMainwindow class, in the cpp file I have certain QGroupBox, I have resize event for the mainwindow when I resize the mainwindow it fires. But I want the resizeevent for the QGroupBox, When I resize the QGroupBox I want to write some custom logic to place a button on the top right corner of the QGroupBpox. I have no idea how to implement the QGroupBox class again and put it into mainwindow so I can write some custom logic in the resize event of QGroupBox.
I tried implementing a class inheriting from QWidget. But this doesnt work.
#ifndef SRIBBLEAREA_H
#define SRIBBLEAREA_H
#include <QGroupBox>
#include <QPushButton>
class ScribbleArea : public QWidget {
Q_OBJECT
public:
ScribbleArea(QWidget* parent = nullptr);
protected:
void resizeEvent(QResizeEvent* event) override;
QGroupBox* sensorBox;
QPushButton* fullScreenButton;
};
#endif
#include <iostream>
#include "ScribbleArea.h"
#include <QtDebug>
ScribbleArea::ScribbleArea(QWidget* parent) : QGroupBox(parent) {
this->setMinimumSize(1, 1);
}
ScribbleArea::~ScribbleArea() {
}
void ScribbleArea::resizeEvent(QResizeEvent* e) {
qDebug() << "resize event called for QGroupBox";
QWidget::resizeEvent(e);
}
Using Qt to create an application that accepts a file drop. I have an area on my UI that I want to drop the file into, using a Qlabel. I have the function of dragging and dropping the file into the UI working, however I can drop it anywhere on the window, and not just into the Qlabel area.
I thought using
ui->label_drag->setAcceptDrops(true);
would work, however this just removed the functionality all together. What is the best way of handling this? if possible at all.
Thanks
The best way to do this is to override the QLabel class. In the dragEnterEvent be sure to call acceptProposedAction to process the move and leave events. If you don't do that, only the dragEnter event will fire.
Sample code follows. To use this in your project, add the source to your project and then right click on the label on the form and promote the item to QLabelDragDrop.
#ifndef QLABELDRAGDROP_H
#define QLABELDRAGDROP_H
#include <QLabel>
class QLabelDragDrop : public QLabel
{
Q_OBJECT
public:
explicit QLabelDragDrop(QWidget *parent = nullptr);
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dragLeaveEvent(QDragLeaveEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
signals:
public slots:
};
#endif // QLABELDRAGDROP_H
#include "qlabeldragdrop.h"
#include <QDebug>
#include <QDragEnterEvent>
#include <QDropEvent>
QLabelDragDrop::QLabelDragDrop(QWidget *parent) : QLabel(parent)
{
setAcceptDrops(true);
setMouseTracking(true);
}
void QLabelDragDrop::dragEnterEvent(QDragEnterEvent *event)
{
qDebug() << "dragEnterEvent";
event->acceptProposedAction();
}
void QLabelDragDrop::dragLeaveEvent(QDragLeaveEvent *event)
{
qDebug() << "dragLeaveEvent";
releaseMouse();
}
void QLabelDragDrop::dragMoveEvent(QDragMoveEvent *event)
{
qDebug() << "dragMoveEvent";
}
void QLabelDragDrop::dropEvent(QDropEvent *event)
{
qDebug() << "dropEvent";
}
I create a new QWidget object and I want to know when the close button is pressed.
I have tried the following code:
pWindow = new QWidget();
connect(pWindow , SIGNAL(triggered()), this, SLOT(processCloseButtonWindowsClicked()));
but it give an error:
no signal triggered of pWindow
How to achieve this?
Cause
QWidget does not have a triggered signal.
Solution
I would suggest you to:
Subclass QWidget and reimplement QWidget::closeEvent
Check QEvent::spontaneous to differentiate between a click of the close button and the call to QWidget::close
According to your app's logic either call QWidget::closeEvent(event); to close the widget, or QEvent::ignore to leave it open
Example
I have prepared an example for you of how to implement the proposed solution:
#include <QMainWindow>
#include <QCloseEvent>
#include <QPushButton>
class FooWidget : public QWidget
{
Q_OBJECT
public:
explicit FooWidget(QWidget *parent = nullptr) :
QWidget(parent) {
auto *button = new QPushButton(tr("Close"), this);
connect(button, &QPushButton::clicked, this, &FooWidget::close);
resize(300, 200);
setWindowTitle("Foo");
}
protected:
void closeEvent(QCloseEvent *event) override {
if (event->spontaneous()) {
qDebug("The close button was clicked");
// do event->ignore();
// or QWidget::closeEvent(event);
} else {
QWidget::closeEvent(event);
}
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
FooWidget *pWindow;
public:
explicit MainWindow(QWidget *parent = nullptr) :
QMainWindow(parent),
pWindow(new FooWidget()) {
pWindow->show();
}
};
void QWidget::closeEvent(QCloseEvent *event) will be the possible way I would go with.
You can read the documentation here.
Before, check if Qt has a class for what you want to do. Maybe you want to use QDialog instead of QWidget for what you want to achieve.
The following code: suppose you want to delete the widget when the X is clicked and you just want to know when to do something.
Try connecting the signal from the base class QObject of your widget when it is Destroyed:
-Your Widget
-attribute setted to destroy your widget after X(closebotton is clicked) or the close() handler is triggered
-connect the destroyed() signal to whatever slot you want to do something before it is destroyed
pWindow = new QWidget();
pWindow->setAttribute(Qt::WA_DeleteOnClose,true);
connect(pWindow , SIGNAL(destroyed()), this,SLOT(processCloseButtonWindowsClicked()));
for more info:
https://doc.qt.io/qt-5/qwidget.html#close
https://doc.qt.io/qt-5/qobject.html#destroyed
In a QWidget derived class object with implemented tabletEvent(QTabletEvent *event) and mousePressEvent(QMouseEvent *event), the mousePressEvent gets called every time tabletEvent gets called with type TabletEvent::TabletPress. According to the Qt documentation, this should not happen:
The event handler QWidget::tabletEvent() receives TabletPress, TabletRelease and TabletMove events. Qt will first send a tablet event, then if it is not accepted by any widget, it will send a mouse event.
mainwindow.cpp
#include "mainwindow.h"
#include "tabletwidget.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
TabletWidget* tw = new TabletWidget(this);
setCentralWidget(tw);
}
tabletwidget.h
#ifndef TABLETWIDGET_H
#define TABLETWIDGET_H
#include <QWidget>
class TabletWidget : public QWidget
{
Q_OBJECT
public:
explicit TabletWidget(QWidget *parent = 0);
protected:
void tabletEvent(QTabletEvent *event) Q_DECL_OVERRIDE;
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
signals:
public slots:
};
#endif // TABLETWIDGET_H
tabletwidget.cpp
#include "tabletwidget.h"
#include <QDebug>
#include <QTabletEvent>
TabletWidget::TabletWidget(QWidget *parent) : QWidget(parent)
{
}
void TabletWidget::tabletEvent(QTabletEvent *event)
{
event->accept();
qDebug() << "tabletEvent: " << event->type();
}
void TabletWidget::mousePressEvent(QMouseEvent *event)
{
qDebug() << "mousePressEvent";
}
The output generated if I use the tip of the pen or press any button of the Wacom Intuos CTH-680S-DEIT is:
tabletEvent: 92
mousePressEvent
tabletEvent: 87
tabletEvent: 87
tabletEvent: 87
tabletEvent: 87
tabletEvent: 93
So first the tabletEvent gets called, and even though I accept the event, mousePressEvent gets called anyway. Every following tabletEvent is of type QTabletEvent::TabletMove and the last one is QTabletEvent::TabletRelease. From the Qt documentation:
QEvent::TabletMove 87
QEvent::TabletPress 92
QEvent::TabletRelease 93
I have tested this on Mac OS 10.10.3 and Windows 7 with the same result. Is this a bug or am I doing it wrong?
This is tested on Qt 5.4.2.
Indeed, according to Qt documentation, the Qt should not be sending mouse events when the tablet is in use. But it seems to do it anyway (I'm using version 5.5).
One way to get around it is to reimplement event() method of QApplication - that's where TabletEnterProximity and TabletLeaveProximity are sent; those functions are not sent to the QWidget's event().
So, whenever the application catches either TabletEnterProximity or TabletLeaveProximity events, you may send a signal to your TabletWidget to change a private bool variable _deviceActive. Then, inside the TabletWidget you add a check for each MousePressEvent (and MouseReleaseEvent) to see if the _deviceActive is true or not; and implement the event only if the flag is false.
To illustrate, the inherited TabletApplication would look like this:
class TabletApplication : public QApplication {
Q_OBJECT
public:
TabletApplication(int& argv, char** argc): QApplication(argv,argc){}
bool event(QEvent* event){
if (event->type() == QEvent::TabletEnterProximity || event->type() == QEvent::TabletLeaveProximity) {
bool active = event->type() == QEvent::TabletEnterProximity? 1:0;
emit sendTabletDevice(active);
return true;
}
return QApplication::event(event);
}
signals:
void sendTabletActive(bool active);
};
And the additional parts inside tabletwidget.h:
class TabletWidget : public QWidget {
// ...
public slots:
void setTabletDeviceActive(bool active){
_deviceActive = active;
}
// ...
private:
bool _deviceActive;
};
Then you check inside the mouse events if the device is active:
void TabletWidget::mousePressEvent(QMouseEvent *event)
{
if (!_deviceActive)
qDebug() << "mousePressEvent";
}
Of course, don't forget to connect the corresponding signal with the slot. Hope it helps.
Reference: TabletApplication from Qt tablet example
Is there a way that I can override the Events of those widgets that I have placed on my designer?
I have seen tutorials demonstrating how to use Events in Qt but they all force the coder to create the widgets from scratch (by inheriting them from the widget they want the event to be associated and then add that widgets programmatically to the form) and when someone is using the QtCreator designer those examples are of no use.
How can I override a specific event for a specific widgets on my form in Qt?
You can do it using event filters.
Here is how I did it. First I needed to add eventFilter(QObject*,QEvent*); to the header of my Mainwindow (my form) and then inside its cpp file, defined that method.
And finally install the filter in constructor. The code below represents my forms content (MainWindow.h and MainWindo.cpp).
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
bool eventFilter( QObject* sender, QEvent* event);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QMessageBox"
#include <QKeyEvent> //dont forget to add this
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->lineEdit->installEventFilter(this); //installing the eventfilter for lineEdit
}
bool MainWindow::eventFilter(QObject *sender, QEvent *event)
{
if (sender == ui->lineEdit)
{
if(event->type()== QEvent::KeyPress)
{
QKeyEvent * keyEvent = (QKeyEvent*)(event);
if( keyEvent->key() == Qt::Key_Control)
{
QMessageBox::information(this,"Salam ","Control Key was ressed");
return true;
}else
{
return false;
}
}
}
return QWidget::eventFilter(sender,event);
}
MainWindow::~MainWindow()
{
delete ui;
}