Пример нескольких распознавителей
В этом примере демонстрируются расширенные возможности api api автоматизации компьютеров MicrosoftTablet, используемого для распознавания рукописного ввода .
Это включает в себя следующее:
- Перечисление установленных распознавителей
- Создание контекста распознавателя с помощью определенного распознавателя языка
- Сериализация результатов распознавания с помощью коллекции росчерков
- Упорядочение коллекций росчерков в настраиваемую коллекцию в объекте InkDisp
- Сериализация объектов рукописного ввода в и получение их из файла сериализованного формата рукописного ввода (ISF)
- Настройка направляющих ввода распознавателя
- Использование синхронного и асинхронного распознавания
Рукописные заголовки
Во-первых, добавьте заголовки для интерфейсов автоматизации планшетного ПК. Они устанавливаются вместе с пакетом sdk microsoft Windows XP Tablet PC Edition.
#include <msinkaut.h>
#include <msinkaut_i.c>
Файл EventSinks.h определяет интерфейсы IInkEventsImpl и IInkRecognitionEventsImpl.
#include "EventSinks.h"
Перечисление установленных распознавителей
Метод LoadMenu приложения заполняет меню Создать новые штрихи доступными распознавателями. Создается Объект InkRecognizers . Если свойство Languages объекта InkRecognizers не является пустым, распознаватель является распознавателем текста и значение его свойства 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 приложения создает и инициализирует новый контекст распознавателя, а также настраивает руководства, поддерживаемые соответствующим языком. Метод CreateRecognizerContext объекта IInkRecognizer создает объект 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 и присоединяет его к контексту распознавателя.
Сохранение коллекции strokes для контекста распознавателя
Метод приложения SaveStrokeCollection
проверяет наличие существующего контекста распознавателя и завершает распознавание текущей коллекции штрихов. Затем коллекция InkStrokes добавляется в CustomStrokes объекта ink.
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)))
...