嵌入式Linux应用程序开发-(10)i.MX6UL基于嵌入式QT实现电容屏多点触控

i.MX6UL基于嵌入式QT实现电容屏多点触控

        基于i.MX6UL平台,使用嵌入式QT实现电容屏的多点触控,前提是开发板的电容触摸屏驱动已经支持多点触控,并且驱动程序能通过事件方式向应用程序上报触控数据。关于电容触摸屏多点触控驱动程序的介绍,不在本章节描述之列。本章节重在实现多点触控的QT应用程序。

        嵌入式QT电容屏多点触控应用程序,是基于qTUIO库和mtdev库来实现的。qTUIO是国外一位开发者编写的第三方库,这个第三方库在本地UDP套接字上实现了一个基于TUIO协议的监听器,然后把监听到的事件转发到QT的内部事件系统。mtdev是一个独立的库,它可以将来自内核的MT事件转换为时隙类型的B协议,这些MT事件可能来自内核中所有的MT设备。

        总的来说,mtdev库可以把内核上报的多点触摸事件,通过mtdev2tuio后台应用程序(后面会讲到这个应用程序的移植),转换为TUIO协议,然后QT应用程序可以使用qTUIO库对内核上报的触摸事件进行处理。

以下是多点触控应用程序(涂鸦画板)运行时的界面:

界面描述:

(1)手写板界面,可以用多个手指同时涂鸦,最多支持5点同时触摸。

(2)Options里面有clear screen按钮,可以进行清屏。

 

本章节主要分以下两个步骤进行讲述:

(1) 在i.MX6UL开发板上移植 qTUIO 库,使其支持嵌入式QT多点触摸应用程序开发。

(2) 基于 qTUIO 库,编写一个QT多点触控应用程序。(手写板应用程序)

一、在i.MX6UL开发板上移植qTUIO库

在i.MX6UL开发板上移植qTUIO库,需要依赖以下的第三方库:liblo,mtdev,mtdev2tuio,qTUIO。

通过以下网址,下载以上的第三方依赖库:

        https://github.com/x29a/qTUIO

        https://github.com/olivopaolo/mtdev2tuio

        http://bitmath.org/code/mtdev/

        http://liblo.sourceforge.net/

如果不能访问以上网址,也可以使用作者下载好的文件:

        链接:https://pan.baidu.com/s/17l6LonZuh8U7axVRQ4TV-w          提取码:gvo7

 

(1)下载完成后,把下载好的文件,通过FileZilla工具上传到ubuntu系统,作者上传到ubuntu的/opt/work/tools/qt_multitouch/目录,以上依赖库的交叉编译也是基于该目录进行,把下载好的文件进行解压,并创建好各个依赖库的安装目录,如下图所示。

以上依赖库的交叉编译顺序是:liblo  -->  mtdev  -->  mtdev2tuio  -->  qTUIO。交叉编译完的文件,统一存放在 imx6ul-rootfs目录,方便移植到开发板。

(2)在ubuntu系统的命令行终端,执行以下命令,配置交叉编译环境。

#export CC=/opt/EmbedSky/gcc-linaro-5.3.1-2016.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc
#export CXX=/opt/EmbedSky/gcc-linaro-5.3.1-2016.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++

(3)先交叉编译liblo库,执行以下命令进行交叉编译:

#cd /opt/work/tools/qt_multitouch/liblo-0.26
#export SKIP_RMDIR_CHECK=yes
#./configure --prefix=/opt/work/tools/qt_multitouch/liblo_install --host=arm
#make clean
#make
#make install

交叉编译成功后,会在 liblo_install 目录下生成bin、include、lib目录,如下图所示:

执行以下命令,把生成的三个目录都复制到imx6ul-rootfs文件夹:

#cp  /opt/work/tools/qt_multitouch/liblo_install/*  /opt/work/tools/qt_multitouch/imx6ul-rootfs/  -a

(4)然后,再交叉编译mtdev库,执行以下命令进行交叉编译:

#cd /opt/work/tools/qt_multitouch/mtdev-1.1.3
#./configure --prefix=/opt/work/tools/qt_multitouch/mtdev_install --host=arm
#make clean
#make
#make install

交叉编译成功后,会在 mtdev_install 目录下生成 bin、include、lib 目录,如下图所示:

执行以下命令,把生成的三个目录都复制到imx6ul-rootfs文件夹:

#cp  /opt/work/tools/qt_multitouch/mtdev_install/*  /opt/work/tools/qt_multitouch/imx6ul-rootfs/  -a

(5)接着,再交叉编译 mtdev2tuio-master 后台程序,mtdev2tuio 后台程序是连接mtdev与qTUIO的桥梁,在运行基于qTUIO库编写的应用程序前,需要先运行mtdev2tuio,与以上两个依赖库不同,mtdev2tuio-master源码里面已经有Makefile文件,交叉编译过程可以基于此Makefile进行,修改 /opt/work/tools/qt_multitouch/mtdev2tuio-master/Makefile 内容,如下图所示:

修改完Makefile文件后,执行以下命令,进行交叉编译:

#cd /opt/work/tools/qt_multitouch/mtdev2tuio-master
#make clean
#make

