I want to resize the app window proportionally 1:1. I tried to change it inside the ResizeEvent, but then I got the window flickering. Now my code looks like this, but it doesn't work.
filterobject.h:
class FilterObject:public QObject{
public:
QWidget *target = nullptr;//it holds a pointer to target object
int goalHeight=0;
FilterObject(QObject *parent=nullptr):QObject(parent){}//uses QObject constructor
bool eventFilter(QObject *watched, QEvent *event) override;//and overrides eventFilter function
};
widget.h:
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
virtual int heightForWidth ( int w ) const { return w*9/16;}
//virtual void resizeEvent(QResizeEvent *event) override;
~Widget();
private:
Ui::Widget *ui;
};
widget.cpp:
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void QWidget::resizeEvent(QResizeEvent *event){
FilterObject *filter = new FilterObject();
QWidget *targetWidget = new QWidget();
filter->target=targetWidget;
targetWidget->installEventFilter(filter);
}
bool FilterObject::eventFilter(QObject *watched, QEvent *event) {
if(watched != target){//checks for correct target object.
return false;
}
if(event->type() != QEvent::Resize){//and correct event
return false;
}
QResizeEvent *resEvent = static_cast<QResizeEvent*>(event);
goalHeight = 9*resEvent->size().width()/16;
if(target->height()!=goalHeight){
target->setFixedHeight(goalHeight);
}
return true;
};
Perhaps this code will work, but my condition if(event->type() != QEvent::Resize) does not work .. Any ideas?
You have some problems in your code. First of all you should install event filter once e.g in your constructor. You create an object of event filter and install it every time resizeEvent is triggered which is wrong. Also you are installing event filter on the wrong object (a new QWidget). So remove the resizeEvent function and insert in the constructor of Widget:
FilterObject *filter = new FilterObject();
filter->target=this;
installEventFilter(filter);
Related
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.
I want to install an event filter on an object of a custom class in Qt. So I created a project such as QtGuiApplication1 on the Qt Designer and created a simple class as myClass as which has a widget and a QGraphicsView for drawing a colored rectangle.
in header file:
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QtGuiApplication1.h"
#include "myClass.h"
class QtGuiApplication1 : public QMainWindow
{
Q_OBJECT
public:
QtGuiApplication1(QWidget *parent = Q_NULLPTR);
private:
Ui::QtGuiApplication1Class ui;
bool eventFilter(QObject *obj, QEvent *ev);
myClass* myClass_;
};
in .cpp
#include "QtGuiApplication1.h"
QtGuiApplication1::QtGuiApplication1(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
myClass_ = new myClass(this, QRect(100, 100, 200, 200));
myClass_->installEventFilter(this);
}
bool QtGuiApplication1::eventFilter(QObject * obj, QEvent * ev)
{
if (obj == myClass_)
{
bool hi = true;
}
return false;
}
and the myClass code is here:
header file of myClass:
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
#include <QGraphicsView>
class myClass : public QObject
{
Q_OBJECT
public:
explicit myClass(QObject *parent = 0);
myClass();
myClass(QWidget* parent, QRect inRect);
private:
QWidget * widget;
QGraphicsView* qGraph_back;
QGraphicsScene* scene_back;
};
#endif /
cpp file of myClass:
#include "myClass.h"
myClass::myClass(QObject *parent) :
QObject(parent)
{
}
myClass::myClass()
{
}
myClass::myClass(QWidget* parent, QRect inRect)
{
widget = new QWidget(parent);
qGraph_back = new QGraphicsView(widget);
scene_back = new QGraphicsScene(qGraph_back);
widget->setGeometry(inRect);
scene_back->setSceneRect(0,0,inRect.width(),inRect.height());
qGraph_back->setBackgroundBrush(QColor(0, 0, 255, 80));
qGraph_back->setScene(scene_back);
qGraph_back->show();
}
I want to get all the events of myClass_ object such as mouse event, But I can't and the eventfilter doesn't work. how to install eventfilter on the object?
The event filter will work only for events in your MyClass instance, only. Not for its children.
So, events, such as a mouse click or move, in your qGraph_back will be not visible in your eventFilter method.
When you add a child in a widget, an QChildEvent event is raised. You can use it to install the event filter on all children (and grandchildren, etc.). But, you have to install the event filter on your MyClass before adding the children.
A quick example:
class Listener: public QObject
{
public:
Listener(): QObject()
{}
bool eventFilter(QObject* object, QEvent* event)
{
qDebug() << Q_FUNC_INFO << object << event;
if (event->type() == QEvent::ChildAdded)
{
QChildEvent* ev = dynamic_cast<QChildEvent*>(event);
ev->child()->installEventFilter(this);
}
return false;
}
};
class Widget: public QWidget
{
public:
Widget(QObject* parent) : QWidget()
{
installEventFilter(parent);
QGraphicsView* view = new QGraphicsView(this);
auto layout = new QHBoxLayout(this);
layout->addWidget(view);
layout->addWidget(new QLabel("Foobar"))
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Listener* listener = new Listener();
Widget* w = new Widget(listener);
w->show();
return app.exec();
}
As you can see, the events in the QLabel are now sent to the listener. But, you can't see the events from the view because they are caught by the viewport widget in the QGraphicsView...
You have to handle the case where the added child has a viewport (inherits from QAbstractItemView, etc.) and it becomes more complicated.
So, if you have to know when the user clicks on your view, it would be easier to use signals/slots and not an event filter.
I am trying to handle mult-itouch events in this simple QWidget based program but not able to receive any touch events.
"MyWidget.h"
#include <QWidget>
class QPaintEvent;
class QEvent;
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *);
bool event ( QEvent * event );
};
"MyWidget.cpp"
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent)
{
setAttribute(Qt::WA_AcceptTouchEvents);
}
void MyWidget::paintEvent(QPaintEvent *evt) {
QPainter painter(this);
painter.fillRect(rect(),QColor(0,255,0));
// painter.drawText(QPoint(rect().left(),rect().top()),"Hello world");
}
bool MyWidget::event(QEvent *event){
if(event->type() == QEvent::TouchBegin ||
event->type() == QEvent::TouchEnd ||
event->type() == QEvent::TouchUpdate ){
qDebug() <<"Touch events";
}
else if(event->type() == QEvent::MouseButtonDblClick) {
qDebug() <<"double click";
}
return QWidget::event(event);
}
Am I missing anything here ?
To make touch events work add the following to your MainWindow:
MyWidget *myWidget = ...;
setCentralWidget(myWidget);
In the MyWidget constructor add:
setAttribute(Qt::WA_AcceptTouchEvents);
//grabGesture(Qt::PinchGesture);
//setAttribute(Qt::WA_InputMethodEnabled);
//setFocusPolicy(Qt::WheelFocus);
setAttribute(Qt::WA_StaticContents);
have a strange problem using QPointer for accessing object method from a different, not related, object.
I've prepared a little example to explain it better.
I created two QWidget, Widget_A and Widget_B in a simple QDialog.
I need to access from Widget_B a public method of Widget_A: I passed widget_a pointer to a method of widget_b for QPointer assignment. Widget_A contains a QLineEdit that I want to clear from Widget_B.
The problem is when hitting pushButton_B nothing happens to lineEdit_A. Console doesn't show any problem, so the most obvious reason is Widget_B is working on a different Widget_A object, not the one I passed.
I also created a connection from QWidget_A to QWidget_B so when editing lineEdit_A a label_B text is changed accordingly: this works.
Before asking, I need a QPointer since in my real project Widget_A could be deleted. Can you explain where I'm wrong? Should I choose a different way? Thanks. Follow some snippets
widget_a.h
class Widget_A : public QWidget
{
Q_OBJECT
public:
explicit Widget_A(QWidget *parent = 0);
~Widget_A();
void clearLineEdit_A();
signals:
void lineEdit_A_changed(const QString &);
private slots:
void on_lineEdit_A_textChanged(const QString &arg1);
private:
Ui::Widget_A *ui;
};
widget_a.cpp
Widget_A::Widget_A(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget_A)
{
ui->setupUi(this);
ui->lineEdit_A->setText("write something here...");
ui->lineEdit_A->selectAll();
}
Widget_A::~Widget_A()
{
delete ui;
}
void Widget_A::clearLineEdit_A()
{
ui->lineEdit_A->clear();
}
void Widget_A::on_lineEdit_A_textChanged(const QString &arg1)
{
emit lineEdit_A_changed(arg1);
}
widget_b.h
class Widget_B : public QWidget
{
Q_OBJECT
public:
explicit Widget_B(QWidget *parent = 0);
~Widget_B();
void controlWidget(QWidget *w);
private slots:
void changeLabel_B(const QString &text);
void on_pushButton_B_clicked();
private:
Ui::Widget_B *ui;
QPointer<QWidget> qPtrWdgt;
};
widget_b.cpp
Widget_B::Widget_B(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget_B)
{
ui->setupUi(this);
}
Widget_B::~Widget_B()
{
delete ui;
}
void Widget_B::controlWidget(QWidget *w)
{
qPtrWdgt = w;
connect(qPtrWdgt, SIGNAL(lineEdit_A_changed(const QString &)),
this, SLOT(changeLabel_B(const QString &)));
}
void Widget_B::changeLabel_B(const QString &text)
{
ui->label_B->setText(text);
}
void Widget_B::on_pushButton_B_clicked()
{
Widget_A(qPtrWdgt).clearLineEdit_A();
}
dialog.h
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
Widget_A *widget_a;
Widget_B *widget_b;
};
dialog.cpp
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
widget_a = new Widget_A(this);
widget_b = new Widget_B(this);
widget_b->controlWidget(widget_a);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(widget_a);
mainLayout->addWidget(widget_b);
setLayout(mainLayout);
}
Widget_A(qPtrWdgt).clearLineEdit_A();
This creates a new Widget_A by using the Widget_A(QWidget *) constructor, then calls clearLineEdit_A() on it.
If you define qPtrWdgt as QPointer<Widget_A> instead, and since QPointer overloads operator->, then you can use:
qPtrWdgt->clearLineEdit_A();
You'll need to include Widget_A's header in Widget_B's header too.
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