多点触控

更新时间:2024-02-03 07:44:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

多点触控 总结

一、 研发目的

使用win7自带API或者PQ-LABS所提供的开发包,在win7系统下实现多点触控与OSG结合的漫游方式。

二、 确定选型

? 网络资料对比

大量查阅网络相关触摸屏资料,总结出现有触摸屏类型。按外形的分类可分为触摸一体机、触摸屏显示器、外挂式触摸屏;按照原理和材质可分为电阻式、电容式、表面声波式、红外式、光学式。

并对以上不同类型的触摸屏从外观及原理上进行了理论的比对:

? 具体厂家业务员信息提供

联系了北京现有知名厂家的业务人员,进一步了解了市场上触摸屏的实际信息,发现触摸屏最大的区别不是在外观,而是在材质及原理。

触摸一体机、触摸屏显示器、外挂式触摸屏的显示部分其实都是一个显示屏加一个触摸屏组合而成只是加工的方式不一样:外挂式是直接将触摸屏用双面胶粘贴在显示屏表面;触摸显示器是在外挂式的基础上人工加上一个外框,使其拥有一个完整的外观;而触摸一体机又是在触摸屏显示器的基础上将底座和主机集

合于一体。

经实际了解发现目前厂家提供的24寸以上的触摸屏只有红外和光学两种。

? 实地考察

对触摸屏有了一定了解之后开始实际的考察工作,主要考察了以下公司的触摸屏:吉元平,英策长远,金雀,翔远时代,安信思拓。

安信思拓生产的是只有光学屏无红外屏,其中21.5寸两点光学屏处于批量生产阶段,有大量存货。

翔远时代和吉平元主要生产两点红外屏,其中翔远时代的触摸屏存在不能实时变化触摸点数的技术问题,吉平元所作的42寸屏是这几家中无论外观还是性能都是最好的,但需要整体购买(不能由我方提供显示器),且只限于42寸可达到高性能。而且此屏幕为两点屏。

金雀和英策长远都采用美国进口的PQ-LABS屏,PQ-LABS总部在硅谷,中国研发基地在上海,是世界先进的触摸屏研发生产商,质量有保证。英策长远目前最小可以做32寸屏,我们现场试用了这种屏,体验效果很好。目前只有金雀一家公司可以做32寸以下6点屏。

经过以上各方面的限定,最终确定选用红外6点屏,24寸小屏采用金雀提供的屏幕,而32寸及以上大屏则可在英策长远和金雀中做选择。

三、 研发使用触摸屏

24寸外挂式触摸屏+现有24寸液晶显示器

四、 研发前期准备

? 大量查阅网络可用资料,寻找一切可利用资源

主要参考QP-LABS官网,win7官网,以及各种触控相关网页 ? 观看了大量网络多点触控相关视频,使用了google earth,bing

maps 3d等现有可操作的触控软件。

结合实时视景现有漫游操作初步定下漫游的手势操作方式。 ? 公司内部开展交流讨论,后组内讨论

修正了漫游手势操作方式

? 研究了win7官网提供的例子程序以及PQ-LABS自带的程序范例,

联系了PQ-LABS中国总部技术人员。 发现用PQ-LABS实现手势操作相对简单,有现成函数可调用,但考虑到如使用PQ-LABS开发包开发就必须使用PQ-LABS厂家提供的触摸屏,通用性受到限制,故只能使用win7自带的API进行程序的研发。

? 学习了OSG漫游相关知识。

学习了相机,矩阵,漫游器等相关知识。

五、 确定研发方案确定

由于通用性的限制,只能选择win7自带的API进行开发,其中win7提供了两种触控消息处理方法:WM_GISTRUE和WM_TOUCH。

前者是win7自带的默认触控处理方式,用于手势操作的处理,win7提供了5种操作手势:平移,旋转,缩放,两触点同时点击,一触点按下后另一触点点击,此种处理方式是针对于已有手势,不能得到具体执行手势的触摸点数

后者需要先将窗口注册为触摸窗口,然后可以接收触摸消息,可以跟踪到具体某个触摸点,但没有手势的操作,需要自己定义何种操作对应何种手势。

对比两种触控处理方法,后者需要的工作量太大,精准度也很难确定,前者出点数不能获取外,手势操作基本满足现有漫游需要。