交叉编译完成后,会在源码目录下生成 mtdev2tuio 应用程序,如下图所示:

执行以下命令,把mtdev2tuio应用程序复制到 imx6ul-rootfs的bin目录:

#cd  /opt/work/tools/qt_multitouch/mtdev2tuio-master/
#cp  ./mtdev2tuio  /opt/work/tools/qt_multitouch/imx6ul-rootfs/bin  -a

(6)最后,交叉编译qTUIO库。qTUIO库是一个QT工程,其工程的.pro文件以及源码目录是:/opt/work/tools/qt_multitouch/qTUIO-master/src,可以使用qmake工具进行生成Makefile文件,再交叉编译。执行以下命令,进行交叉编译。

#cd /opt/work/tools/qt_multitouch/qTUIO-master
#rm -rf /opt/work/tools/qt_multitouch/qTUIO-master/lib/*
#cd src/
#/opt/EmbedSky/TQIMX6UL/TQ_COREB/imx6ul_rootfs/opt/qt-4.8.7/bin/qmake
#make clean
#make

交叉编译成功后,会在/opt/work/tools/qt_multitouch/qTUIO-master/lib文件夹下生成qTUIO的动态链接库,如下图所示:

执行以下命令,把这些动态链接库,复制到imx6ul-rootfs的lib目录:

#cd  /opt/work/tools/qt_multitouch/qTUIO-master/
#cp  ./lib/*  /opt/work/tools/qt_multitouch/imx6ul-rootfs/lib  -a

(7)完成以上工作后,imx6ul-rootfs目录下各个文件夹里面的内容分别如下图所示:

imx6ul-rootfs/bin目录的内容

imx6ul-rootfs/include目录的内容

imx6ul-rootfs/lib目录的内容

(8)最后,把imx6ul-rootfs/bin目录的文件复制到i.MX6UL开发板的/bin目录,把imx6ul-rootfs/include目录的文件复制到i.MX6UL开发板的/include目录,把imx6ul-rootfs/lib目录的文件复制到i.MX6UL开发板的/usr/lib目录,复制完成后,如下图所示:

(9)至此,qTUIO库已经全部移植到i.MX6UL开发板,开发板已经支持运行基于qTUIO库编写的多点触摸应用程序,为了方便应用程序运行前不用手动运行mtdev2tuio后台程序,我们把该后台程序设置为开机自启动,修改 /etc/embedsky_env文件,如下图所示:

/bin/mtdev2tuio  /dev/input/cap_ts  & 表示监控 /dev/input/cap_ts节点,该节点是电容触摸屏的设备节点,内核里面的的电容触摸事件都是通过该节点进行上报,应用程序通过该节点捕获电容触摸事件,然后进行处理。

 

二、基于qTUIO库编写QT多点触控应用程序

(1)先用Qt Creator构建一个工程,命名为:010_multi_touch,关于如何构建工程,请参考“第一个嵌入式QT应用程序”的具体内容。与以往的程序不同,此次构建的工程,基类选择QMainWindow类。

(2)在编写代码前,我们需要把上一步骤移植好的qTUIO动态库和头文件复制到工程中,复制完成后,如下图所示:

其中,3rdparty目录直接从 /opt/work/tools/qt_multitouch/qTUIO-master/src 复制,并删除目录里面所有的非 .h 文件,只保留 .h 头文件。

(3)在工程目录上,右键 --> 添加库,如下图所示:

(4)选择添加“外部库”,点击下一步,在弹出的对话框中,平台选择Linux,库文件选择刚刚添加进工程的libqTUIO.so,然后点击下一步,如下图所示:

(5)动态链接库添加完成后,010_multi_touch.pro文件如下图所示:

(6)为了方便在界面上进行涂鸦绘画,我们创建一个ScribbleArea类,这个类继承于QWidget类,包含了清屏函数,以及重构的绘画事件函数,窗口大小调整函数,系统事件捕获函数,等等。类的具体内容如下图所示:

class ScribbleArea : public QWidget
{
    Q_OBJECT

public:
    ScribbleArea(QWidget *parent = 0);

public slots:
    void clearImage();   //清屏函数

protected:
    void paintEvent(QPaintEvent *event);    //绘画事件函数
    void resizeEvent(QResizeEvent *event);  //窗口大小调整的事件函数
    bool event(QEvent *event);   //系统事件处理函数

private:
    void resizeImage(QImage *image, const QSize &newSize);   //重新调整窗口大小

    QList<QColor> myPenColors;  //画笔颜色
    QImage image;   //窗口图像
};

(7)在ScribbleArea类的构造函数中,我们设置该类对象可以捕获触摸事件,并初始化设置画笔的颜色,该类的构造函数如下图所示:

ScribbleArea::ScribbleArea(QWidget *parent) : QWidget(parent)
{
     setAttribute(Qt::WA_AcceptTouchEvents);   //接收touch事件
     setAttribute(Qt::WA_StaticContents);      //设置静态的窗口部件

     myPenColors
             << QColor("green")
             << QColor("purple")
             << QColor("red")
             << QColor("blue")
             << QColor("yellow")

             << QColor("pink")
             << QColor("orange")
             << QColor("brown")
             << QColor("grey")
             << QColor("black");
}

