Если у вас есть интерес, как реализовать такой клиент с помощью Qt C++, то этот пост для вас.
Я не стал заострять внимания на тех моментах Qt, которые и так хорошо описаны. В статье я попытался раскрыть, как создавать асинхронные классы в Qt на базе конкретного примера.
Пример программы
Программа посылает на сервер Yandex Dictioanary Api запросы на перевод слов и затем, по мере поступления ответов от сервиса, выводит их на экран.
#include "Precompiled.h" #include <QtYandexApi/QtYandexApi.h> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QtYandexDictionary yandexDictionary(QtYandexApi::getYandexKeyFromFile("dictKey")); QObject::connect(&yandexDictionary, &QtYandexDictionary::translated, [](const QtYaWordTranslation& wordTranslation) { if (wordTranslation.isError()) qDebug() << wordTranslation.errorString(); else { QtYaWord wordForTranslation = wordTranslation.wordForTranslation(); QtYaTranslatedWord translatedWord = wordTranslation.translatedWord(); qDebug() << "\n***************"; qDebug() << "Word: " << wordForTranslation.wordName(); qDebug() << "Direction: " << wordForTranslation.fromLanguage() << "-" << wordForTranslation.toLanguage(); qDebug() << "Main translation: " << translatedWord.mainTranslation(); qDebug() << "Synonyms: " << translatedWord.synonyms(); qDebug() << "Examples: "; for (const auto& example : translatedWord.examples()) { qDebug() << example.first << "-" << example.second; } } }); QStringList russianWords, englishWords; russianWords << "дом" << "время" << "легенда" << "ключ" << "клавиатура" << "монитор" << "случай" << "один" << "два" << "три" << "четыре" << "пять" << "шесть"; englishWords << "home" << "time" << "legend" << "key" << "keyboard" << "monitor" << "infection" << "one" << "two" << "three" << "four" << "five" << "success"; for (const QString& word : russianWords) { yandexDictionary.translate(QtYaWord(word, "ru", "en")); } for (const QString& word : englishWords) { yandexDictionary.translate(QtYaWord(word, "en", "ru")); } return a.exec(); }
***************
Word: «время»
Direction: «ru» — «en»
Main translation: «time»
Synonyms: («while»)
Examples:
«тромбопластиновое время» — «thromboplastin time»
«принять промежуток времени» — «take a while»***************
Word: «клавиатура»
Direction: «ru» — «en»
Main translation: «keyboard»
Synonyms: («keypad», «pad»)
Examples:
«экранная клавиатура» — «onscreen keyboard»
«ЖКИ-клавиатура» — «LCD keypad»
«цифровая клавиатура» — «numeric pad»***************
Word: «ключ»
Direction: «ru» — «en»
Main translation: «key»
Synonyms: («turnkey», «clue», «wrench», «dongle»)
Examples:
«деблокировочный ключ» — «unblocking key»
«решение под ключ» — «turnkey solution»
«иметь никакой ключ» — «have no clue»
«шестигранный ключ» — «hexagonal wrench»***************
Word: «монитор»
Direction: «ru» — «en»
Main translation: «monitor»
Synonyms: ()
Examples:
«ЭЛТ-монитор» — «CRT monitor»***************
Word: «четыре»
Direction: «ru» — «en»
Main translation: «four»
Synonyms: ()
Examples:
«двадцать четыре часа» — «twenty four hours»***************
Word: «один»
Direction: «ru» — «en»
Main translation: «one»
Synonyms: ()
Examples:
«одна иота» — «one jot»***************
Word: «два»
Direction: «ru» — «en»
Main translation: «two»
Synonyms: ()
Examples:
«две несмешивающиеся жидкости» — «two immiscible liquids»***************
Word: «легенда»
Direction: «ru» — «en»
Main translation: «legend»
Synonyms: («myth»)
Examples:
«древнегреческая легенда» — «ancient Greek legend»
«городская легенда» — «urban myth»***************
Word: «три»
Direction: «ru» — «en»
Main translation: «three»
Synonyms: ()
Examples:
«три мушкетера» — «three musketeers»***************
Word: «случай»
Direction: «ru» — «en»
Main translation: «case»
Synonyms: («event», «occasion», «chance», «occurrence»)
Examples:
«невзвешенный случай» — «unweighted case»
«несчастный случай» — «unfortunate event»
«тот редкий случай» — «that rare occasion»
«слепой случай» — «blind chance»
«редкий случай» — «rare occurrence»***************
Word: «пять»
Direction: «ru» — «en»
Main translation: «five»
Synonyms: ()
Examples:
«семьдесят пять лет» — «seventy five year»***************
Word: «home»
Direction: «en» — «ru»
Main translation: «дом»
Synonyms: ()
Examples:
«ancestral home» — «отчий дом»***************
Word: «шесть»
Direction: «ru» — «en»
Main translation: «six»
Synonyms: ()
Examples:
«шесть сигм» — «six Sigma»***************
Word: «time»
Direction: «en» — «ru»
Main translation: «время»
Synonyms: («раз», «тайм»)
Examples:
«thromboplastin time» — «тромбопластиновое время»
«umpteenth time» — «энный раз»
«second time» — «второй тайм»***************
Word: «key»
Direction: «en» — «ru»
Main translation: «ключевой»
Synonyms: ()
Examples:
«key informants» — «ключевые информанты»***************
Word: «monitor»
Direction: «en» — «ru»
Main translation: «монитор»
Synonyms: («видеомонитор»)
Examples:
«LCD monitor» — «жидкокристаллический монитор»***************
Word: «infection»
Direction: «en» — «ru»
Main translation: «инфекция»
Synonyms: («зараза»)
Examples:
«cholangiogenic infection» — «холангиогенная инфекция»
«this infection» — «эта зараза»***************
Word: «one»
Direction: «en» — «ru»
Main translation: «один»
Synonyms: ()
Examples:
«one jot» — «одна иота»***************
Word: «keyboard»
Direction: «en» — «ru»
Main translation: «клавиатура»
Synonyms: ()
Examples:
«QWERTY keyboard» — «клавиатура QWERTY»***************
Word: «three»
Direction: «en» — «ru»
Main translation: «три»
Synonyms: ()
Examples:
«three musketeers» — «три мушкетера»***************
Word: «four»
Direction: «en» — «ru»
Main translation: «четыре»
Synonyms: ()
Examples:
«twenty-four» — «двадцать четыре»***************
Word: «two»
Direction: «en» — «ru»
Main translation: «два»
Synonyms: ()
Examples:
«two immiscible liquids» — «две несмешивающиеся жидкости»***************
Word: «five»
Direction: «en» — «ru»
Main translation: «пять»
Synonyms: ()
Examples:
«ninety-five» — «девяносто пять»***************
Word: «legend»
Direction: «en» — «ru»
Main translation: «легенда»
Synonyms: («предание», «сказание», «поверье»)
Examples:
«ancient Greek legend» — «древнегреческая легенда»
«ancient legend» — «древнее предание»
«epic legends» — «эпические сказания»
«folk legend» — «народное поверье»***************
Word: «success»
Direction: «en» — «ru»
Main translation: «успех»
Synonyms: («успешность», «удача»)
Examples:
«resounding success» — «оглушительный успех»
«financial success» — «финансовая успешность»
«bring success» — «приносить удачу»***************
Word: «дом»
Direction: «ru» — «en»
Main translation: «house»
Synonyms: ()
Examples:
«дом Стенбока» — «Stenbock house»
Код этого примера, а так же реализация самого клиента — bitbucket.org/milovidov/qtyandexapi.
Реализация клиента
В данном разделе я постараюсь осветить некоторые моменты, специфичные для Qt и позволяющие легко реализовывать асинхронные клиенты.
Интерфейс класса QtYandexDictionary выглядит следующим образом:
#ifndef QTYANDEXDICTIONARY_H #define QTYANDEXDICTIONARY_H #include <QObject> #include "qtyandexapi_global.h" #include "QtYaWord.h" #include "QtYaWordTranslation.h" #include "QtYaLanguages.h" class QNetworkAccessManager; class QTYANDEXAPISHARED_EXPORT QtYandexDictionary : public QObject { Q_OBJECT public: explicit QtYandexDictionary(const QString& yandexApiKey, QObject *pParent = 0); void translate(const QtYaWord& word); void getLanguages(); signals: void translated(const QtYaWordTranslation& translation) const; void languagesGot(const QtYaLanguages& langs) const; private: QString m_yandexApiKey; QNetworkAccessManager* m_pNetworkAccessManager; }; #endif // QTYANDEXDICTIONARY_H
Методы translate и getLanguages асинхронные. Они формируют и отправляют соответствующие запросы к Yandex Dictionary Api и возвращают управление потоку из которого были вызваны.
Сигналы translated и languagesGot эмитятся после получения какого либо результата от Yandex Dictionary Api, либо при невозможности подключиться к этому сервису. В аргументах этих сигналов содержится информация полученная от Яндекса или же текст ошибки при невозможности подключиться к нему.
Если посмотреть на код функции translate класса QtYandexDictionary, то она выглядит следующим образом:
void QtYandexDictionary::translate(const QtYaWord &word) { QtYaTranslationGettor* pYandexTranslationGettor = new QtYaTranslationGettor(m_yandexApiKey, word, m_pNetworkAccessManager, this); connect(pYandexTranslationGettor, &QtYaTranslationGettor::translated, this, &QtYandexDictionary::translated); connect(pYandexTranslationGettor, &QtYaTranslationGettor::translated, pYandexTranslationGettor, &QtYaTranslationGettor::deleteLater); }
Здесь мы видим, что для каждого слова переданного в функцию translate создается уникальный экземпляр класса QtYaTranslationGettor. Этот класс непосредственно занимается переводом слов через Yandex Dictionary Api и так же эмитит сигнал translated. Этот же сигнал, без каких либо изменений пробрасывается наверх к QtYandexDictionary. После эмита этого сигнала экземпляр класса QtYaTranslationGettor удаляется за счет коннекта к его же слоту deleteLater.
Как видим, никаких очередей запросов, никакого контроля создаваемых объектов. Каждый объект имеет свое слово для перевода и сам удаляется после завершения своей работы и отправки результата.
Метода getLanguages реализован аналогично:
void QtYandexDictionary::getLanguages() { QtYandexLanguagesGettor* pYandexLanguagesGettor = new QtYandexLanguagesGettor(m_yandexApiKey, m_pNetworkAccessManager, this); connect(pYandexLanguagesGettor, &QtYandexLanguagesGettor::languagesGot, this, &QtYandexDictionary::languagesGot); connect(pYandexLanguagesGettor, &QtYandexLanguagesGettor::languagesGot, pYandexLanguagesGettor, &QtYandexLanguagesGettor::deleteLater); }
Реализация классов QtYaTranslationGettor и QtYandexLanguagesGettor содержит в себе стандартную практику работы с классом QNetworkAccessManager и работу с JSON в Qt5. Поэтому не буду заострять внимания на деталях их реализации.
Тестирование клиента
В данном разделе я покажу, как можно тестировать асинхронные Qt классы.
Пример тестирования асинхронной функции translate выглядит так:
void TQtYandexApi::testTranslation() { const QString rusHome = QString("Home"); QSignalSpy signalSpy(m_pYandexDictionary, SIGNAL(translated(QtYaWordTranslation))); QMetaObject::Connection connection = connect(m_pYandexDictionary, &QtYandexDictionary::translated, [&](const QtYaWordTranslation& translation) { QVERIFY2(translation.isError() == false, translation.errorString().toStdString().c_str()); QVERIFY(translation.wordForTranslation().wordName() == rusHome); QVERIFY(translation.wordForTranslation().fromLanguage() == "en"); QVERIFY(translation.wordForTranslation().toLanguage() == "ru"); QVERIFY(translation.translatedWord().mainTranslation().toLower() == QString::fromUtf8("дом")); }); m_pYandexDictionary->translate(QtYaWord(rusHome, "en", "ru")); END_ASYNC_FUNCTION(signalSpy, connection) }
В начале создаем слово для перевода с английского на русский — переменная rusHome.
Затем создаем signalSpy — это объект будет ждать сигнала от m_pYandexDictionary.
Далее создается обработчик сигнала translated в виде лямбды выражения. В ней проверяется правильность перевода слова.
После этого отправляется запрос на перевод и вызывается макрос END_ASYNC_FUNCTION.
Код этого макроса:
#define END_ASYNC_FUNCTION(signalSpy, connection) \ QVERIFY(signalSpy.wait()); \ QObject::disconnect(connection);
Сначала ждем прихода сигнала. По умолчанию время ожидания сигнала 5 секунд. Если он придет, то начнет выполняться код обработчика, который мы задали в лямбда выражении ранее. Если нет, то тест зафейлится на этой строке.
После отключаем коннект с лямбда выражением, т.к. иначе он сохраниться и после выхода из функции.
Выводы
В статье я привел простой способ создания асинхронных функций и раскрыл способ их тестирования.
ссылка на оригинал статьи http://habrahabr.ru/post/233099/
Добавить комментарий