多个识别器示例
此示例演示用于 手写 识别的 MicrosoftTablet PC Automation 应用程序编程接口 (API) 的高级功能。
包括以下各项:
- 枚举已安装的识别器
- 使用特定语言 识别器 创建识别器上下文
- 使用笔划集合序列化识别结果
- 将笔划集合组织到 InkDisp 对象中的自定义集合中
- 将 墨迹 对象序列化为 ,并从 墨迹序列化格式 (ISF) 文件检索它们
- 设置识别器输入参考线
- 使用同步和异步识别
墨迹页眉
首先,包括适用于 Tablet PC 自动化接口的标头。 它们随 Microsoft Windows XP 平板电脑版软件开发工具包 (SDK) 一起安装。
#include <msinkaut.h>
#include <msinkaut_i.c>
EventSinks.h 文件定义 IInkEventsImpl 和 IInkRecognitionEventsImpl 接口。
#include "EventSinks.h"
枚举已安装的识别器
应用程序的 LoadMenu 方法使用可用的识别器填充“创建新笔划”菜单。 创建 InkRecognizers 。 如果 InkRecognizers 对象的 Languages 属性不为空,则识别器是文本识别器,并且其 Name 属性的值将添加到菜单中。
// Create the enumerator for the installed recognizers
hr = m_spIInkRecognizers.CoCreateInstance(CLSID_InkRecognizers);
...
// Filter out non-language recognizers by checking for
// the languages supported by the recognizer - there is not
// any if it is a gesture or object recognizer.
CComVariant vLanguages;
if (SUCCEEDED(spIInkRecognizer->get_Languages(&vLanguages)))
{
if ((VT_ARRAY == (VT_ARRAY & vLanguages.vt)) // it should be an array
&& (NULL != vLanguages.parray)
&& (0 < vLanguages.parray->rgsabound[0].cElements)) // with at least one element
{
// This is a language recognizer. Add its name to the menu.
CComBSTR bstrName;
if (SUCCEEDED(spIInkRecognizer->get_Name(&bstrName)))
...
}
}
创建墨迹收集器
应用程序的 OnCreate 方法创建 InkCollector 对象,将其连接到其事件源,并启用墨迹收集。
// Create an ink collector object.
hr = m_spIInkCollector.CoCreateInstance(CLSID_InkCollector);
// Establish a connection to the collector's event source.
hr = IInkCollectorEventsImpl<CMultiRecoApp>::DispEventAdvise(m_spIInkCollector);
// Enable ink input in the m_wndInput window
hr = m_spIInkCollector->put_hWnd((long)m_wndInput.m_hWnd);
hr = m_spIInkCollector->put_Enabled(VARIANT_TRUE);
创建识别器上下文
应用程序的 CreateRecoContext 方法创建并初始化新的识别器上下文,并设置关联语言支持的参考线。 IInkRecognizer 对象的 CreateRecognizerContext 方法为语言创建 IInkRecognizerContext2 对象。 如有必要,将替换旧的识别器上下文。 上下文连接到其事件源。 最后,检查识别器上下文的 Capabilities 属性,了解识别器上下文支持的指南。
// Create a recognizer context
CComPtr<IInkRecognizerContext2> spNewContext;
if (FAILED(pIInkRecognizer2->CreateRecognizerContext(&spNewContext)))
return false;
// Replace the current context with the new one
if (m_spIInkRecoContext != NULL)
{
// Close the connection to the recognition events source
IInkRecognitionEventsImpl<CMultiRecoApp>::DispEventUnadvise(m_spIInkRecoContext);
}
m_spIInkRecoContext.Attach(spNewContext.Detach());
// Establish a connection with the recognizer context's event source
if (FAILED(IInkRecognitionEventsImpl<CMultiRecoApp>::DispEventAdvise(m_spIInkRecoContext)))
...
// Set the guide if it's supported by the recognizer and has been created
int cRows = 0, cColumns = 0;
InkRecognizerCapabilities dwCapabilities = IRC_DontCare;
if (SUCCEEDED(pIInkRecognizer->get_Capabilities(&dwCapabilities)))
...
收集笔划和显示识别结果
应用程序的 OnStroke 方法更新墨迹收集器的 InkStrokes ,取消现有的异步识别请求,并在识别器上下文中创建识别请求。
// Add the new stroke to the current collection
hr = m_spIInkStrokes->Add(pIInkStroke);
if (SUCCEEDED(hr))
{
// Cancel the previous background recognition requests
// which have not been processed yet
m_spIInkRecoContext->StopBackgroundRecognition();
// Ask the context to update the recognition results with newly added strokes
// When the results are ready, the recognizer context returns them
// through the corresponding event RecognitionWithAlternates
CComVariant vCustomData;
m_spIInkRecoContext->BackgroundRecognize(vCustomData);
}
应用程序的 OnRecognition
方法将识别请求的结果发送到输出窗口的 UpdateString
方法。
// Update the output window with the new results
m_wndResults.UpdateString(bstrRecognizedString);
删除笔划和识别结果
应用程序的 OnClear 方法从 InkDisp 对象中删除所有笔划和识别结果,并清除窗口。 将删除识别器上下文与其 InkStrokes 集合的关联。
// Detach the current stroke collection from the recognizer context and release it
if (m_spIInkRecoContext != NULL)
m_spIInkRecoContext->putref_Strokes(NULL);
m_spIInkStrokes.Release();
// Clear the custom strokes collection
if (m_spIInkCustomStrokes != NULL)
m_spIInkCustomStrokes->Clear();
// Delete all strokes from the Ink object
// Passing NULL as a stroke collection pointer means asking to delete all strokes
m_spIInkDisp->DeleteStrokes(NULL);
// Get a new stroke collection from the ink object
...
// Ask for an empty collection by passing an empty variant
if (SUCCEEDED(m_spIInkDisp->CreateStrokes(v, &m_spIInkStrokes)))
{
// Attach it to the recognizer context
if (FAILED(m_spIInkRecoContext->putref_Strokes(m_spIInkStrokes)))
...
}
更改识别器上下文
当用户在“创建新笔划”菜单中选择识别器时,将调用应用程序的 OnNewStrokes 方法。 保存当前 InkStrokes 。 如果选择了其他语言识别器,则会创建新的识别器上下文。 然后,新的 InkStrokes 将附加到新的识别器上下文。
// Save the current stroke collection if there is any
if (m_spIInkRecoContext != NULL)
{
// Cancel the previous background recognition requests
// which have not been processed yet
m_spIInkRecoContext->StopBackgroundRecognition();
// Let the context know that there'll be no more input
// for the attached stroke collection
m_spIInkRecoContext->EndInkInput();
// Add the stroke collection to the Ink object's CustomStrokes collection
SaveStrokeCollection();
}
...
// If a different recognizer was selected, create a new recognizer context
// Else, reuse the same recognizer context
if (wID != m_nCmdRecognizer)
{
// Get a pointer to the recognizer object from the recognizer collection
CComPtr<IInkRecognizer> spIInkRecognizer;
if ((m_spIInkRecognizers == NULL)
|| FAILED(m_spIInkRecognizers->Item(wID - ID_RECOGNIZER_FIRST,
&spIInkRecognizer))
|| (false == CreateRecoContext(spIInkRecognizer)))
{
// restore the cursor
::SetCursor(hCursor);
return 0;
}
// Update the status bar
m_bstrCurRecoName.Empty();
spIInkRecognizer->get_Name(&m_bstrCurRecoName);
UpdateStatusBar();
// Store the selected recognizer's command id
m_nCmdRecognizer = wID;
}
然后,它调用 StartNewStrokeCollection,这将创建一个空 的 InkStrokes 并将其附加到识别器上下文。
保存识别器上下文的笔划集合
应用程序的 SaveStrokeCollection
方法检查现有的识别器上下文,并完成对当前笔划集合的识别。 然后将 InkStrokes 集合添加到墨迹对象的 CustomStrokes 中。
if (m_spIInkRecoContext != NULL)
{
if (SUCCEEDED(m_spIInkStrokes->get_Count(&lCount)) && 0 != lCount)
{
CComPtr<IInkRecognitionResult> spIInkRecoResult;
InkRecognitionStatus RecognitionStatus;
if (SUCCEEDED(m_spIInkRecoContext->Recognize(&RecognitionStatus, &spIInkRecoResult)))
{
if (SUCCEEDED(spIInkRecoResult->SetResultOnStrokes()))
{
CComBSTR bstr;
spIInkRecoResult->get_TopString(&bstr);
m_wndResults.UpdateString(bstr);
}
...
}
}
// Detach the stroke collection from the old recognizer context
m_spIInkRecoContext->putref_Strokes(NULL);
}
// Now add it to the ink's custom strokes collection
// Each item (stroke collection) of the custom strokes must be identified
// by a unique string. Here we generate a GUID for this.
if ((0 != lCount) && (m_spIInkCustomStrokes != NULL))
{
GUID guid;
WCHAR szGuid[40]; // format: "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
if (SUCCEEDED(::CoCreateGuid(&guid))
&& (::StringFromGUID2(guid, szGuid, countof(szGuid)) != 0))
{
CComBSTR bstrGuid(szGuid);
if (FAILED(m_spIInkCustomStrokes->Add(bstrGuid, m_spIInkStrokes)))
...