I have a subclass of QGraphicsView to recognize mouse events, and it does. But when those mouse events happen, I need to call some other function in a different class, to handle how the mouse event interacts with the scene.
//Subclass
class Drawspace : public QGraphicsView {
public:
Drawspace(QGraphicsScene * scene, QWidget * parent) : QGraphicsView(scene, parent) {}
Drawspace(QWidget* parent) : QGraphicsView(parent) {}
void mousePressEvent(QMouseEvent * event) {
QMessageBox::information(this, tr("Dialog"), "You clicked the board (from QGraphicsView)");
QWidget::mousePressEvent(event);
}
};
//mainwindow code (my subclass is called "board")
mainwindow::mainwindow(QWidget *parent)
: QMainWindow(parent) {
//Initialize other stuff
ui.setupUi(this);
//Problem here
connect(ui.board, SIGNAL(Drawspace::mousePressEvent(QMouseEvent*)), this, SLOT(on_click(QMouseEvent*)));
}
void mainwindow::on_click(QMouseEvent * event) {
QMessageBox::information(this, tr("Dialog"), "You clicked the screen");
//Do stuff here
}
It builds fine, and when I click the drawspace, I get a dialog that says "You clicked the board (from QGraphicsView)", but I don't get the second from "You clicked the screen".
And yes, the header file for mainwindow has the Q_OBJECT macro
EDIT: Can be fixed by manually defining your own signal, calling that, and changing the connection to use Qt5 syntax. See my answer for code
OP here, the problem can be fixed by defining a signal, calling emit on it and changing the connection to use Qt5 syntax.
//New drawspace: Added signal definition and called emit on it from mousePressEvent
class Drawspace : public QGraphicsView, public QObject {
Q_OBJECT
public:
Drawspace(QGraphicsScene * scene, QWidget * parent) : QGraphicsView(scene, parent) {}
Drawspace(QWidget* parent) : QGraphicsView(parent) {}
void setObjectName(QString str) { QGraphicsView::setObjectName(str); }
void mousePressEvent(QMouseEvent * event) {
QMessageBox::information(this, tr("Dialog"), "Detected click in Drawspace");
emit clicked(event);
}
Q_SIGNALS:
void clicked(QMouseEvent * event);
};
//Used Qt5 syntax, this will automatically pass arguments
connect(ui.board, &Drawspace::clicked, this, &mainwindow::on_click);
mousePressEvent() is not a signal. It is a member function. You cannot connect() a function to a function like you can connect a signal to a signal.
Qt signals are declared as functions, but they get some special treatment. You'd need to use Qt's keywords to signify a signal so it can get the treatment that will make it work.
Make the mousePressEvent virtual, or you are creating a NEW method:
virtual void mousePressEvent(QMouseEvent * event)
and say it is overriden (depending on your compiler:)
virtual void mousePressEvent(QMouseEvent * event) override (Q_DECL_OVERRIDE)
Related
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
}
}
How can I connect a key enterPressed() event in my main GUI window to a specific button (also in main GUI)?
Usecase:
User starts the application, enter some data and press the Enter button and will be bypassed to a "Start" button is clicked.
My idea:
connect(this, SIGNAL(returnPressed()), ui.btn_Start, SLOT(clicked()));
class ClassA : QMainWindow
{
Q_OBJECT
// ...
private:
Ui::ClassAClass ui;
// ...
};
You can connect signals to other signals:
connect(this, &MainWindow::returnPressed, ui.btn_Start, &QPushButton::released);
And that is it. The signal is forwarded to QPushButton's instance signal emitting it so whatever is connected to it will be invoked. Additionally (you talk about events) in QMainWindow::keyPressEvent you re-implement:
class MainWindow : public QMainWindow
{
//...
signals:
void returnPressed() const;
protected:
void keyPressEvent(QKeyEvent *event);
};
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
if(event->key() == Qt::Key_Return)
emit returnPressed();
QMainWindow::keyReleaseEvent(event);
}
I have a QStackedWidget within a QDockWidget - depending on which page is shown I wish to show/hide the close button on the QDockWidget. I can do this by using QDockWidget::setFeatures().
However the issue I'm facing is how to capture the signal of the close button so that I can change the dock features / set the stacked widget page index.
I have attempted to use an event filter:
class EventFilter : public QObject
{
Q_OBJECT
public:
EventFilter( QObject* aParent );
protected:
bool eventFilter(QObject *obj, QEvent *event);
};
EventFilter::EventFilter( QObject* aParent )
: QObject( aParent )
{
}
bool EventFilter::eventFilter( QObject *obj, QEvent *event )
{
if ( event->type() == QEvent::Close )
{
return true;
}
return QObject::eventFilter( obj, event );
}
And installed it as so:
EventFilter* filter = new EventFilter( this );
u->dockWidget_6->installEventFilter( filter );
In the constructor of my QMainWindow - the eventFilter() method did not get called.
So next I attempted to use the QDockWidget::visibilityChanged changed signal since it sounded like this might be what I wanted:
connect( u->dockWidget_6, SIGNAL(visibilityChanged(bool)), SLOT(dockWindowClosed(bool)) );
This slot did get called - but not when the close button was clicked.
Finally I attempted to use a promoted QDockWidget to capture the QWidget::closeEvent().
class DockWidgetWithCloseSignal : public QDockWidget
{
Q_OBJECT
public:
explicit DockWidgetWithCloseSignal(const QString &title, QWidget *parent = 0, Qt::WindowFlags flags = 0)
: QDockWidget( title, parent, flags )
{
}
explicit DockWidgetWithCloseSignal(QWidget *parent = 0, Qt::WindowFlags flags = 0)
: QDockWidget( parent, flags )
{
}
protected:
void closeEvent(QCloseEvent *event)
{
event->ignore();
}
};
I saw the constructor was called which means this new widget was indeed being used, but again no joy since the closeEvent() was never called when the close button was clicked.
Turns out that everything apart from the visibilityChanged signal works!
I added a signal to the overridden closeEvent() method which I could then connect to any slot I wanted.
The actual issue was that within the stacked widget I had another QDockWidget on another page, hence I was adding all of these things to the wrong QDockWidget! (And of course promoted the wrong QDockWidget too doh!).
Hopefully this question can serve as a reference to anyone else that needs to figure out how to do this - rather than why it isn't working.
Create a new CloseDockWidget based on DockWidget.
Override the closeEvent() method, but emit an additional closed() signal from there.
widgets/qclosedockwidget.cpp:
#include "qclosedockwidget.h"
namespace Widgets
{
QCloseDockWidget::QCloseDockWidget(const QString &title, QWidget *parent)
: QDockWidget(title, parent)
{
// constructor
}
void QCloseDockWidget::closeEvent(QCloseEvent *event)
{
emit closed(); // <------ signal
QDockWidget::closeEvent(event);
}
} // namespace Widgets
widgets/qclosedockwidget.h:
#ifndef QCLOSEDOCKWIDGET_H
#define QCLOSEDOCKWIDGET_H
#include <QDockWidget>
namespace Widgets
{
class QCloseDockWidget : public QDockWidget
{
Q_OBJECT
public:
QCloseDockWidget(const QString &title = "", QWidget *parent = nullptr);
protected:
void closeEvent(QCloseEvent *event);
signals:
void closed();
};
} // namespace Widgets
#endif // QCLOSEDOCKWIDGET_H
Now you are able to instantiate and connect to the new signal:
auto *dockWidget = new Widgets::QCloseDockWidget("MyDockWidget", this);
connect(dockWidget, &Widgets::QCloseDockWidget::closed, this, &MainWindow::dockWidgetCloseClicked);
I have two widgets defined as follows
class mainWindow : public QWidget
{
Q_OBJECT
public:
mainWindow();
void readConfig();
private:
SWindow *config;
QVector <QString> filePath;
QVector <QLabel*> alias,procStatus;
QVector <int> delay;
QGridLayout *mainLayout;
QVector<QPushButton*> stopButton,restartButton;
QVector<QProcess*> proc;
QSignalMapper *stateSignalMapper, *stopSignalMapper, *restartSignalMapper;
public slots:
void openSettings();
void startRunning();
void statusChange(int);
void stopProc(int);
void restartProc(int);
void renew();
};
class SWindow : public QWidget
{
Q_OBJECT
public:
SWindow(QWidget *parent=0);
void readConfig();
void addLine(int);
private:
QVector<QPushButton*> selectButton;
QVector<QLabel*> filePath;
QVector<QLineEdit*> alias;
QSignalMapper *selectSignalMapper;
QVector<QSpinBox*> delay;
QGridLayout *mainLayout;
public slots:
void selectFile(int);
void saveFile();
void addLineSlot();
};
when i create and display SWindow object from mainWindow like this
void mainWindow::openSettings()
{
config = new SWindow();
config->show();
}
everything is ok, but now i need to access the mainWindow from SWindow, and
void mainWindow::openSettings()
{
config = new SWindow(this);
config->show();
}
doesn't display SWindow. How can i display SWindow?
How do i call a function on widget close?
By default a QWidget isn't a window. If it is not a window and you specify a parent, it will be displayed inside the parent (so in your case it is probably hidden by other widgets inside your mainWindow).
Look at windowFlags() too. Or you could make your SWindow inherit from QDialog, depending on what you use it for.
As for calling a function on widget close : you could reimplement closeEvent().
When you do config = new SWindow(this); you're setting the parent of config to be the instance of mainWindow.
This means config is no longer a top-level widget, therefore it won't display outside the mainWindow instance (specifically, it would need to be the central widget or inside the mainWindow instance's layout to be displayed).
EDIT: Sorry - I missed your last question; How do i call a function on widget close
You will want to override the QWidget::closeEvent(QCloseEvent *event) method. This gets called when you close a top-level widget. The most practical thing to do is emit() a signal so that another class can handle it having been closed.
As noted by Leiaz, you can use the windowsFlags flag when you create the widget. It would look like this:
void mainWindow::openSettings()
{
config = new SWindow(this, Qt::window);
config->show();
}
To reimplement the closeEvent:
header:
protected:
virtual void closeEvent ( QCloseEvent * event )
cpp:
void sWindow::closeEvent(QCloseEvent *event)
{
this->parentWidget()->SomeFunction();
qWidget::closeEvent(event);
}
However, its probably better to use signal/slots for your case here. Since you said you want to call the parent's renew method on some button click in sWindow, what you want is to EMIT a signal everytime the button is clicked, and connect this signal in the parent with the parent's refresh slot.
void sWindow::sWindow()
{
...
connect(ui.button, SIGNAL(clicked()), this, SLOT(btnClicked()));
}
void sWindow::btnClicked()
{
// whatever else the button is supposed to do
emit buttonClicked();
}
and in your parent class
void mainWindow::openSettings()
{
config = new SWindow(this, Qt::window);
connect(config, SIGNAL(buttonClicked()), this, SLOT(refresh()));
config->show();
}
I have to connect focus event from some QLineEdit element (ui->lineEdit) to the method focus(). How can I do this?
There is no signal emitted when a QLineEdit gets the focus. So the notion of connecting a method to the focus event is not directly appropriate.
If you want to have a focused signal, you will have to derive the QLineEdit class. Here is a sample of how this can be achieved.
In the myLineEdit.h file you have:
class MyLineEdit : public QLineEdit
{
Q_OBJECT
public:
MyLineEdit(QWidget *parent = 0);
~MyLineEdit();
signals:
void focussed(bool hasFocus);
protected:
virtual void focusInEvent(QFocusEvent *e);
virtual void focusOutEvent(QFocusEvent *e);
};
In the myLineEdit.cpp file you have :
MyLineEdit::MyLineEdit(QWidget *parent)
: QLineEdit(parent)
{}
MyLineEdit::~MyLineEdit()
{}
void MyLineEdit::focusInEvent(QFocusEvent *e)
{
QLineEdit::focusInEvent(e);
emit(focussed(true));
}
void MyLineEdit::focusOutEvent(QFocusEvent *e)
{
QLineEdit::focusOutEvent(e);
emit(focussed(false));
}
You can now connect the MyLineEdit::focussed() signal to your focus() method (slot).
I assume you mean connect as in signals/slots, focus event isn't a signal it's a virtual method you have to override in order to change the behavior:
http://doc.qt.io/qt-5/qlineedit.html#focusInEvent