diff --git a/ScreenTranslator.pro b/ScreenTranslator.pro
index 8af33c0..4d9d2ef 100644
--- a/ScreenTranslator.pro
+++ b/ScreenTranslator.pro
@@ -37,6 +37,7 @@ SOURCES += main.cpp\
ProcessingItem.cpp \
ImageProcessing.cpp \
LanguageHelper.cpp \
+ WebTranslator.cpp \
GoogleWebTranslator.cpp
HEADERS += \
@@ -51,6 +52,7 @@ HEADERS += \
ResultDialog.h \
ImageProcessing.h \
LanguageHelper.h \
+ WebTranslator.h \
GoogleWebTranslator.h \
StAssert.h
@@ -71,4 +73,5 @@ OTHER_FILES += \
images/icon.ico \
README.md \
uncrustify.cfg\
+ translators/google.js \
TODO.md
diff --git a/Settings.h b/Settings.h
index b7cf46f..da45ae3 100644
--- a/Settings.h
+++ b/Settings.h
@@ -24,6 +24,7 @@ namespace settings_names {
const QString doTranslation = "doTranslation";
const QString sourceLanguage = "source_language";
const QString translationLanguage = "translation_language";
+ const QString translationTimeout = "translation_timeout";
}
@@ -47,6 +48,7 @@ namespace settings_values {
const bool doTranslation = true;
const QString sourceLanguage = "auto";
const QString translationLanguage = "ru";
+ const int translationTimeout = 15; // secs
}
#endif // SETTINGS_H
diff --git a/SettingsEditor.cpp b/SettingsEditor.cpp
index 5c3c89c..0fc694f 100644
--- a/SettingsEditor.cpp
+++ b/SettingsEditor.cpp
@@ -64,6 +64,7 @@ void SettingsEditor::saveSettings () const {
settings.setValue (translationLanguage, trLanguage);
QString sourceLanguage = dictionary_.translateForOcrCode (ocrLanguage);
settings.setValue (sourceLanguage, sourceLanguage);
+ settings.setValue (translationTimeout, ui->translateTimeoutSpin->value ());
settings.endGroup ();
}
@@ -104,6 +105,7 @@ void SettingsEditor::loadSettings () {
ui->doTranslationCombo->setChecked (GET (doTranslation).toBool ());
QString trLanguage = dictionary_.translateCodeToUi (GET (translationLanguage).toString ());
ui->translateLangCombo->setCurrentText (trLanguage);
+ ui->translateTimeoutSpin->setValue (GET (translationTimeout).toInt ());
settings.endGroup ();
#undef GET
}
diff --git a/SettingsEditor.ui b/SettingsEditor.ui
index 4db4394..c255e6a 100644
--- a/SettingsEditor.ui
+++ b/SettingsEditor.ui
@@ -7,7 +7,7 @@
0
0
603
- 269
+ 296
@@ -194,7 +194,7 @@
Перевод
- -
+
-
<html><head/><body><p>Необходимо ли переводить (вкл) распознанный текст.</p></body></html>
@@ -204,20 +204,37 @@
- -
+
-
+
+
+ <html><head/><body><p>Максимальное время, которое может быть затрачено на перевод, чтобы он не считался "зависшим".</p></body></html>
+
+
+ Максимальное время перевода:
+
+
+
+ -
+
+
+ сек.
+
+
+
+ -
<html><head/><body><p>Язык, на который осуществляется перевод.</p></body></html>
- Язык результата
+ Язык результата:
translateLangCombo
- -
+
-
diff --git a/WebTranslator.cpp b/WebTranslator.cpp
new file mode 100644
index 0000000..a5b3cf9
--- /dev/null
+++ b/WebTranslator.cpp
@@ -0,0 +1,124 @@
+#include
+#include
+#include
+#include
+#include
+
+#include "WebTranslator.h"
+#include "ProcessingItem.h"
+#include "Settings.h"
+#include "StAssert.h"
+#include "WebTranslatorProxy.h"
+
+WebTranslator::WebTranslator ()
+ : QObject (),
+ proxy_ (new WebTranslatorProxy (this)), view_ (new QWebView), isReady_ (true) {
+
+ view_->settings ()->setAttribute (QWebSettings::AutoLoadImages, false);
+ view_->settings ()->setAttribute (QWebSettings::DeveloperExtrasEnabled, true);
+
+ connect (view_, SIGNAL (loadFinished (bool)), SLOT (loadFinished (bool)));
+ connect (view_->page ()->mainFrame (), SIGNAL (javaScriptWindowObjectCleared ()),
+ this, SLOT (addProxyToView ()));
+ connect (view_->page ()->networkAccessManager (), SIGNAL (finished (QNetworkReply *)),
+ this, SLOT (replyFinished (QNetworkReply *)));
+
+ translationTimeout_.setSingleShot (true);
+ connect (&translationTimeout_, SIGNAL (timeout ()), SLOT (abortTranslation ()));
+
+ connect (proxy_, SIGNAL (error (QString)), SLOT (proxyError (QString)));
+ connect (proxy_, SIGNAL (translated (QString)), SLOT (proxyTranslated (QString)));
+
+ applySettings ();
+}
+
+WebTranslator::~WebTranslator () {
+ delete view_;
+}
+
+void WebTranslator::addProxyToView () {
+ view_->page ()->mainFrame ()->addToJavaScriptWindowObject ("st_wtp", proxy_);
+ view_->page ()->mainFrame ()->evaluateJavaScript (script_);
+}
+
+void WebTranslator::translate (ProcessingItem item) {
+ queue_.push_back (item);
+ translateQueued ();
+}
+
+void WebTranslator::translateQueued () {
+ if (isReady_ && !script_.isEmpty () && !queue_.isEmpty ()) {
+ isReady_ = false;
+ runScriptForItem (queue_.first ());
+ translationTimeout_.start ();
+ }
+}
+
+void WebTranslator::runScriptForItem (const ProcessingItem &item) {
+ ST_ASSERT (!script_.isEmpty ());
+ proxy_->setItem (item);
+ view_->page ()->mainFrame ()->evaluateJavaScript ("translate();");
+}
+
+void WebTranslator::proxyError (const QString &message) {
+ emit error (message);
+ finishTranslation ();
+}
+
+void WebTranslator::proxyTranslated (const QString &text) {
+ if (!queue_.isEmpty () && queue_.first ().recognized == proxy_->sourceText ()) {
+ ProcessingItem &item = queue_.first ();
+ item.translated = text;
+ emit translated (item);
+ }
+ finishTranslation ();
+}
+
+void WebTranslator::abortTranslation () {
+ emit error (tr ("Перевод отменен по таймауту."));
+ finishTranslation ();
+}
+
+void WebTranslator::loadFinished (bool ok) {
+ if (!ok) {
+ QString url = view_->url ().toString ();
+ emit error (tr ("Ошибка загрузки страницы (%1) для перевода.").arg (url));
+ finishTranslation ();
+ }
+}
+
+void WebTranslator::finishTranslation () {
+ translationTimeout_.stop ();
+ if (!queue_.isEmpty ()) {
+ queue_.pop_front ();
+ }
+ isReady_ = true;
+ translateQueued ();
+}
+
+void WebTranslator::replyFinished (QNetworkReply *reply) {
+ emit proxy_->resourceLoaded (reply->url ().toString ());
+}
+
+void WebTranslator::applySettings () {
+ QSettings settings;
+ settings.beginGroup (settings_names::translationGroup);
+#define GET(NAME) settings.value (settings_names::NAME, settings_values::NAME)
+ translationTimeout_.setInterval (GET (translationTimeout).toInt () * 1000);
+#undef GET
+
+ QFile f ("translators/google.js");
+ if (f.open (QFile::ReadOnly)) {
+ script_ = QString::fromUtf8 (f.readAll ());
+ if (script_.isEmpty ()) {
+ emit error (tr ("Пустой сценарий для перевода. Перевод недоступен."));
+ }
+ }
+ else {
+ emit error (tr ("Не считан сценарий для перевода. Перевод недоступен."));
+ }
+}
+
+void WebTranslator::setDebugMode (bool isOn) {
+ view_->setVisible (isOn);
+}
diff --git a/WebTranslator.h b/WebTranslator.h
new file mode 100644
index 0000000..7f79114
--- /dev/null
+++ b/WebTranslator.h
@@ -0,0 +1,53 @@
+#ifndef WEBTRANSLATOR_H
+#define WEBTRANSLATOR_H
+
+#include
+#include
+#include
+
+#include "ProcessingItem.h"
+
+class QWebView;
+class QNetworkReply;
+
+class WebTranslatorProxy;
+
+class WebTranslator : public QObject {
+ Q_OBJECT
+
+ public:
+ explicit WebTranslator ();
+ ~WebTranslator ();
+
+ signals:
+ void translated (ProcessingItem item);
+ void error (QString text);
+
+ public slots:
+ void translate (ProcessingItem item);
+ void applySettings ();
+ void setDebugMode (bool isOn);
+
+ private slots:
+ void loadFinished (bool ok);
+ void replyFinished (QNetworkReply *reply);
+ void addProxyToView ();
+ void abortTranslation ();
+ void proxyError (const QString &message);
+ void proxyTranslated (const QString &text);
+
+ private:
+ void translateQueued ();
+ void runScriptForItem (const ProcessingItem &item);
+ void finishTranslation ();
+
+ private:
+ WebTranslatorProxy *proxy_;
+ QWebView *view_;
+ QVector queue_;
+ bool isReady_;
+ QString script_;
+ QTimer translationTimeout_;
+};
+
+#endif // WEBTRANSLATOR_H
diff --git a/WebTranslatorProxy.cpp b/WebTranslatorProxy.cpp
new file mode 100644
index 0000000..b298baa
--- /dev/null
+++ b/WebTranslatorProxy.cpp
@@ -0,0 +1,25 @@
+#include "WebTranslatorProxy.h"
+#include "ProcessingItem.h"
+
+WebTranslatorProxy::WebTranslatorProxy (QObject *parent)
+ : QObject (parent) {
+}
+
+void WebTranslatorProxy::setItem (const ProcessingItem &item) {
+ sourceText_ = item.recognized;
+ sourceLanguage_ = item.ocrLanguage;
+ resultLanguage_ = item.translateLanguage;
+}
+
+const QString &WebTranslatorProxy::sourceText () const {
+ return sourceText_;
+}
+
+const QString &WebTranslatorProxy::sourceLanguage () const {
+ return sourceLanguage_;
+}
+
+const QString &WebTranslatorProxy::resultLanguage () const {
+ return resultLanguage_;
+}
+
diff --git a/WebTranslatorProxy.h b/WebTranslatorProxy.h
new file mode 100644
index 0000000..1ff4220
--- /dev/null
+++ b/WebTranslatorProxy.h
@@ -0,0 +1,38 @@
+#ifndef WEBTRANSLATORPROXY_H
+#define WEBTRANSLATORPROXY_H
+
+#include
+
+class ProcessingItem;
+
+/*!
+ * \brief Proxy class between WebTranslator and QWebView.
+ */
+class WebTranslatorProxy : public QObject {
+ Q_OBJECT
+ Q_PROPERTY (QString sourceText READ sourceText)
+ Q_PROPERTY (QString sourceLanguage READ sourceLanguage)
+ Q_PROPERTY (QString resultLanguage READ resultLanguage)
+
+ public:
+ explicit WebTranslatorProxy (QObject *parent = 0);
+
+ void setItem (const ProcessingItem &item);
+
+ const QString &sourceText () const;
+ const QString &sourceLanguage () const;
+ const QString &resultLanguage () const;
+
+ signals:
+ void translated (const QString &text);
+ void error (const QString &message);
+
+ void resourceLoaded (const QString &url);
+
+ private:
+ QString sourceText_;
+ QString sourceLanguage_;
+ QString resultLanguage_;
+};
+
+#endif // WEBTRANSLATORPROXY_H
diff --git a/translators/google.js b/translators/google.js
new file mode 100644
index 0000000..11f0499
--- /dev/null
+++ b/translators/google.js
@@ -0,0 +1,31 @@
+var isPageLoaded = false;
+var isTranslationFinished = false; // async translation request
+
+function checkFinished () {
+ if (!isPageLoaded || !isTranslationFinished) return;
+ setTimeout(function () {
+ var spans = [].slice.call (document.querySelectorAll ('#result_box > span'));
+ var text = spans.reduce (function (res, i) {
+ return res + ' ' + i.innerText;
+ }, '');
+ st_wtp.translated (text);
+ }, 500); // wait for gui fill
+}
+function onResourceLoad (url) {
+ if (url.indexOf ('/translate_a/single') > -1) {
+ isTranslationFinished = true;
+ checkFinished ();
+ }
+}
+st_wtp.resourceLoaded.connect (onResourceLoad);
+function onPageLoad () {
+ isPageLoaded = true;
+ checkFinished ();
+}
+window.onload = onPageLoad();
+
+function translate (){
+ var url = 'https://translate.google.com/#auto/' +
+ st_wtp.resultLanguage + '/' + st_wtp.sourceText;
+ window.location = url;
+}