Why multi-touch events using touchpad (laptop) is not working in this Qt program? - qt

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

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.

Qt window resize aspect ratio 1:1

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

Qt: how to apply a 2-step key shortcut to action

I know how to apply a keyboard shortcut to an action. And in some software such as Visual Studio there are shortcuts that do the job in more than one step (such as Ctrl+K,Ctrl+C to comment the code).
Another example of that in Sublime Text:
I wonder whether or not it is possible to implement in Qt.
You can create it by using the multiple arguments constructor for QKeySequence.
like this:
auto ac = new QAction(this);
ac->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_K, Qt::CTRL + Qt::Key_C));
Try this:
action->setShortcut("Ctrl+K,Ctrl+C");
QKeySequence may be implicitly created from QString.
Due to documentation:
Up to four key codes may be entered by separating them with commas, e.g. "Alt+X,Ctrl+S,Q".
MOC generates almost same code when you create shortcut for a QAction via Qt Designer. But it makes it slightly different:
action->setShortcut(QApplication::translate("MainWindow", "Ctrl+K, Ctrl+C", 0));
but it's actually same thing.
You can use eventFilter to get mouse & keyboard events.
I use boolean to get first and second key, Ctrl + K then C.
I made you a sample code it's working.
.cpp file:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
firstKey = false;
secondKey = false;
this->installEventFilter(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
bool MainWindow::eventFilter(QObject *object, QEvent *event)
{
if (object == this &&event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if ((keyEvent->key() == Qt::Key_Control))
{
firstKey = true;
return true;
}
else if ((keyEvent->key() == Qt::Key_K))
{
secondKey = true;
return true;
}
else if ((keyEvent->key() == Qt::Key_C))
{
if(firstKey && secondKey)
{
firstKey = false;
secondKey = false;
QMessageBox::information(this, "", "Ctrl + k + c");
}
return true;
}
else
return false;
}
else
return false;
}
void MainWindow::keyReleaseEvent(QKeyEvent *e)
{
if (e->type() == QEvent::KeyRelease)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
if ((keyEvent->key() == Qt::Key_Control))
{
firstKey = false;
}
}
}
.h file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QMessageBox>
#include <QKeyEvent>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
bool firstKey;
bool secondKey;
bool eventFilter(QObject *object, QEvent *event);
void keyReleaseEvent(QKeyEvent *e);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

QGraphicsPixmapItem, alternative methods of connecting to slots

I'm aware I need to derive from QObject in order to connect to a slot if I am using QGraphicsPixmapItem, but I am struggling to do this. I have tried alternative ways to achieve what I want, I have tried onMousePress and isSelectable i.e.
run->setFlag(QGraphicsPixmapItem::ItemIsSelectable);
if (run->isSelected())
{
qDebug() << "selected";
}
else if (!run->isSelected())
{
qDebug() << "not selected";
}
although run is selectable, the first argument is never true, it is always "not selected"
This is my code, I am working on the slot method;
mainwindow.cpp
int MainWindow::sim()
{
...
QGraphicsPixmapItem* run = new QGraphicsPixmapItem(QPixmap::fromImage(image6));
run->scale(0.3,0.3);
run->setPos(-200,-200);
run->setFlag(QGraphicsPixmapItem::ItemIsSelectable);
run->setCursor(Qt::PointingHandCursor);
connect(run, SIGNAL(selectionChanged()), this, SLOT(runClicked()));
scene->addItem(run);
//pause
QGraphicsPixmapItem* pause = new QGraphicsPixmapItem(QPixmap::fromImage(image7));
pause->scale(0.3,0.3);
pause->setPos(-160,-197);
pause->setFlag(QGraphicsPixmapItem::ItemIsSelectable);
pause->setCursor(Qt::PointingHandCursor);
connect(pause, SIGNAL(selectionChanged()), this, SLOT(pauseClicked()));
scene->addItem(pause);
...
}
void MainWindow::runClicked()
{
qDebug() << "run Clicked";
}
void MainWindow::pauseClicked()
{
qDebug() << "pause Clicked";
}
mainwindow.h
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
int sim();
...
public slots:
void runClicked();
void pauseClicked();
...
So obviously I get the error when connecting to the slots. Could anyone help please? Thank you.
To find out if your item is selected, do this:
QVariant MyItem::itemChange( GraphicsItemChange change, const QVariant& value )
{
if ( change == QGraphicsItem::ItemSelectedHasChanged ) {
qDebug() << ( isSelected() ? "selected" : "not selected" );
}
return QGraphicsItem::itemChange( change, value );
}
If you want to use signals and slots, you need to subclass both QObject and QGraphicsPixmapItem.
Because QObject doesn't contain clicked() signal, you need to implement that, too, by re-implementing
void mousePressEvent ( QGraphicsSceneMouseEvent *e ) and void mouseReleaseEvent ( QGraphicsSceneMouseEvent *e ).
MyItem:
#pragma once
#include <QGraphicsPixmapItem>
#include <qobject.h>
#include <QMouseEvent>
#include <QGraphicsSceneMouseEvent>
class MyItem: public QObject, public QGraphicsPixmapItem
/* moc.exe requires to derive from QObject first! */
{
Q_OBJECT
public:
MyItem(QGraphicsItem *parent = 0): QObject(), QGraphicsPixmapItem(parent)
{
}
MyItem(const QPixmap & pixmap, QGraphicsItem * parent = 0 ): QObject(),
QGraphicsPixmapItem(pixmap, parent)
{
}
signals:
void clicked();
protected:
// re-implement processing of mouse events
void mouseReleaseEvent ( QGraphicsSceneMouseEvent *e )
{
// check if cursor not moved since click beginning
if ((m_mouseClick) && (e->pos() == m_lastPoint))
{
// do something: for example emit Click signal
emit clicked();
}
}
void mousePressEvent ( QGraphicsSceneMouseEvent *e )
{
// store click position
m_lastPoint = e->pos();
// set the flag meaning "click begin"
m_mouseClick = true;
}
private:
bool m_mouseClick;
QPointF m_lastPoint;
};
And simple example of usage:
#include <qgraphicsview.h>
#include <qgraphicsscene.h>
#include "reader.h"
#include <qdebug.h>
class MainAppClass: public QObject
{
Q_OBJECT
public:
MainAppClass()
{
QGraphicsScene *scene = new QGraphicsScene();;
scene->setSceneRect( -100.0, -100.0, 200.0, 200.0 );
MyItem *item = new MyItem(QPixmap("about.png"));
connect(item, SIGNAL(clicked()), this, SLOT(pixmapClicked()));
scene->addItem(item);
QGraphicsView * view = new QGraphicsView( scene );
view->setRenderHints( QPainter::Antialiasing );
view->show();
}
public slots:
void pixmapClicked()
{
qDebug() << "item clicked!" ;
}
};

QGraphicsview mouserelease event does not register

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

Resources