QT中关于QGestureEvent的一点经验

项目背景:

主要是用于谱图控件QwtPlot的一个手势控制,目标时实现手指捏合对谱图进行缩放已经对谱图的移动
设备是华为的一款平板电脑。

实现代码

主要参照了他人的代码经行改写的
头文件:
class Plot : public QwtPlot
{
Q_OBJECT
public:
Plot(QWidget * = NULL);
//这个属性主要是后面设置X轴的最大值最小值
void setMassRange(double startMass,double endMass)
{
this->min= startMass;
this->max= endMass;
}
//这个属性是捕获选择器捕获的点的X轴坐标
double getCurrentX()
{
return currentX;
}

private:
QwtPlotGrid* grid = nullptr;
QwtPlotZoomer* zoomer = nullptr;
QwtPlotPanner* panner = nullptr;
QwtPlotMagnifier* magnifier = nullptr;
QwtPlotPicker* picker = nullptr;
//最后一次放缩比例
qreal lastScale;
//总的放缩比例
qreal totalScale = 1;

double min;
double max;
double currentX = -1;

//事件处理函数
bool gestureEvent(QGestureEvent event);
//移动事件处理函数
void panTriggered(QPanGesture
pan);
//放缩事件处理函数
void pinchTriggered(QPinchGesture*);
void scaleFactorChange(double zb, bool isX, bool isY);

protected:
bool event(QEvent *e);

public slots:
void onSelected(const QPointF& point);
};

//cpp文件
#include “Plot.h”
#include
#include

Plot::Plot(QWidget *parent)
:QwtPlot(parent)
{
plotLayout()->setAlignCanvasToScales(true);
//设置网格
grid = new QwtPlotGrid();
grid->enableX(true);
grid->enableY(true);
grid->enableXMin(false);
grid->enableYMin(false);
grid->setMajorPen(QPen(Qt::black,0,Qt::DotLine));
grid->setMinorPen(QPen(Qt::black,0,Qt::DotLine));
grid->attach(this);

//选择器
picker = new QwtPlotPicker(this->canvas());
picker->setStateMachine(new QwtPickerClickPointMachine);
connect(picker,SIGNAL(selected(const QPointF&)),this,SLOT(onSelected(const QPointF&)));

//设置缩放框

// zoomer = new QwtPlotZoomer(this->canvas());
// zoomer->setTrackerMode(QwtPlotPicker::AlwaysOff);
// zoomer->setRubberBandPen(QColor(Qt::red));
// zoomer->setTrackerPen(QColor(Qt::red));

//设置画布移动
//panner = new QwtPlotPanner(this->canvas());

//整体放缩
//magnifier = new QwtPlotMagnifier(this->canvas());

// magnifier->setAxisEnabled(QwtPlot::xBottom,false);
// magnifier->setAxisEnabled(QwtPlot::yLeft,false);

//注册信息,这个很重要必须有
grabGesture(Qt::PinchGesture);
grabGesture(Qt::PanGesture);
注册touch事件是最初的想法,后面会说
this->setAttribute(Qt::WA_AcceptTouchEvents);
replot();
setAutoReplot(true);

}

bool Plot::event(QEvent event)
{
if (event->type() == QEvent::Gesture && ){
return gestureEvent(static_cast>(event));
}
return QWidget::event(event);
}

