(五)Qt 动态手势识别“左右滑动”以及实现翻页效果

使用Qt实现手势识别驱动的动态翻页效果
本系列文章介绍了如何通过Qt实现动态手势识别,包括“左右滑动”来控制软件操作,如翻页效果。通过QGraphicsScene模块绘制图形并确保其始终在前,结合QStackWidget实现页面切换动画,详细讲解了识别左右滑动的手势算法及其实现屏幕滑动效果的代码步骤。

系列文章目录

通过Qt实现手势识别控制软件操作相关系列技术方案

(一)Qt 将某控件、图案绘制在最前面的方法,通过QGraphicsScene模块实现
(二)Qt QGraphicsScene模块实现圆点绘制在所有窗体的最前方,实现圆点的“彩色拖尾”效果以及“选中方框”效果
(三)Qt 动态手势识别“握拳”
(四)Qt 动态手势识别“手掌随动”+“握拳选择”
(五)Qt 动态手势识别“左右滑动”以及实现翻页效果


1、前言

  本系列博客为Qt实现动态手势识别以及实现一些特定效果的工程性记录博客。

1.1 目标

  本篇博客主要记录了动态手势识别实现“左右滑动翻页”的效果的过程。


2、效果

  为了实现对“左右滑动手势”现象的监测,首先设计好UI相关内容,即在Widget背景内实现“变色”的效果,且每当监测到“右移”或是“左移”的动作时,背景内的数字产生变化,每次“右移”发生时,背景的数字将会+1,每次“左移”发生时,背景的数字将会-1。
  数字右侧的窗口是摄像头的实时画面情况,用于验证效果。

  先看演示效果视频:

动态手势识别_左移页面_视角1

动态手势识别_右移页面_视角1

动态手势识别_右移页面_视角2

  效果演示动态图如下图所示:
在这里插入图片描述

图1 “右移”效果gif图

在这里插入图片描述

图2 “左移”效果gif图

2、代码实现

2.1 动态手势识别“左右滑动”

  判断动态手势的“左右滑动”,与本系列里第(三)篇文章里所说的“检测握拳动作”类似,均为实现判断某一特定系列内各帧静态手势的具体类别、相对位置、运动速度等具体参数内容。
  能判断手势是否存在“左右滑动”现象的手法很多,这里只介绍一种简单的方法步骤:
  (1)判定动作初始帧的位置,如果想右移,那么初始帧的位置必然位于监测画面的左半边,如果想左移,那么初始帧的位置必然位于监测画面的右半边(当然了,这里没什么必然,看个人对动态手势的具体定义吧);
  (2)检测该动作序列中,后一帧对前一帧在横坐标方向上的偏移量,若是右移,那么偏移量均应该为正值,若为左移,偏移量均应该为负值;
  (3)该序列每帧数据的纵坐标方向上的偏差应该小于某一特定的阈值,用于判断该动作是水平方向上的运动;
  (4)手势的速度问题,在上述第(2)点的偏移量,需要检测每个偏移量的数值大小,其绝对值应该大于某一特定的数值(这个自行确定);
  (5)最后一帧的位置问题,判断最后一帧的具体位置是否出现在其应该出现的位置,对于左移而言,其最后一帧的位置应该在监测画面的右半边。
  下面是识别“左右滑动”的算法的C++/Qt代码实现。