故决定选取前者进行研发。

六、 研发阶段

研发采用OSG+QT框架,首先遇到的问题是如何让屏幕接收到触摸消息,为此尝试了以下几种方法:

? 子类化

首先需要得到触控窗口的句柄:

osgViewer::GraphicsWindowWin32* pGraphicsWindow = dynamic_cast< osgViewer::GraphicsWindowWin32* >(_viewQOSG->getCamera()->getGraphicsContext());

HWND hwnd = pGraphicsWindow->getHWND();

通过SetWindowLong来重新设置窗口的过程函数:

WNDPROC g_OldEdit;

g_OldEdit=(WNDPROC)SetWindowLong(hwnd,GWL_WNDPROC,(LONG)NewProc);

LRESULT CALLBACK NewProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

{

switch (message)

{

case WM_LBUTTONDOWN: {

int j = 1; }

break;

case WM_LBUTTONDBLCLK: {

int j = 1; }

break; case WM_TOUCH: {

int i = 0; }

break; }

return CallWindowProc (g_OldEdit, hwnd, message, wParam, lParam); }

此时出现只可以截获到鼠标消息并不能接收到触摸消息。猜测是OSG窗口截获过滤掉了触摸消息,于是准备采用hook来截获窗口消息。

? HOOK截获窗口消息

查看网络相关资料,尝试使用钩子来截获窗口消息,发现网上引用的代码hook是从MFC中的CObject类继承过来的,而win32程序无此类库,并且HOOK内部关键代码也是通过SetWindowLong转换现有窗口过程实现窗口消息截获,故放弃使用此方法。

? 通过引用C#现有触控工程来实现消息的接收

在网络上搜索到win7多点触控的C#工程代码,研究其实现方式,发现可以用于现有窗口的触控程序接收,但发现C++无法引用C#工程,反而C#引用C++相对简单,于是分析例子程序中的工程设置,发现需要做以下更改才能正确引用C#项目: ? 添加公共运行时支持:

工程?右键?Properties?General?Common Language Runtime Support?Common Language Runtime Support (/clr) ? 添加C#工程引用

工程 ?右键?References?Framework and References?Add New References?Browse?XX.dll ? 消息handle的建立

_gestureHandler=Windows7::Multitouch::Win32Helper::Factory::CreateHandler(System::IntPtr(hwnd));

_gestureHandler->PanBegin+=gcnew

System::EventHandler(OnPanHandler);

_gestureHandler->Pan += gcnew

System::EventHandler(OnPanHandler);

_gestureHandler->PanEnd += gcnew

System::EventHandler(OnPanHandler);

_gestureHandler->RotateBegin += gcnew

System::EventHandler

^>(OnRotateHandler);

_gestureHandler->Rotate += gcnew

System::EventHandler(OnRotateHandler);

_gestureHandler->RotateEnd += gcnew

System::EventHandler(OnRotateEndHandler);

_gestureHandler->ZoomBegin += gcnew

System::EventHandler(OnZoomHandler);

_gestureHandler->Zoom += gcnew

System::EventHandler(OnZoomHandler);

_gestureHandler->ZoomEnd+=gcnew

System::EventHandler(OnZoomHandler);

通过以上代码可以在OnPanHandler函数中处理平移手势,在OnRotateHandler函数中处理旋转手势,在OnZoomHandler函数中处理缩放手势。

使用以上方法解决了消息接收问题,接下来进入了消息处理阶段的代码编写。由于手势操作不区分点数只辨认手势所以在实现单点拖动和多点拖动实现不同功能时又遇到问题。现在必须解决获取触摸点数问题,这时考虑到一下三种解决方案:

? 从C#工程中的窗口处理过程中获取触摸点数

C#工程提供了类似于win7的两种消息处理方法GestureHandler和TouchHandler,前者核心函数为:

WindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)

第一个参数为窗口句柄,第二个为手势类型,第三个没有使用,第四个用来获取手势信息,此时需要提到此处理方法用到的结构体

