开发多点触控MFC应用程序
当下计算机变得越来越智能化,越来越无所不能,触摸屏的普及只是时间问题了。 虽然鼠标和键盘不会很快就离开人们的视野,毕竟人们使用鼠标跟键盘已经成为一种习惯,但是处理信息或者说操作计算机的其他方法也层出不穷——比如触控技术。从硬件技术的角度来讲,触控技术是可行的,随着最新一代的触摸屏技术,接触技术现在已经存在,如今Web开发人员可以利用Silverlight 3提供多点触摸功能。可惜的是,只有Windows 7同时支持Silverlight和多点触摸能力。这个因素大大制约了这部分功能的运用和推广,不过如果多点触摸继续流行开来的话,情况会有所改变的,不过现在Windows 7为触摸屏技术提供了发挥的软件空间,Windows 7让屏幕触控技术成为可能。
借助 Windows 7 和触摸感应屏幕,您只需使用手指即可在电脑上翻阅在线报纸,翻阅相册,拖拽文件和文件夹。 多年来在 Windows 中早已开始采用触控技术。Windows 7 进一步将其扩展到电脑的每个角落。“开始”菜单和任务栏现在都采用了加大显示、易于手指触摸的图标。常用的 Windows 7 程序也都可以执行触摸操作。您甚至可以在“画图”中使用手指来画图!
Windows 触控功能还可以识别多点触控(使用适当的监视器)。是否需要缩小图片将手指捏在一起即可。是否要用鼠标右键单击某项内容?用一个手指触及该内容,并用第二个手指点击屏幕即可。Windows 触控功能仅在 Windows 7 的家庭高级版、专业版和旗舰版中提供。
什么是触控操作呢?触控指 Windows 允许你使用手指直接与计算机进行交互的方式。与使用鼠标、键盘或手写笔相比,触摸更加方便、自然、具有吸引力。也更符合人们日常的交流习惯。而Windows 7中引入了全新的多点触摸的概念。多点触摸又称多点触控,简而言之可以理解为一个屏幕多点操作。多点触摸不但是两个点或者几个点同时应用到屏幕上这么一点点便利,由于是多点触摸,所以他能感应到手指滑动的快慢以及力度(力度用触摸点的多少转换来实现),从而操作系统应用起来更加人性化。传统的触控屏幕一次只能判断一个触控点,若同时有两个以上的点被触碰,就不能做出正确反应,或者说反应混乱了。多重触控的任务可以分解为两个方面的工作,一是同时采集多点信号,二是对每路信号的意义进行判断,也就是所谓的手势识别。最早在Apple公司的iPhone上应用。多点触摸技术是一项划时代的输入方式。可以设想随着全息投影的发展,完全有可能实现屏幕在空中投影,而用户直接在投影中触控电脑,科幻电影中的场景将会变成现实。
下面我们来看一段关于用手玩转win7触摸屏多点触摸屏电脑,现在您是不是被这种绚丽界面所吸引,被这种便捷而有趣的操作所震撼?
我们自己打造的程序里面如何使用多点触摸技术呢,就是让我们的程序也具有此种多点触摸功能,用手就可以玩转我们的应用程序呢?
好了,不多说了,下面我用实例来讲解这些功能实现:
首先:我们基于MFC新建一个简单窗体工程,如下图所示:
单击"finish"完成工程创建
接下来我们向应用程序添加触控支持,表现以下两点:
1.我们正在构建的应用程序需要支持触控的硬件,因此我们需要在应用程序中查看这一点。
2.在 Scratchpad.cpp 中,在 CScratchPadApp::InitInstance(): 后添加以下检查代码:
BYTE digitizerStatus = (BYTE) GetSystemMetrics(SM_DIGITIZER);
if ((digitizerStatus & (0x80 + 0x40)) == 0)
//堆栈就绪+多触点
{
AfxMessageBox(L"No touch input is currently available."); return false; } BYTE nInputs = (BYTE) GetSystemMetrics(SM_MAXIMUMTOUCHES);
CString str; str.Format(L"Touch input available with %d touch points.", nInputs); AfxMessageBox(str); return true;
3. 您可以看到,除了查看触控可用性和就绪情况以外,我们还可以发现硬件支持的触控输入数量。
4. 编译并运行。
5. 根据机器上触控输入的数量,您应该看到类似下图的输出:
6.为了注册应用程序客户端视图窗口来接收触控消息,我们需要调用 MFC 函数 CWnd::RegisterTouchWindow()。我们将在视图创建之后执行该操作,即在 OnCreate() 事件处理程序中完成。
切换到 Class View 并选择 CChildView 类。
在 Properties 页面中,转到 Message 属性表并导航到 WM_CREATE,然后从下拉框中添加 OnCreate() 消息处理程序:
7. 在 CChildView::OnCreate() 处理程序中添加以下代码,注册视图窗口的触控输入:
if (!RegisterTouchWindow()) { ASSERT(FALSE); }
注意:调用 CWnd::RegisterTouchWindow() 注册(和注销)窗口,使其具有触控功能,允许接收低级 WM_TOUCH 消息。
8. 因为我们注册了视图来接收触控输入,我们必须重写接收每个触控消息的处理程序:CWnd::OnTouchInput()。
该处理程序接收来自 Windows Touch 的单个输入,并在应用程序处理该消息时返回 TRUE;否则返回 FALSE。
9.在 ChildView.h 中添加该方法声明:
// 重写
protected: virtual BOOL OnTouchInput(CPoint pt, int nInputNumber, int nInputsCount, PTOUCHINPUT pInput);
10.在 ChildView.cpp 中提供相应的实现:
BOOL CChildView::OnTouchInput(CPoint pt, int nInputNumber, int nInputsCount, PTOUCHINPUT pInput)
{
// 在此处输入消息处理Tocuh return false;
}
然后我们向项目添加笔画源和头文件,并使用手指绘制线条
我们希望将手指作为多输入设备。我们希望为每个触摸屏幕的手指绘制一条线。为此,我们将使用两个笔画集合。一个集合保存完成的笔画(线条),另一个集合保存正在绘制的线条。触摸屏幕的每个手指都指向 m_StrkColDrawing 集合中的笔画。当我们从屏幕拿开手指时,我们将手指的笔画从m_StrkColDrawing 移动到 m_StrkColFinished 集合。此外,如果用户在多点触控监视器上使用两个以上的手指,我们希望笔画有不同的颜色。
1. 在 Starter 文件夹中,您将找到两个文件:Stroke.h 和 Stroke.cpp。将它们复制到项目文件夹下并使用“Add Existing item…”将其添加到项目中。
2. 类似地,向项目添加 StrokeCollection.h 和 StrokeCollection.cpp。
3. 将 "Stroke.h" 和 "StrokeCollection.h" 放在 StdAfx.h 头文件结束处。
#include "Stroke.h"
#include "StrokeCollection.h"
4. 将这些私有成员变量定义添加到 ChildView.h 中:
private: int m_iCurrColor; // 当前笔触的颜色 CStrokeCollection m_StrkColFinished; // 用户完成输入笔画,接触笔触焦点 CStrokeCollection m_StrkColDrawing; // 收集用户当前的绘图笔画
5.重要:我们必须初始化当前颜色。我们将在 ChildView.cpp 的 CChildView 构造函数中完成该操作:
CChildView::CChildView() :m_iCurrColor(0)
{ }
6. 要绘制完成的集合,我们将以下调用添加到 CChildView::OnPaint() 处理程序的末尾。它将绘制所有已完成的笔画。
m_StrkColFinished.Draw(&dc);
7. 我们需要处理所有接收到的触控输入消息,因此我们处理感兴趣的所有事件:touch input down、move 和 up。
8. 在 CChildView.h 中,声明以下方法,我们将用来处理不同的触控输入事件:
protected:
// 不同触摸输入事件的处理程序
BOOL OnTouchInputDown(CPoint pt, PTOUCHINPUT pInput);
BOOL OnTouchInputMove(CPoint pt, PTOUCHINPUT pInput);
BOOL OnTouchInputUp(CPoint pt, PTOUCHINPUT pInput);
9.在 CChildView.cpp 中,添加每个触控输入处理程序的实现:
BOOL CChildView::OnTouchInputDown(CPoint pt, PTOUCHINPUT pInput)
{ // 创建新的画笔,并为他添加指针 COLORREF strokeColor = GetTouchColor((pInput->dwFlags & TOUCHEVENTF_PRIMARY) != 0); CStroke* pStrkNew = new CStroke(pInput->dwID, strokeColor); pStrkNew->Add(pt); // 添加新的笔触收集画板中的笔画 m_StrkColDrawing.Add(pStrkNew); return true;
} BOOL CChildView::OnTouchInputMove(CPoint pt, PTOUCHINPUT pInput)
{ // 在绘图查找笔画收集笔触。 int strokeIndex = m_StrkColDrawing.FindStrokeById(pInput->dwID); if (strokeIndex >= 0) { CStroke* pStrk = m_StrkColDrawing[strokeIndex]; // 增加笔画触摸点 pStrk->Add(pt); // 绘制最后的笔画 pStrk->Draw(GetDC()); } return true;
} BOOL CChildView::OnTouchInputUp(CPoint pt, PTOUCHINPUT pInput)
{ // 在绘图查找笔画收集笔触. int strokeIndex = m_StrkColDrawing.FindStrokeById(pInput->dwID); if (strokeIndex >= 0) { CStroke* pStrkCopy = m_StrkColDrawing[strokeIndex]; // 从绘图上移除笔画. m_StrkColDrawing.RemoveAt(strokeIndex); // 在已经完成的笔画中增加一画. m_StrkColFinished.Add(pStrkCopy); } return true;
}
10.在 CChildView.cpp 中,修改 CChildView::OnTouchInput() 的实现,以根据需要调用每个触控输入处理程序:
BOOL CChildView::OnTouchInput(CPoint pt, int nInputNumber, int nInputsCount, PTOUCHINPUT pInput)
{ if ((pInput->dwFlags & TOUCHEVENTF_DOWN) == TOUCHEVENTF_DOWN) // 触摸按下事件 { return OnTouchInputDown(pt, pInput); } else if ((pInput->dwFlags & TOUCHEVENTF_MOVE) == TOUCHEVENTF_MOVE) // 触摸移动事件 { return OnTouchInputMove(pt, pInput); } else if ((pInput->dwFlags & TOUCHEVENTF_UP) == TOUCHEVENTF_UP) // 触摸移动事件 { return OnTouchInputUp(pt, pInput); } return false;
}
11.注意,调用了 GetTouchColor() 方法,但它尚未实现。当用户移动应用程序窗口上的多个手指时,该方法负责处理钢笔的颜色。在 CChildView.h 中添加该方法的声明
private:
COLORREF GetTouchColor(bool bPrimaryContact);
12.以下是 CChildView.cpp 的实现:
COLORREF CChildView::GetTouchColor(bool bPrimaryContact)
{
static COLORREF c_arrColor[] = // 数组中的颜色 {
RGB(255, 0, 0), // 红 RGB(0, 255, 0), // 绿 RGB(0, 0, 255), // 蓝 RGB(0, 255, 255), // 青 RGB(255, 0, 255), // 品红 RGB(255, 255, 0) // 黄 };
COLORREF color;
if (bPrimaryContact) {
// 主要接触中绘制黑色.
color = RGB(0,0,0); // 黑 }
else {
// 保存当前的次要颜色.
color = c_arrColor[m_iCurrColor];
// 移动到数组中的下一个颜色
m_iCurrColor = (m_iCurrColor + 1) % (sizeof(c_arrColor)/sizeof(c_arrColor[0])); }
return color;
}
13.最后,由于我们已经动态创建了许多笔画,我们需要确保每个笔画在应用程序退出之前都被销毁,因此我们在 CChildView‘ 的析构函数中包含以下内容:
CChildView::~CChildView()
{ for (int i = 0; i < m_StrkColDrawing.GetCount(); ++i) { delete m_StrkColDrawing[i]; } for (int i = 0; i < m_StrkColFinished.GetCount(); ++i) { delete m_StrkColFinished[i]; }
}
4.现在编码部分已经全部完成,可以开始实验刚才实现的应用程序了。
15.编译并运行应用程序。它应该如下所示:
==============================stdafx.h=====================
#pragma once
#include "afxtempl.h" #include <vector>
using namespace std; class CStroke : public CArray<POINT, POINT>
{
public: CStroke(int id, COLORREF clr); ~CStroke(void); COLORREF GetColor() const { return m_clr; } int GetId() const { return m_id; } void Draw(CDC* pDC) const; private: COLORREF m_clr; // Stroke color int m_id; // Stroke ID
};
==============================stdafx.cpp=====================
#include "StdAfx.h"
#include "Stroke.h" CStroke::CStroke(int id, COLORREF color): m_clr(color), m_id(id)
{ SetSize(0, 1000);
} void CStroke::Draw(CDC* pDC) const
{ if (GetCount() <= 0) return; CPen pen(PS_SOLID, 3, m_clr); CPen* oldPen = pDC->SelectObject(&pen); pDC->MoveTo(GetAt(0)); pDC->Polyline(GetData(), GetCount()); pDC->SelectObject(oldPen);
} CStroke::~CStroke(void)
{
}
==============================StrokeCollection.h=====================
#pragma once
#include "afxtempl.h"
#include "Stroke.h" class CStrokeCollection : public CArray<CStroke*, CStroke*>
{
public: CStrokeCollection(void); ~CStrokeCollection(void); // Search the collection for given ID. int FindStrokeById(int id) const; // Draw the collection of the strokes. void Draw(CDC* pDC) const; };
==============================StrokeCollection.h=====================
#include "StdAfx.h"
#include "StrokeCollection.h" CStrokeCollection::CStrokeCollection(void)
{
} CStrokeCollection::~CStrokeCollection(void)
{
} int CStrokeCollection::FindStrokeById(int id) const
{ for (int i = 0; i < GetCount(); i++) { if (GetAt(i)->GetId() == id) { return i; } } return -1;
}
void CStrokeCollection::Draw(CDC* pDC) const
{ for (int i = 0; i < GetCount(); ++i) { GetAt(i)->Draw(pDC); }
}
相关文章:
开发多点触控MFC应用程序
当下计算机变得越来越智能化,越来越无所不能,触摸屏的普及只是时间问题了。 虽然鼠标和键盘不会很快就离开人们的视野,毕竟人们使用鼠标跟键盘已经成为一种习惯,但是处理信息或者说操作计算机的其他方法也层出不穷——比如触控技术…...
使用nlohmann json库进行序列化与反序列化
nlohmann源码仓库:https://github.com/nlohmann/json使用方式:将其nlohmann文件夹加入,包含其头文件json.hpp即可demo #include <iostream> #include "nlohmann/json.hpp" #include <vector>using json nlohmann::js…...
高教社杯数模竞赛特辑论文篇-2012年A题:葡萄酒的评价(附获奖论文)
目录 摘 要 一、问题重述 二、问题分析 2.1 问题一的分析 2.2 问题二的分析...
手写RPC——数据序列化工具protobuf
手写RPC——数据序列化工具protobuf Protocol Buffers(protobuf)是一种用于结构化数据序列化的开源库和协议。下面是 protobuf 的一些优点和缺点: 优点: 高效的序列化和反序列化:protobuf 使用二进制编码,…...
【MATLAB第70期】基于MATLAB的LightGbm(LGBM)梯度增强决策树多输入单输出回归预测及多分类预测模型(全网首发)
【MATLAB第70期】基于MATLAB的LightGbm(LGBM)梯度增强决策树多输入单输出回归预测及多分类预测模型(全网首发) 一、学习资料 (LGBM)是一种基于梯度增强决策树(GBDT)算法。 本次研究三个内容,分别是回归预测,二分类预测和多分类预…...
Linux进程间通信的几种方式
分析&回答 管道(pipe)以及有名管道:管道可用于有亲缘关系进程间通信,有名管道克服了管道没有名字的限制,因此具有管道的所有功能之外,它还允许无亲缘关系进程间通信。信号(Signalÿ…...
Android 13.0 Launcher3定制之双层改单层(去掉抽屉式一)
1.概述 在13.0的系统产品开发中,对于在Launcher3中的抽屉模式也就是双层模式,在系统原生的Launcher3中就是双层抽屉模式的, 但是在通过抽屉上滑的模式拉出app列表页,但是在一些产品开发中,对于单层模式的Launcher3的产品模式也是常用的功能, 所以需要了解抽屉模式,然后修…...
【uniapp 配置启动页面隐私弹窗】
为什么需要配置 原因 根据工业和信息化部关于开展APP侵害用户权益专项整治要求,App提交到应用市场必须满足以下条件: 1.应用启动运行时需弹出隐私政策协议,说明应用采集用户数据 2.应用不能强制要求用户授予权限,即不能“不给权…...
2分钟讲清楚C#的委托, C语言的函数指针,Java的函数式接口
很多小伙伴学习C# 的委托时往往一头雾水, 不明白委托是什么, 有什么作用, 今天我就用2分钟讲清楚 这是一个C# 的控制台程序 定义一个最简单的委托 delegate int Calculate(int a, int b); 这相当于定义了一个Calculate类型, 只不过这个类型需要传入2个int类型的参数 返回值也…...
华为云物联网平台微信小程序开发教程2.0【完整详细教程】
一、简介 在之前曾发布过一篇文章“华为云物联网平台的微信小程序开发”,在最近接到部分用户私信在使用开发过程中出现的问题,例如API访问的"401"现象等问题,在重新查看上面的文章教程时发现教程内容的步骤不详细,现对教…...
Laravel 模型1对1关联 1对多关联 多对多关联 ⑩①
作者 : SYFStrive 博客首页 : HomePage 📜: THINK PHP 📌:个人社区(欢迎大佬们加入) 👉:社区链接🔗 📌:觉得文章不错可以点点关注 ὄ…...
【分类】分类性能评价
评价指标 1、准确率、召回率、精确率、F-度量、ROC 属于各类的样本的并不是均一分布,甚至其出现概率相差很多个数量级,这种分类问题称为不平衡类问题。在不平衡类问题中,准确率并没有多大意义,我们需要一些别的指标。 通…...
M1 Pro 新芯片安装python2 方案汇总
前言:磨刀不误砍柴工,环境装好,才能打工。M1 Pro 新芯片安装python2 文章目录 方案一 docker 容器构造环境(如果涉及本地两个仓库需要关联则不适用)方案二 使用 pyenv 🚀 作者简介:作为某云服务…...
无涯教程-Android - Broadcast Receivers
Broadcast Receivers 仅响应来自其他应用程序或系统本身的广播消息,这些消息有时称为events或intents。例如,应用程序还可以启动广播,以使其他应用程序知道某些数据已下载到设备并可供他们使用,因此广播接收器将拦截此通信并启动适…...
【Pytorch】Tutorials个人翻译集合
本文记录Pytorch Tutorials文档的翻译文章集合. 由于本人英语能力有限(only-CET4),欢迎指正翻译中的错误。 Introduction to PyTorch 【Pytorch】Pytorch文档学习1:Tensors 【Pytorch】Pytorch文档学习2:DATASETS &a…...
WordPress(6)网站侧边栏倒计时进度小工具
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 效果图在这里插入图片描述一、添加位置二、主题style.css文件中添加美化1.引入库2.添加自定义的HTML模块效果图 提示:以下是本篇文章正文内容,下面案例可供参考 一、添加位置 在主题中 child.js…...
uniapp小程序单页面改变手机电量,头部通知的颜色效果demo(整理)
onShow(){ // 改变电池的颜色 wx.setNavigationBarColor({ frontColor: ‘#ffffff’, //只支持两种颜色 backgroundColor: ‘#ffffff’, animation: { duration: 1 } }) }...
数据挖掘导论学习笔记1(第1 、2章)
参考:https://blog.csdn.net/u013232035/article/details/48281659?spm1001.2014.3001.5506 和《数据挖掘导论》学习笔记(第1-2章)_时机性样本_schdut的博客-CSDN博客 第1章 绪论 数据挖掘是一种技术,它将传统的数据分析方法…...
从零开始,探索C语言中的字符串
字符串 1. 前言2. 预备知识2.1 字符2.2 字符数组 3. 什么是字符串4. \04.1 \0是什么4.2 \0的作用4.2.1 打印字符串4.2.2 求字符串长度 1. 前言 大家好,我是努力学习游泳的鱼。你已经学会了如何使用变量和常量,也知道了字符的概念。但是你可能还不了解由…...
Ubuntu学习---跟着绍发学linux课程记录(第二部分)
文章目录 7 文件权限7.1 文件的权限7.2 修改文件权限7.3 修改文件的属主 8、可执行脚本8.2Shell脚本8.3python脚本的创建 9Shell9.1Shell中的变量9.2 环境变量9.3用户环境变量 学习链接: Ubuntu 21.04乌班图 Linux使用教程_60集Linux课程 所有资料在 http://afanihao.cn/java …...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