QString HandPostureDetect::dynamic_gesture_recognize_leftSlide(const QQueue<QPointF>* historyPoints)
{
    int countLeftMove = 0; 
    int countTotalPoints = historyPoints->size(); 

    const QPointF& startPoint = (*historyPoints).front();
    const QPointF& endPoint = (*historyPoints).back();

    double distance = qSqrt(qPow(endPoint.x() - startPoint.x(), 2) + qPow(endPoint.y() - startPoint.y(), 2));

    if (distance < 0.3 || startPoint.x() < 0.5)
    {
        pre_left_slide_condition = 0;
        return ""; 
    }

    for (int i = 1; i < countTotalPoints; ++i)
    {
        const QPointF& currentPoint = (*historyPoints)[i];
        const QPointF& previousPoint = (*historyPoints)[i - 1];

        double deltaX = currentPoint.x() - previousPoint.x();
        double deltaY = currentPoint.y() - previousPoint.y();

        if (deltaX < 0 && qAbs(deltaY) < 0.1) 
        {
            countLeftMove++;
        }
    }

    double ratioLeftMove = static_cast<double>(countLeftMove) / countTotalPoints;
    if (ratioLeftMove >= 0.3) 
    {
        if (pre_left_slide_condition == 0)
        {
            qDebug() << "Left Slide_" << left_slide_count;
            left_slide_count++;
            emit send_result(3);
        }
        pre_left_slide_condition = 1;
        return "Left Slide";
    }
    else
    {
        pre_left_slide_condition = 0;
        return "";
    }
}

2.2 屏幕滑动效果的实现

  通过QStackWidget模块实现容纳多个不同的QWidget对象,可进行按索引选择所展现的具体页面。
  这里“左右切换屏幕”的效果用Qt的动画模块QPropertyAnimation进行的实现。
  当每次有动态手势被检测到的时候,同时调用Interface_Switching_Animation函数,该函数包含两个参数,第一个参数int m_currentIndex用来表示QStackWidget的索引数,第二个参数int left_right_type用来表示是“左移”还是“右移”。

void Widget::Interface_Switching_Animation(int m_currentIndex, int left_right_type)
void Widget::Interface_Switching_Animation(int m_currentIndex, int left_right_type)
{
    int flag = 0;

    QLabel *m_label = new QLabel(ui->stackedWidget);
    m_label->resize(QSize(ui->stackedWidget->width(),ui->stackedWidget->height()));
    m_label->setPixmap(ui->stackedWidget->grab());
    m_label->setAttribute(Qt::WA_DeleteOnClose);
    m_label->show();

    QPropertyAnimation *animation1 = new QPropertyAnimation(m_label, "geometry");
    animation1->setDuration(1000);

    if(left_right_type == 1)
    {
        //向左拉出前一页
        animation1->setStartValue(QRect(0,0,ui->stackedWidget->width(),ui->stackedWidget->height()));
        animation1->setEndValue(QRect(ui->stackedWidget->width()*2,0,ui->stackedWidget->width(),ui->stackedWidget->height()));
        flag = 0;
    }

    else if(left_right_type == 0)
    {
        //向右拉出前一页
        animation1->setStartValue(QRect(0,0,ui->stackedWidget->width(),ui->stackedWidget->height()));
        animation1->setEndValue(QRect(-ui->stackedWidget->width()*2,0,ui->stackedWidget->width(),ui->stackedWidget->height()));
        flag = 1;
    }


    animation1->setEasingCurve(QEasingCurve::InCubic);

    QParallelAnimationGroup *group = new QParallelAnimationGroup;
    group->addAnimation(animation1);
    group->start(QAbstractAnimation::DeleteWhenStopped);

    ui->stackedWidget->setCurrentIndex(m_currentIndex);
    m_label->raise();
    connect(group, &QParallelAnimationGroup::finished, this, [=](){m_label->close();}); //关闭动画完成后关闭label

    if(flag == 0)
    {
        count_times++;
    }
    else if (flag == 1)
    {
        count_times--;
    }

    QString text = QString::number(count_times);
    QString styleSheet = "background-color: #";
    styleSheet += QString::number(qrand() % 256, 16).rightJustified(2, '0'); // 随机生成颜色
    styleSheet += QString::number(qrand() % 256, 16).rightJustified(2, '0');
    styleSheet += QString::number(qrand() % 256, 16).rightJustified(2, '0');

    QWidget* currentwidget = ui->stackedWidget->currentWidget();
    currentwidget->setStyleSheet(styleSheet);

    QLabel *label = new QLabel(currentwidget);
    label->setFont(QFont("Arial", 360));
    label->setText(text);
    label->setAlignment(Qt::AlignCenter);
    label->setGeometry(currentwidget->rect());
//        label->setStyleSheet("color: white;");
    label->show();
}