void Plot::scaleFactorChange(double zb, bool isX, bool isY)
{
bool doReplot = false;

const bool autoReplot = this->autoReplot();
setAutoReplot( false );
//是否是x轴放缩
if(isX){
    const QwtScaleDiv scaleDiv = this->axisScaleDiv(QwtPlot::xBottom);
    double begin = scaleDiv.lowerBound();
    double end = scaleDiv.lowerBound() + scaleDiv.range();
    //这是捕获两指的中间坐标,使得往两边伸缩时候保持中间位置不变
    const double center = scaleDiv.lowerBound() + scaleDiv.range()*zb;

    double d1 = (center-begin)/lastScale;
    double d2 = (end-center)/lastScale;
    
    //避免显示数据之外的区域
	begin = (center-d1)>min ? (center-d1):min;
    end = (center+d2)setAxisScale( QwtPlot::xBottom, begin, end );
    doReplot = true;
}
//是否是y轴放缩,没有实现这条判断
if(isY){
    const QwtScaleDiv scaleDiv = this->axisScaleDiv(QwtPlot::yLeft);
    double begin = scaleDiv.lowerBound();
    double end = scaleDiv.lowerBound() + scaleDiv.range();
    const double center = scaleDiv.lowerBound() + scaleDiv.range()*zb;

    double d1 = (center-begin)/lastScale;
    double d2 = (end-center)/lastScale;
    this->setAxisScale( QwtPlot::yLeft, center-d1, center+d2 );
    doReplot = true;
}

setAutoReplot(autoReplot);

if(doReplot)
    replot();

}

void Plot::panTriggered(QPanGesture *pan)
{
//捕获最后一次的偏移量,这个是显示屏的坐标,偏移量数字很大
QPointF point = pan->lastOffset();
//获得谱图的坐标范围
const QwtScaleDiv scaleDiv = this->axisScaleDiv(QwtPlot::xBottom);
double begin = scaleDiv.lowerBound();
double end = scaleDiv.lowerBound() + scaleDiv.range();
//最后一个5是个敏感系数,如果移动的太快就设置大一些,移动的太慢就设置小一些
//根据偏移量与屏幕宽度比设置实际偏移量
double dx = scaleDiv.range()*point.x()/this->width()/5;
begin = begin-dx;
end = end-dx;
//只显示数据区域
if(begin <= min){
this->setAxisScale( QwtPlot::xBottom, min, min+scaleDiv.range() );
return;
}
if(end >= max){
this->setAxisScale( QwtPlot::xBottom, max-scaleDiv.range(), max);
return;
}

this->setAxisScale( QwtPlot::xBottom, begin, end );

}

bool Plot::gestureEvent(QGestureEvent *event)
{

if(QGesture *pan = event->gesture(Qt::PanGesture))
panTriggered(static_cast(pan))
if (QGesture *pinch = event->gesture(Qt::PinchGesture))
pinchTriggered(static_cast(pinch));
return true;
}

void Plot::pinchTriggered(QPinchGesture *gesture)
{
QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();

if (changeFlags & QPinchGesture::ScaleFactorChanged) {
    //如果是两指放缩比例改变,则放缩
    lastScale = gesture->lastScaleFactor();

    QPoint centerPoint = this->mapFromGlobal(gesture->lastCenterPoint().toPoint());
    double zb = (double)centerPoint.x()/(double)this->width();
    //因为无法判断是X轴和Y轴中哪个进行放缩,根据重要性只让x轴放缩
    scaleFactorChange(zb,true,false);
}

if (gesture->state() == Qt::GestureFinished) {
    totalScale *= lastScale;
}

replot();
update();

}

void Plot::onSelected(const QPointF& point)
{
//捕获选择器的x坐标
currentX = point.x();
qDebug() << currentX;
}

实际上,功能和最初的目标还是有差池的,首先是QPinchGesture没有提供手指触点的坐标,所以无法根据手指所成角度判断是缩放X轴还是Y轴,最后只实现了X轴的缩放和平移;对于缩放和平移两个事件实际上是一起被触发,也没有找到好的方法去区分两种事件,主要原因是,在平板上单指是无法触发QPanGesture事件相应函数,必须也是双指才可以,尝试了禁用touch和mousePress事件相应函数,并没什么效果,如果哪位前辈知道,希望留言告知!

失败经历

既然是经验之谈,肯定要说下失败的经历。
最初的想法是利用touch事件来触发,但是也碰到了touch事件中只能触发begin类型的事件,对于end和update事件没有响应,这个问题至今也没查清楚,所以后面就改用手势事件来触发,最总还是勉强实现了功能,路漫漫其修远兮,我也会就遇到的一些问题记录下来,与大家共勉之。

尾语

小白水平,如有误导信息请务必指出,也欢迎大家留言自己的想法和建议。

你可能感兴趣的:(Qt)