(8)当系统有触摸事件上报时,应用程序会调用bool ScribbleArea::event(QEvent *event)函数,实际上,这个被重构的event函数不仅仅只在触摸事件发生时被调用,对于一般的系统事件,这个函数也会被调用,在这个event函数里面,我们只处理QEvent::TouchBegin,QEvent::TouchUpdate,QEvent::TouchEnd事件,其他时间忽略不进行处理。event函数的内容如下图所示:

//系统事件处理函数
bool ScribbleArea::event(QEvent *event)
{
    switch (event->type())
    {
        case QEvent::TouchBegin:     //触摸开始事件
        case QEvent::TouchUpdate:    //触摸更新事件
        case QEvent::TouchEnd:       //触摸结束事件
        {
            //获取触摸事件中所有的触摸点列表
            QList<QTouchEvent::TouchPoint> touchPoints = static_cast<QTouchEvent *>(event)->touchPoints();
            foreach (const QTouchEvent::TouchPoint &touchPoint, touchPoints)
            {
                switch (touchPoint.state())
                {
                    case Qt::TouchPointStationary:
                    // don't do anything if this touch point hasn't moved
                    continue;
                    default:
                    {
                        QRectF rect = touchPoint.rect();  //获取触摸点的矩形区域
                        if (rect.isEmpty())   //如果矩形区域空白
                        {
                            qreal diameter = qreal(20);
                            rect.setSize(QSizeF(diameter, diameter));  //重置触摸点的大小
                            rect.setX(touchPoint.pos().x() - 0);    //重置触摸点的x,y位置
                            rect.setY(touchPoint.pos().y() - 0);
                        }

                        QPainter painter(&image);
                        painter.setPen(Qt::NoPen);   //不使用画笔
                        painter.setBrush(myPenColors.at(touchPoint.id() % myPenColors.count())); //使用画刷
                        painter.drawEllipse(rect); //根据给定的矩形区域,绘制一个椭圆点
                        painter.end();    //绘制完成

                        int rad = 2;
                        update(rect.toRect().adjusted(-rad,-rad, +rad, +rad));  //更新绘制的区域
                    }
                    break;
                }
            }
            break;
        }
        default:
        return QWidget::event(event);
    }
    return true;
}

在该函数里面,只处理三种触摸事件,先获取触摸事件里面的所有触摸点,然后根据该触摸点的坐标和矩形大小,绘制椭圆点,然后调用update函数更新绘制的区域,update函数被调用的时候,void ScribbleArea::paintEvent(QPaintEvent *event)事件函数就会被触发。

(9)图形会在void ScribbleArea::paintEvent(QPaintEvent *event)事件函数里面绘制,函数的内容如下图所示:

//paintEvent重绘事件,在调用QWidget::update()函数后被触发
void ScribbleArea::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    const QRect rect = event->rect();    //构造一个矩形
    painter.drawImage(rect.topLeft(), image, rect);  //重新绘制这个矩形
}

(10)在MainWindow的窗体构造函数里实例化一个ScribbleArea对象,并把该对象设置为MainWindow的中心窗体,然后绘制一个菜单栏,并重置窗体的大小。代码如下图所示:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    scribbleArea = new ScribbleArea;    //实例化一个scribbleArea对象
    setCentralWidget(scribbleArea);     //设置中心窗体

    createActions();     //设置清屏按钮
    createMenus();       //创建菜单栏

    setWindowTitle(tr("Finger Paint"));    //修改窗体title
    resize(800, 480);    //重置窗体大小
}

(11)在应用程序的入口int main(int argc, char *argv[])函数里面,使用QTuio类实例化一个对象qTUIO,该对象引用了MainWindow类对象进行实例化,然后运行qTUIO.run()函数,这样,qTUIO就可以监听系统的多点触摸事件,当系统有多点触摸事件上报时,qTUIO会把这些事件转换为Qt内部可以处理的系统事件,方便Qt应用程序进行处理,main函数的内容如下图所示:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWindow window;
    window.show();

    QTuio qTUIO(&window);
    qTUIO.run();
    return app.exec();
}

(12)至此,整个多点触摸应用程序已经开发完成,编译下载到开发板,应用程序运行的现象如下图所示。

        关于qTUIO库的实现机制和实现原理,作者没有进行太多深入的研究和分析,网上对于该库的讲解资料也比较少。根据移植过程的分析,大概可以推断系统的MultiTouch事件会被mtdev库捕获,然后这些事件会通过mtdev2tuio通过TUIO协议转发到qTUIO库,最后qTUIO库把这些多点触摸事件转换为Qt系统内部可处理的事件。qTUIO库的源代码里提供了这个库的使用例程,例程源码在 qTUIO-master/examples/ 目录里面,感兴趣的开发者可以进行移植测试。如对qTUIO库有比较深入的研究,或者发现以上的移植过程有纰漏,欢迎联系作者并指正,谢谢。

 

 

 

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

工程师进阶笔记

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

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

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

打赏作者

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

抵扣说明:

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

余额充值