1 缘起
激光手势导航项目,控制常规办公软件和特定开发软件的窗口切换及其翻页展示,用于多媒体的会议、论坛、课堂教学、指挥调度等场景。
开发之初,设想用Python语言开发,拟用PyUSB、Kivy/Tkinter、python-pptx/word/excel/acrobat,PyKeyboard/PyMouse等库,深入之后发现这些库多是用C++开发打包的,而且不新、不全面,作为解释语言也没有编译语言C++运行效率高,与其特制一些把C++库弥补Python应用,倒不如运用顺手的C++更快捷,反正底层已经采用C++实现了WinUSB远程指令数据接收。特别是Window窗口的操控,WinAPI更为经典直接,Python也是在应用它的包装库。于是回到了C++开发实现。
主要是两类窗口:引导窗口和展示窗口。
引导窗口是核心,控制展示窗口切换和翻页,但只能小视野指示。
展示窗口,展示常规办公软件特定开发软件的窗口,切换及其翻自如,客户体验要好。
2 基础
开发环境:这里选用EnbarcaderoRadStudio,核心C++Builder。
操控技术:WinAPI窗口控制函数及其键盘操控模拟,WinUSB多线程操控指令接收。
3 引导窗口设计
3.1 窗口设计思想
桌面顶层透明小视窗动态图文指示。透明视窗通过设置窗口属性加以实现:
AlphaBlend=true;
AlphaBlendValue=100;
3.2 GIF动画实现
以可视化控件TTimer加载gif图片,自定义TGIFImage实现动画效果:
相关头文件:
#include <Vcl.Imaging.GIFImg.hpp>
class TfmShow : public TForm
{
__published: // IDE-managed Components
TImage *imgDrct;
private: // User declarations
TGIFImage *m_pGifImg;
}
相关CPP文件:
imgDrct->Picture->LoadFromFile("normal.gif");
m_pGifImg = dynamic_cast<TGIFImage*>(imgDrct->Picture->Graphic);
m_pGifImg->AnimationSpeed =100;
m_pGifImg->Transparent = true;
m_pGifImg->Animate = true;
3.3 窗口造型设计
自适图片大小、无边框、最前:
fmShow->AutoSize = true;
fmShow->BorderStyle = bsNone;
fmShow->FormStyle = fsStayOnTop;
靠右、居中、椭圆外形,以WinAPI窗口函数实现,固定窗口在一个相应形状的框内:
void_fastcall TfmShow::FormCreate(TObject *Sender) {
HWND hWnd = FindWindow(NULL, L"便捷操控"); // 指示窗口及其定位
int cx = GetSystemMetrics(SM_CXSCREEN);
int cy = GetSystemMetrics(SM_CYSCREEN);
RECT rect;
GetWindowRect(hWnd,&rect);
cx -= rect.right - rect.left + 10;
cy = (cy - rect.bottom + rect.top) / 2;
SetWindowPos(hWnd, HWND_TOP, cx, cy, 0, 0, SWP_NOSIZE);
int x = rect.right - rect.left;
int y = rect.bottom - rect.top;
HRGN hRgn = CreateRoundRectRgn(0, 0, 2*x, y, x, y); // 圆角矩形
//HRGN hRgn = CreateEllipticRgn(0, 0, x, y); // 椭圆形
SetWindowRgn(hWnd, hRgn, TRUE);
}
AERO通明玻璃效果--GalssFrame:
fmShow->GlassFrame->Enabled = true;
fmShow->GlassFrame->SheetOfGlass = true;
3.4 水波涟漪特效
PS动画特定制作
4 展示窗口控制
4.1 窗口展示的类型划分
便于控制起见,按同类文档的窗口显示方式区分多窗口MDI和单窗口SDI两类,MDI视窗如:PPT、word、excel、myEclipse等,SDI视窗如acrobat、notepad++、rapidStudio、googleChrom、fireFox等。
4.2 窗口桌面顶层显示
相关线程可能存在阻塞,使用SetForegroundWindow并不一定能把相关视窗带到最前显示,可先操控其线程再调用SetForegroundWindow():
BOOL SetTopWindow(HWND hWnd) { // 设置窗口到最顶层[参数:窗口句柄]
HWND hForeWnd = GetForegroundWindow();
DWORD dwForeID = GetWindowThreadProcessId(hForeWnd, NULL);
DWORD dwCurID = GetCurrentThreadId();
AttachThreadInput(dwCurID, dwForeID, TRUE);
ShowWindow(hWnd, SW_MAXIMIZE); //SW_SHOWNORMAL
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
SetForegroundWindow(hWnd);
AttachThreadInput(dwCurID, dwForeID, FALSE);
return TRUE;
}
4.3 不同类型的窗口展示控制
控制只显示同种类型视窗中最靠前的一个,每次操作最大化显示一个窗口。
使用FindWindow()函数,按窗口类如PPTFrameClass,寻找并得到相应窗口句柄,窗口类可用工具软件如PpsSpyUI得到。
4.4 相同类型的窗口间展示控制
模拟键盘操作,MDI类型视窗,采用ctrl+F6:
keybd_event(0x11, 0, 0, 0);
keybd_event(0x75, 0, 0, 0);
keybd_event(0x11, 0, 2, 0);
keybd_event(0x75, 0, 2, 0);
SDI类型视窗,采用ctrl+Tab:
keybd_event(0x11, 0, 0, 0);
keybd_event(0x09, 0, 0, 0);
keybd_event(0x11, 0, 2, 0);
keybd_event(0x09, 0, 2, 0);
4.5 相同类型的窗口内展示控制
模拟键盘操作,上下翻页采用gageUp/gageDown:
keybd_event(0x21, 0, 0, 0);
keybd_event(0x21, 0, 2, 0);
keybd_event(0x22, 0, 0, 0);
keybd_event(0x22, 0, 2, 0);
直达首末页,采用ctrl+home/ctrl+end:
keybd_event(0x11, 0, 0, 0);
keybd_event(0x24, 0, 0, 0);
keybd_event(0x11, 0, 2, 0);
keybd_event(0x24, 0, 2, 0);
keybd_event(0x11, 0, 0, 0);
keybd_event(0x23, 0, 0, 0);
keybd_event(0x11, 0, 2, 0);
keybd_event(0x23, 0, 2, 0);
4.6 PPT文档的播放操控
模拟键盘操作,进入shift+F5:
keybd_event(0x10, 0, 0, 0);
keybd_event(0x74, 0, 2, 0);
keybd_event(0x10, 0, 0, 0);
keybd_event(0x74, 0, 2, 0);
上下翻页采用gageUp/gageDown:
keybd_event(0x21, 0, 0, 0);
keybd_event(0x21, 0, 2, 0);
keybd_event(0x22, 0, 0, 0);
keybd_event(0x22, 0, 2, 0);
直达首末页,采用ctrl+home/ctrl+end:
keybd_event(0x24, 0, 0, 0);
keybd_event(0x24, 0, 2, 0);
keybd_event(0x23, 0, 0, 0);
keybd_event(0x23, 0, 2, 0);
退出,采用esc:
keybd_event(0x1b, 0, 0, 0);
keybd_event(0x1b, 0, 2, 0);