总结

  本系列文章,通过动、静态手势识别技术方式操控Qt界面软件实现相关操作功能:
(一)Qt 将某控件、图案绘制在最前面的方法,通过QGraphicsScene模块实现
(二)Qt QGraphicsScene模块实现圆点绘制在所有窗体的最前方,实现圆点的“彩色拖尾”效果以及“选中方框”效果
(三)Qt 动态手势识别“握拳”
(四)Qt 动态手势识别“手掌随动”+“握拳选择”
(五)Qt 动态手势识别“左右滑动”以及实现翻页效果

  本项目所有代码的获取,请私信与本人联系。

qt 翻页效果C++程序 #ifndef PICTUREFLOW_H #define PICTUREFLOW_H #include class PictureFlowPrivate; /*! Class PictureFlow implements an image show widget with animation effect like Apple's CoverFlow (in iTunes and iPod). Images are arranged in form of slides, one main slide is shown at the center with few slides on the left and right sides of the center slide. When the next or previous slide is brought to the front, the whole slides flow to the right or the right with smooth animation effect; until the new slide is finally placed at the center. */ class PictureFlow : public QWidget { Q_OBJECT Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) Q_PROPERTY(QSize slideSize READ slideSize WRITE setSlideSize) Q_PROPERTY(int slideCount READ slideCount) Q_PROPERTY(int centerIndex READ centerIndex WRITE setCenterIndex) public: enum ReflectionEffect { NoReflection, PlainReflection, BlurredReflection }; /*! Creates a new PictureFlow widget. */ PictureFlow(QWidget* parent = 0); /*! Destroys the widget. */ ~PictureFlow(); /*! Returns the background color. */ QColor backgroundColor() const; /*! Sets the background color. By default it is black. */ void setBackgroundColor(const QColor& c); /*! Returns the dimension of each slide (in pixels). */ QSize slideSize() const; /*! Sets the dimension of each slide (in pixels). */ void setSlideSize(QSize size); /*! Returns the total number of slides. */ int slideCount() const; /*! Returns QImage of specified slide. */ QImage slide(int index) const; /*! Returns the index of slide currently shown in the middle of the viewport. */ int centerIndex() const; /*! Returns the effect applied to the reflection. */ ReflectionEffect reflectionEffect() const; /*! Sets the effect applied to the reflection. The default is PlainReflection. */ void setReflectionEffect(ReflectionEffect effect); public slots: /*! Adds a new slide. */ void addSlide(const QImage& image); /*! Adds a new slide. */ void addSlide(const QPixmap& pixmap); /*! Sets an image for specified slide. If the slide already exists, it will be replaced. */ void setSlide(int index, const QImage& image); /*! Sets a pixmap for specified slide. If the slide already exists, it will be replaced. */ void setSlide(int index, const QPixmap& pixmap); /*! Sets slide to be shown in the middle of the viewport. No animation effect will be produced, unlike using showSlide. */ void setCenterIndex(int index); /*! Clears all slides. */ void clear(); /*! Shows previous slide using animation effect. */ void showPrevious(); /*! Shows next slide using animation effect. */ void showNext(); /*! Go to specified slide using animation effect. */ void showSlide(int index); /*! Rerender the widget. Normally this function will be automatically invoked whenever necessary, e.g. during the transition animation. */ void render(); /*! Schedules a rendering update. Unlike render(), this function does not cause immediate rendering. */ void triggerRender(); signals: void centerIndexChanged(int index); protected: void paintEvent(QPaintEvent *event); void keyPressEvent(QKeyEvent* event); void mousePressEvent(QMouseEvent* event); void resizeEvent(QResizeEvent* event); private slots: void updateAnimation(); private: PictureFlowPrivate* d; }; #endif // PICTUREFLOW_H
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wang_chao118

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值