From 9ccc5a67eb1e1d8bcca9dab65aba9525a2a07fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 7 Jul 2013 17:26:13 +0200 Subject: [PATCH] Translator updated. Removed webkit dependency. --- YACReader/YACReader.pro | 2 +- YACReader/files.qrc | 1 - YACReader/images.qrc | 4 + YACReader/translator.cpp | 373 +++++++++++++++++++++++++++++++++--- YACReader/translator.h | 63 +++++- YACReader/viewer.cpp | 1 - images/close.png | Bin 8350 -> 215 bytes images/dropDownArrow.png | Bin 0 -> 135 bytes images/fromTo.png | Bin 0 -> 171 bytes images/speaker.png | Bin 0 -> 200 bytes images/translatorSearch.png | Bin 0 -> 241 bytes 11 files changed, 411 insertions(+), 33 deletions(-) create mode 100644 images/dropDownArrow.png create mode 100644 images/fromTo.png create mode 100644 images/speaker.png create mode 100644 images/translatorSearch.png diff --git a/YACReader/YACReader.pro b/YACReader/YACReader.pro index 1227a908..3ff5f1a8 100644 --- a/YACReader/YACReader.pro +++ b/YACReader/YACReader.pro @@ -29,7 +29,7 @@ INCLUDEPATH += /usr/local/include/poppler/qt4 LIBS += -L/usr/local/lib -lpoppler-qt4 } -QT += network webkit phonon opengl +QT += network phonon opengl CONFIG += release CONFIG -= flat diff --git a/YACReader/files.qrc b/YACReader/files.qrc index 6bce09ab..453372fa 100644 --- a/YACReader/files.qrc +++ b/YACReader/files.qrc @@ -3,7 +3,6 @@ ../files/about.html ../files/helpYACReader.html ../files/shortcuts.html - ../files/translator.html diff --git a/YACReader/images.qrc b/YACReader/images.qrc index 535f84f2..c0c13672 100644 --- a/YACReader/images.qrc +++ b/YACReader/images.qrc @@ -69,5 +69,9 @@ ../images/useNewFlowButton.png ../images/useOldFlowButton.png ../images/notificationsLabel.png + ../images/fromTo.png + ../images/dropDownArrow.png + ../images/translatorSearch.png + ../images/speaker.png diff --git a/YACReader/translator.cpp b/YACReader/translator.cpp index ced688a2..85029c67 100644 --- a/YACReader/translator.cpp +++ b/YACReader/translator.cpp @@ -6,63 +6,285 @@ #include #include #include +#include "translator.h" + +#include "yacreader_busy_widget.h" + #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include -#include "translator.h" +#include + +#define APPID "417CEAD93449502CC3C9B69FED26C54118E62BCC" YACReaderTranslator::YACReaderTranslator(QWidget * parent) -:QWidget(parent) +:QWidget(parent),drag(false) { + QString scrollBarStyle = "QScrollBar:vertical { border: none; background: #404040; width: 7px; margin: 0 3px 0 0; }" + "QScrollBar::handle:vertical { background: #DDDDDD; width: 7px; min-height: 20px; }" + "QScrollBar::add-line:vertical { border: none; background: #404040; height: 10px; subcontrol-position: bottom; subcontrol-origin: margin; margin: 0 3px 0 0;}" + + "QScrollBar::sub-line:vertical { border: none; background: #404040; height: 10px; subcontrol-position: top; subcontrol-origin: margin; margin: 0 3px 0 0;}" + "QScrollBar::up-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-up.png') center top no-repeat;}" + "QScrollBar::down-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-down.png') center top no-repeat;}" + + "QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {background: none; }"; + this->setCursor(QCursor(Qt::ArrowCursor)); this->setAutoFillBackground(true); this->setBackgroundRole(QPalette::Window); QPalette p(this->palette()); - p.setColor(QPalette::Window, QColor(96,96,96)); + p.setColor(QPalette::Window, QColor("#404040")); this->setPalette(p); QVBoxLayout *layout = new QVBoxLayout(this); - QWebView * view = new QWebView(); - QFile f(":/files/translator.html"); - f.open(QIODevice::ReadOnly); - QTextStream txtS(&f); - txtS.setCodec(QTextCodec::codecForName("UTF-8")); - QString contentHTML = txtS.readAll(); - view->setHtml(contentHTML); - view->page()->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks); - connect(view->page(),SIGNAL(linkClicked(QUrl)),this,SLOT(play(QUrl))); - QHBoxLayout * buttonBar = new QHBoxLayout(); + //TITLE BAR + QHBoxLayout * titleBar = new QHBoxLayout(); QPushButton * close = new QPushButton(QIcon(QPixmap(":/images/close.png")),""); close->setFlat(true); - buttonBar->addStretch(); - close->resize(18,18); - buttonBar->addWidget(close); - buttonBar->setMargin(0); + QLabel * title = new QLabel(tr("YACReader translator")); + title->setStyleSheet("QLabel {font-size:18px; font-family:Arial; color:white;}"); + titleBar->addWidget(title); + titleBar->addStretch(); + close->resize(14,14); + close->setStyleSheet("QPushButton {margin:0;padding:0;border:none;}"); + titleBar->addWidget(close); + titleBar->setContentsMargins(0,0,0,0); + titleBar->setSpacing(0); connect(close,SIGNAL(clicked()),this->parent(),SLOT(animateHideTranslator())); - layout->setMargin(0); - layout->setSpacing(0); + layout->addLayout(titleBar); - layout->addLayout(buttonBar); - layout->addWidget(view); + //INPUT TEXT + text = new QTextEdit(this); + text->setMinimumHeight(110); + text->setMaximumHeight(110); + layout->addSpacing(12); + layout->addWidget(text); + text->setStyleSheet("QTextEdit{border:none;background:#2a2a2a;color:white; font-size:12px; padding:6px;}"+scrollBarStyle); - resize(view->size().width()/1.60,view->size().height()); + //COMBOBOXES + QHBoxLayout * combos = new QHBoxLayout(); + from = new QComboBox(this); + to = new QComboBox(this); + QString comboBoxStyle = "QComboBox {border:none;background:#2a2a2a;color:white;font-size:12px;font-family:Arial;padding-left:8px;}" + "QComboBox::down-arrow {image: url(:/images/dropDownArrow.png);}" + "QComboBox::drop-down {border:none; padding-right:10px;}" + "QComboBox QAbstractItemView {border: none; background:#272727; color:white; selection-background-color: #202020; outline:none;}" + "QComboBox QAbstractItemView::item {padding-left:8px;}" + scrollBarStyle + ; + from->setStyleSheet(comboBoxStyle); + to->setStyleSheet(comboBoxStyle); + from->setFixedHeight(22); + to->setFixedHeight(22); + QLabel * arrow = new QLabel(this); + QPixmap arrowPixmap(":/images/fromTo.png"); + arrow->setPixmap(arrowPixmap); + QPushButton * searchButton = new QPushButton(this); + searchButton->setIcon(QIcon(":/images/translatorSearch.png")); + searchButton->setStyleSheet("QPushButton {border:none; background:#2a2a2a;}"); + searchButton->setFixedSize(22,22); + combos->addWidget(from,1); + combos->addSpacing(9); + combos->addWidget(arrow,0); + combos->addSpacing(9); + combos->addWidget(to,1); + combos->addSpacing(9); + combos->addWidget(searchButton,0); + layout->addSpacing(12); + layout->addLayout(combos); + + + //RESULTS + QHBoxLayout * resultsTitleLayout = new QHBoxLayout(); + resultsTitle = new QLabel(tr("Translation")); + resultsTitle->setStyleSheet("QLabel {font-family:Arial;font-size:14px;color:#e3e3e3;}"); + speakButton = new QPushButton(this); + speakButton->setStyleSheet("QPushButton {border:none;}"); + speakButton->setIcon(QIcon(":/images/speaker.png")); + resultsTitleLayout->addWidget(resultsTitle,0,Qt::AlignVCenter); + resultsTitleLayout->addSpacing(10); + resultsTitleLayout->addWidget(speakButton,0,Qt::AlignVCenter); + resultsTitleLayout->addStretch(); + + layout->addSpacing(15); + layout->addLayout(resultsTitleLayout); + layout->addSpacing(12); + + resultText = new QLabel(); + resultText->setWordWrap(true); + resultText->setStyleSheet("QLabel {color:white;font-size:12px;}"); + resultText->setText("ñlkas lakj dflkaj lasd jflie lkajd fie kljads ijef lasei afsliej ljse f"); + layout->addWidget(resultText); + + layout->addStretch(); + + //CLEAR BUTTON + clearButton = new QPushButton(tr("clear")); + layout->addWidget(clearButton,0,Qt::AlignRight); + clearButton->setMinimumWidth(95); + clearButton->setStyleSheet("QPushButton {border:1px solid #212121; background:#2a2a2a; color:white; font-family:Arial; font-size:12px; padding-top:5px; padding-bottom:5px;}"); + + resize(400,479); music = createPlayer(MusicCategory); + layout->setMargin(0); + layout->setContentsMargins(18,12,18,12); + setContentsMargins(0,0,0,0); + layout->setSpacing(0); + + hideResults(); + populateCombos(); + + busyIndicator = new YACReaderBusyWidget(this); + busyIndicator->move((this->width()-busyIndicator->width())/2,(this->height()-busyIndicator->height())*2/3); + busyIndicator->hide(); + show(); + + connect(searchButton,SIGNAL(pressed()),this,SLOT(translate())); + connect(speakButton,SIGNAL(pressed()),this,SLOT(play())); + connect(clearButton,SIGNAL(pressed()),this,SLOT(clear())); } -void YACReaderTranslator::play(const QUrl & url) +void YACReaderTranslator::hideResults() { - MediaSource src(url); + resultsTitle->setHidden(true); + speakButton->setHidden(true); + resultText->setHidden(true); +} + +void YACReaderTranslator::clear() +{ + hideResults(); + text->clear(); +} + +void YACReaderTranslator::translate() +{ + QString text = this->text->toPlainText(); + if(text.isEmpty()) + return; + QString from = this->from->itemData(this->from->currentIndex()).toString(); + QString to = this->to->itemData(this->to->currentIndex()).toString(); + + TranslationLoader * translationLoader = new TranslationLoader(text,from,to); + connect(translationLoader,SIGNAL(requestFinished(QString)),this,SLOT(setTranslation(QString))); + connect(translationLoader,SIGNAL(error()),this,SLOT(error())); + connect(translationLoader,SIGNAL(timeOut()),this,SLOT(error())); + connect(translationLoader,SIGNAL(finished()),translationLoader,SLOT(deleteLater())); + + TextToSpeachLoader * tts = new TextToSpeachLoader(text,from); + connect(tts,SIGNAL(requestFinished(QUrl)),this,SLOT(setSpeak(QUrl))); + connect(tts,SIGNAL(error()),this,SLOT(error())); + connect(tts,SIGNAL(timeOut()),this,SLOT(error())); + connect(tts,SIGNAL(finished()),tts,SLOT(deleteLater())); + + translationLoader->start(); + tts->start(); + + resultsTitle->setText(tr("Translation")); + + hideResults(); + + busyIndicator->show(); +} + +void YACReaderTranslator::error() +{ + resultsTitle->setText(tr("Service not available")); + resultsTitle->setHidden(false); + busyIndicator->hide(); +} + +void YACReaderTranslator::setSpeak(const QUrl & url) +{ + resultsTitle->setHidden(false); + speakButton->setHidden(false); + + ttsSource = url; +} + +void YACReaderTranslator::setTranslation(const QString & string) +{ + resultText->setText(string); + + resultsTitle->setHidden(false); + resultText->setHidden(false); + busyIndicator->hide(); +} + +void YACReaderTranslator::populateCombos() +{ + QList combos; + combos.append(from); + combos.append(to); + + for(int i=0;iaddItem("Arabic","ar"); + combo->addItem("Bulgarian","bg"); + combo->addItem("Catalan","ca"); + combo->addItem("Chinese Simplified","zh-CHS"); + combo->addItem("Chinese Traditional","zh-CHT"); + combo->addItem("Czech","cs"); + combo->addItem("Danish","da"); + combo->addItem("Dutch","nl"); + combo->addItem("English","en"); + combo->addItem("Estonian","et"); + combo->addItem("Finnish","fi"); + combo->addItem("French","fr"); + combo->addItem("German","de"); + combo->addItem("Greek","el"); + combo->addItem("Haitian Creole","ht"); + combo->addItem("Hebrew","he"); + combo->addItem("Hindi","hi"); + combo->addItem("Hungarian","hu"); + combo->addItem("Indonesian","id"); + combo->addItem("Italian","it"); + combo->addItem("Japanese","ja"); + combo->addItem("Korean","ko"); + combo->addItem("Latvian","lv"); + combo->addItem("Lithuanian","lt"); + combo->addItem("Norwegian","no"); + combo->addItem("Polish","pl"); + combo->addItem("Portuguese","pt"); + combo->addItem("Romanian","ro"); + combo->addItem("Russian","ru"); + combo->addItem("Slovak","sk"); + combo->addItem("Slovenian","sl"); + combo->addItem("Spanish","es"); + combo->addItem("Swedish","sv"); + combo->addItem("Thai","th"); + combo->addItem("Turkish","tr"); + combo->addItem("Ukrainian","uk"); + combo->addItem("Vietnamese","vi"); + } + from->setCurrentIndex(from->findText("English")); + to->setCurrentIndex(from->findText("Spanish")); +} + +void YACReaderTranslator::play() +{ + //QMessageBox::question(this,"xxx",ttsSource.toString()); + MediaSource src(ttsSource); src.setAutoDelete(true); - music->setCurrentSource(src); - music->play(); + music->setCurrentSource(src); + music->play(); } YACReaderTranslator::~YACReaderTranslator() @@ -72,8 +294,12 @@ YACReaderTranslator::~YACReaderTranslator() void YACReaderTranslator::mousePressEvent(QMouseEvent *event) { - drag = true; - click = event->pos(); + QPoint p = mapTo(this,event->pos()); + if(p.y() < 40) + { + drag = true; + click = event->pos(); + } } void YACReaderTranslator::mouseReleaseEvent(QMouseEvent *event) @@ -88,3 +314,94 @@ void YACReaderTranslator::mouseMoveEvent(QMouseEvent * event) } +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +TranslationLoader::TranslationLoader(QString text, QString from, QString to) + :QThread(),text(text),from(from),to(to) +{ +} + +void TranslationLoader::run() +{ + QNetworkAccessManager manager; + QEventLoop q; + QTimer tT; + + tT.setSingleShot(true); + connect(&tT, SIGNAL(timeout()), &q, SLOT(quit())); + connect(&manager, SIGNAL(finished(QNetworkReply*)),&q, SLOT(quit())); + + QString url = "http://api.microsofttranslator.com/V2/Ajax.svc/Translate?appid=%1&from=%2&to=%3&text=%4&contentType=text/plain"; + url = url.arg(APPID).arg(from).arg(to).arg(text); + + QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(url))); + + tT.start(5000); // 5s timeout + q.exec(); + + if(tT.isActive()){ + // download complete + if(reply->error() == QNetworkReply::NoError) + { + QString utf8 = QString::fromUtf8(reply->readAll()); + utf8 = utf8.remove(0,1); + utf8 = utf8.remove(utf8.count()-1,1); + + QString translated(utf8); + emit(requestFinished(translated)); + } + else + emit(error()); + } else { + emit(timeOut()); + } +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + + +TextToSpeachLoader::TextToSpeachLoader(QString text, QString language) + :QThread(),text(text),language(language) +{ +} + + +void TextToSpeachLoader::run() +{ + QNetworkAccessManager manager; + QEventLoop q; + QTimer tT; + + tT.setSingleShot(true); + connect(&tT, SIGNAL(timeout()), &q, SLOT(quit())); + connect(&manager, SIGNAL(finished(QNetworkReply*)),&q, SLOT(quit())); + + QString url = "http://api.microsofttranslator.com/V2/Ajax.svc/Speak?appid=%1&language=%2&text=%3&contentType=text/plain"; + url = url.arg(APPID).arg(language).arg(text); + + QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(url))); + + tT.start(5000); // 5s timeout + q.exec(); + + if(tT.isActive()){ + // download complete + if(reply->error() == QNetworkReply::NoError) + { + QString utf8 = QString::fromUtf8(reply->readAll()); + utf8 = utf8.remove(0,1); + utf8 = utf8.remove(utf8.count()-1,1); + utf8 = utf8.replace("\\",""); + + emit(requestFinished(QUrl(utf8))); + } + else + emit(error()); + } else { + emit(timeOut()); + } +} diff --git a/YACReader/translator.h b/YACReader/translator.h index 759ce22c..71fb49e9 100644 --- a/YACReader/translator.h +++ b/YACReader/translator.h @@ -4,7 +4,16 @@ class QUrl; class QMouseEvent; class QPoint; -#include +class QTextEdit; +class QComboBox; +class QLabel; +class QPushButton; +class YACReaderBusyWidget; + +#include +#include +#include + #include using namespace Phonon; @@ -17,16 +26,66 @@ class YACReaderTranslator : public QWidget ~YACReaderTranslator(); public slots: - void play(const QUrl & url); + void play(); + + protected slots: + void translate(); + void setSpeak(const QUrl & url); + void setTranslation(const QString & string); + void error(); + void clear(); protected: void mousePressEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); void mouseMoveEvent ( QMouseEvent * event ); + void hideResults(); + + void populateCombos(); bool drag; QPoint click; private: MediaObject * music; + QTextEdit * text; + QComboBox * from; + QComboBox * to; + QLabel * resultsTitle; + QPushButton * speakButton; + QLabel * resultText; + YACReaderBusyWidget * busyIndicator; + QUrl ttsSource; + QPushButton * clearButton; + }; +class TranslationLoader : public QThread +{ + Q_OBJECT +public: + TranslationLoader(QString text, QString from, QString to); +signals: + void requestFinished(QString); + void timeOut(); + void error(); +private: + QString text; + QString from; + QString to; + void run(); +}; + +class TextToSpeachLoader : public QThread +{ + Q_OBJECT +public: + TextToSpeachLoader(QString text, QString language); +signals: + void requestFinished(QUrl); + void timeOut(); + void error(); +private: + QString text; + QString language; + void run(); +}; #endif \ No newline at end of file diff --git a/YACReader/viewer.cpp b/YACReader/viewer.cpp index 58305bd3..81ed5730 100644 --- a/YACReader/viewer.cpp +++ b/YACReader/viewer.cpp @@ -12,7 +12,6 @@ #include "page_label_widget.h" #include "notifications_label_widget.h" -#include #include #define STEPS 22 diff --git a/images/close.png b/images/close.png index 964f206524d137f65339102c3123d92f51696dc2..54d51b35d61af702f1ba296872bb85baddbdf43c 100644 GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1Gm7Xq+Ar-gQ1a)#AP~fqNU9j51$-SJ#eWmacL1o{g5gfJ_ z0>wLRq&hfyTKbx7o~h=fOVqqvRAfCPa_70Jn=-7_%%ei$j(t!x;9YC!adz`7 zrroL&re2YFcvODvu4#{}`eG-|Tr)j*=JJp6-A7*S;JD{s$8Er{b=f|S^H~o>fR14B MboFyt=akR{0C5RS;{X5v literal 8350 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 SziT?sUDSr z1<%~X^wgl##FWaylc_d9MOmIMjv*Ddk`fLu{rmr4pW(>!!jpy%m>fKr754ET`M>dm zCd0>S1-}E!3>mb3%10VcxXEydBh}jBs)fOyzopr04}~nLjV8( literal 0 HcmV?d00001 diff --git a/images/translatorSearch.png b/images/translatorSearch.png new file mode 100644 index 0000000000000000000000000000000000000000..066f21e9bbcf0c0ea8884a8a02f8b06fef84d49d GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp@K+MCz1|)ZGH@^v_BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrH1%P4;wg45_%4l+e)h|Nnn!hL6Aga}>yT2ul<&E?B}G@~~Rr z9s46T1v`Gx+5O&^Yc<3igy-{LebI1w8jDUFr^C}<6OYcEaD)GXsiBck9YYX%!wrV% zYOFbCjG7ZJ>pl1+tRWy$@I#Yfwi@$_W#0o@c>|gg9@tbVJnT}RaENJz;||kD9em7Q mrcE0dnKv@55?sTmaDbs(_O*jTpQ0Afc?_PeelF{r5}E*5WmF*m literal 0 HcmV?d00001