public struct GESTUREINFO {

public uint cbSize; // size, in bytes, of this structure (including variable length Args field)

public uint dwFlags; // see GF_* flags

public uint dwID; // gesture ID, see GID_* defines

public IntPtr hwndTarget; // handle to window targeted by this gesture

public POINTS ptsLocation; // current location of this gesture

public uint dwInstanceID; // internally used public uint dwSequenceID; // internally used

public ulong ullArguments; // arguments for gestures whose arguments fit in 8 BYTES

public uint cbExtraArgs; // size, in bytes, of extra arguments, if any, that accompany this gesture }

通过GetGestureInfo(lParam, ref gestureInfo);可以得到存储以上结构体信息的变量gestureInfo,但分析此结构体中具体变量,发现此结构体是用来记录一个手势操作,没有具体的触摸点信息。而窗口过程其他参数也不能得到需要信息,故如使用GestureHandler无法获取触摸点数。 后者核心函数为:

WindowProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam)

第一个参数为窗口句柄,第二个为触摸类型,第三个存储第几个触摸点,第四个用来获取触摸消息。

如果在此函数中设置一个变量就可以获得到触摸点数。单此中处理方法和win7中WM_TOUCH一样需要自己编写手势判断。 综上所述,开是尝试同时在一个窗口挂载两个handle,但调试后发现同一个窗口不能同时挂载两个handle,所以这两种只能选择一种,而TouchHandler明显不能选择,故保留GestureHandler。

? 为OSG窗口设置两个窗口过程一个获取触摸点数信息一个获取手势信息

在上一种解决方案研究过程中发现了之前使用子类化不能接受触摸消息的原因是没有将窗口注册为触摸窗口(RegisterTouchWindow),由此又有了以下想法,在使用GestureHandler接收触摸手势的同时使用子类化来接收WM_TOUCH消息来获得触摸点数,于是编写了新的窗口回调函数并将窗口注册为触摸窗口,但发现手势消息又不能接收了。反复查看代码后发现在win7默认是接收手势消息的,如果注册为触摸窗口后就会改为接收触摸消息,两者为互斥关系。

于是做了新的尝试,在接受完WM_TOUCH消息并存储当前点数信息后马上取消触摸窗口的注册(UnregisterTouchWindow),让其接收手势消息。此方法经过实际调试发现,WM_TOUCH并不是调用一次就会把触摸点数记录下来,二是有几个触摸点数就会调用几次,这就无法知道他到底什么时候会调用完毕。再者当WM_TOUCH处理完后此次触摸消息就不再被手势接收,也就是会丢失touchdown瞬间的手势信息,使手势节后不再准确。

? 屏蔽单点拖动的触摸消息,交给漫游器去处理

在以上方法都行不通的情况下,开始寻找别的出路,继续研究代码,发现有在接收手势之前会有一个手势接收参数的设置:

SetGestureConfig()这里来设置需要接收什么样的手势,而这里涉及到一个结构体:

public struct GESTURECONFIG {

public uint dwID; // gesture ID

public uint dwWant; // settings related to gesture ID that are to be turned on public uint dwBlock; // settings related to gesture ID that are to be turned off }

此结构体是用来设置具体需要接收和屏蔽的手势,于是做以下尝试,把单点拖动的手势屏蔽掉,而利用漫游器可以处理单点拖动来实现单点和多点拖动的区分,具体代码如下:

GESTURECONFIG[] gestureConfig = new[] {

new GESTURECONFIG { dwID = User32.GID_PAN , dwWant = User32.GC_ALLGESTURES, dwBlock

=User32.GC_PAN_WITH_SINGLE_FINGER_VERTICALLY|User32.GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY } ,

new GESTURECONFIG{dwID = User32.GID_ROTATE , dwWant = User32.GC_ALLGESTURES,dwBlock =0 } };

SetGestureConfig(ControlHandle, 0, 2, gestureConfig, (uint)Marshal.SizeOf(typeof(GESTURECONFIG)));

经测试此方法可行。

解决了平移问题之后进行旋转功能的实现,需要做的是定轴旋转 可以通过以下公式来获得:MxNew = MxOld*(-tran*rotate*tran);tran的大小就是旋转轴的位置,比如轴位于(x,y,0),那么就需要maketraslate(x,y,0);

缩放相对来说易于解决这里就不做具体分析。

七、 测试阶段

本文来源:https://www.bwwdw.com/article/6vtw.html

Top