From 8a65cddc6fc16d20d88528761ffc9d71e27856f6 Mon Sep 17 00:00:00 2001 From: Gres Date: Sun, 8 Mar 2020 13:49:15 +0300 Subject: [PATCH] Allow edit user substitutions --- screen-translator.pro | 2 + src/settings.cpp | 41 ++++++++++- src/settings.h | 2 + src/settingseditor.cpp | 20 +++--- src/settingseditor.h | 1 - src/settingseditor.ui | 34 +++++---- src/substitutionstable.cpp | 142 +++++++++++++++++++++++++++++++++++++ src/substitutionstable.h | 27 +++++++ 8 files changed, 237 insertions(+), 32 deletions(-) create mode 100644 src/substitutionstable.cpp create mode 100644 src/substitutionstable.h diff --git a/screen-translator.pro b/screen-translator.pro index 96485b7..65d7dd8 100644 --- a/screen-translator.pro +++ b/screen-translator.pro @@ -47,6 +47,7 @@ HEADERS += \ src/settings.h \ src/settingseditor.h \ src/stfwd.h \ + src/substitutionstable.h \ src/task.h \ src/translate/translator.h \ src/translate/webpage.h \ @@ -72,6 +73,7 @@ SOURCES += \ src/service/widgetstate.cpp \ src/settings.cpp \ src/settingseditor.cpp \ + src/substitutionstable.cpp \ src/translate/translator.cpp \ src/translate/webpage.cpp \ src/translate/webpageproxy.cpp \ diff --git a/src/settings.cpp b/src/settings.cpp index 0d3a936..0e9a01c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -22,7 +22,10 @@ const QString qs_lastUpdateCheck = "lastUpdateCheck"; const QString qs_recogntionGroup = "Recognition"; const QString qs_tessDataPlace = "tessdata_dir"; const QString qs_ocrLanguage = "language"; -const QString qs_imageScale = "image_scale"; + +const QString qs_correctionGroup = "Correction"; +const QString qs_userSubstitutions = "userSubstitutions"; +const QString qs_useUserSubstitutions = "useUserSubstitutions"; const QString qs_translationGroup = "Translation"; const QString qs_doTranslation = "doTranslation"; @@ -34,7 +37,7 @@ const QString qs_translationTimeout = "translation_timeout"; const QString qs_debugMode = "translation_debug"; const QString qs_translators = "translators"; -QString shuffle(const QString &source) +QString shuffle(const QString& source) { if (source.isEmpty()) { return source; @@ -47,6 +50,28 @@ QString shuffle(const QString &source) return QString::fromUtf8(result.data()); } +QStringList packSubstitutions(const Substitutions& source) +{ + QStringList result; + for (const auto& i : source) { + result << i.first << i.second.source << i.second.target; + } + return result; +} + +Substitutions unpackSubstitutions(const QStringList& raw) +{ + const auto count = raw.size(); + if (count < 3) + return {}; + + Substitutions result; + for (auto i = 0, end = raw.size(); i < end; i += 3) { + result.emplace(raw[i], Substitution{raw[i + 1], raw[i + 2]}); + } + return result; +} + } // namespace void Settings::save() @@ -82,8 +107,11 @@ void Settings::save() settings.setValue(qs_tessDataPlace, tessdataPath); settings.setValue(qs_ocrLanguage, sourceLanguage); - // TODO corrector + settings.endGroup(); + settings.beginGroup(qs_correctionGroup); + settings.setValue(qs_useUserSubstitutions, useUserSubstitutions); + settings.setValue(qs_userSubstitutions, packSubstitutions(userSubstitutions)); settings.endGroup(); settings.beginGroup(qs_translationGroup); @@ -136,6 +164,13 @@ void Settings::load() settings.endGroup(); + settings.beginGroup(qs_correctionGroup); + useUserSubstitutions = + settings.value(qs_useUserSubstitutions, useUserSubstitutions).toBool(); + userSubstitutions = + unpackSubstitutions(settings.value(qs_userSubstitutions).toStringList()); + settings.endGroup(); + settings.beginGroup(qs_translationGroup); doTranslation = settings.value(qs_doTranslation, doTranslation).toBool(); diff --git a/src/settings.h b/src/settings.h index 613a0aa..1d8a6d6 100644 --- a/src/settings.h +++ b/src/settings.h @@ -36,7 +36,9 @@ public: int autoUpdateType{0}; // Never QString lastUpdateCheck{""}; + Substitutions userSubstitutions; + bool useUserSubstitutions{true}; bool debugMode{false}; diff --git a/src/settingseditor.cpp b/src/settingseditor.cpp index 5639dd4..389bc69 100644 --- a/src/settingseditor.cpp +++ b/src/settingseditor.cpp @@ -45,13 +45,13 @@ SettingsEditor::SettingsEditor() connect(ui->tessdataEdit, &QLineEdit::textChanged, // this, &SettingsEditor::updateTesseractLanguages); - // connect(ui->recognizerFixTable, SIGNAL(itemChanged(QTableWidgetItem *)), - // SLOT(recognizerFixTableItemChanged(QTableWidgetItem *))); + // correction - // // ui->translateLangCombo->addItems(dictionary_.translateLanguagesUi()); + ui->userSubstitutionsTable->setEnabled(ui->useUserSubstitutions->isChecked()); + connect(ui->useUserSubstitutions, &QCheckBox::toggled, // + ui->userSubstitutionsTable, &QTableWidget::setEnabled); // translation - updateTranslationLanguages(); // updates @@ -87,6 +87,9 @@ Settings SettingsEditor::settings() const if (auto lang = langs.findByName(ui->tesseractLangCombo->currentText())) settings.sourceLanguage = lang->id; + settings.useUserSubstitutions = ui->useUserSubstitutions->isChecked(); + settings.userSubstitutions = ui->userSubstitutionsTable->substitutions(); + settings.doTranslation = ui->doTranslationCheck->isChecked(); settings.ignoreSslErrors = ui->ignoreSslCheck->isChecked(); settings.debugMode = ui->translatorDebugCheck->isChecked(); @@ -126,6 +129,9 @@ void SettingsEditor::setSettings(const Settings &settings) if (auto lang = langs.findById(settings.sourceLanguage)) ui->tesseractLangCombo->setCurrentText(lang->name); + ui->useUserSubstitutions->setChecked(settings.useUserSubstitutions); + ui->userSubstitutionsTable->setSubstitutions(settings.userSubstitutions); + ui->doTranslationCheck->setChecked(settings.doTranslation); ui->ignoreSslCheck->setChecked(settings.ignoreSslErrors); ui->translatorDebugCheck->setChecked(settings.debugMode); @@ -157,7 +163,6 @@ void SettingsEditor::openTessdataDialog() void SettingsEditor::updateTesseractLanguages() { ui->tesseractLangCombo->clear(); - ui->correctLangCombo->clear(); QDir dir(ui->tessdataEdit->text()); if (!dir.exists()) @@ -178,11 +183,6 @@ void SettingsEditor::updateTesseractLanguages() std::sort(names.begin(), names.end()); ui->tesseractLangCombo->addItems(names); - ui->correctLangCombo->addItems(names); -} - -void SettingsEditor::updateCorrectionsTable() -{ } void SettingsEditor::updateTranslators(const QString &path, diff --git a/src/settingseditor.h b/src/settingseditor.h index 1dba6c1..1a95568 100644 --- a/src/settingseditor.h +++ b/src/settingseditor.h @@ -24,7 +24,6 @@ private: void updateCurrentPage(); void openTessdataDialog(); void updateTesseractLanguages(); - void updateCorrectionsTable(); void updateTranslators(const QString &path, const QStringList &enabled); void updateTranslationLanguages(); diff --git a/src/settingseditor.ui b/src/settingseditor.ui index 229224e..d2f82fe 100644 --- a/src/settingseditor.ui +++ b/src/settingseditor.ui @@ -255,37 +255,28 @@ - - - - <html><head/><body><p>Заполняется на основании содержания tessdata</p></body></html> - - - Language: - - - tesseractLangCombo - - - - - - <html><head/><body><p>Символы, регулярно распознаваемые с ошибками. При обнаружении будут заменены на указанные.</p></body></html> - Corrections + Substitutions Qt::AlignCenter + + + + Substitute recognized text + + + - + QAbstractItemView::SelectRows @@ -510,6 +501,13 @@ + + + SubstitutionsTable + QTableWidget +
substitutionstable.h
+
+
captureEdit repeatCaptureEdit diff --git a/src/substitutionstable.cpp b/src/substitutionstable.cpp new file mode 100644 index 0000000..ac70f2a --- /dev/null +++ b/src/substitutionstable.cpp @@ -0,0 +1,142 @@ +#include "substitutionstable.h" +#include "debug.h" +#include "languagecodes.h" + +#include +#include +#include +#include + +namespace +{ +QStringList allSourceLanguages() +{ + LanguageCodes langs; + const auto &allLangs = langs.all(); + + QStringList result; + result.reserve(allLangs.size()); + for (const auto &i : allLangs) { + if (i.second.tesseract.isEmpty()) + continue; + result.append(i.second.name); + } + return result; +} + +class SubstitutionDelegate : public QStyledItemDelegate +{ +public: + explicit SubstitutionDelegate(QObject *parent = nullptr) + : QStyledItemDelegate(parent) + { + } + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override + { + painter->drawText(option.rect, Qt::AlignCenter, index.data().toString()); + } +}; + +} // namespace + +SubstitutionsTable::SubstitutionsTable(QWidget *parent) + : QTableWidget(parent) + , languagesModel_(new QStringListModel(allSourceLanguages(), this)) +{ + setItemDelegate(new SubstitutionDelegate(this)); + setColumnCount(int(Column::Count)); + setHorizontalHeaderLabels({tr("Language"), tr("Source"), tr("Changed")}); + connect(this, &SubstitutionsTable::itemChanged, // + this, &SubstitutionsTable::handleItemChange); +} + +void SubstitutionsTable::setSubstitutions(const Substitutions &substitutions) +{ + for (const auto &i : substitutions) addRow(i.first, i.second); + addRow(); // for editing + resizeColumnsToContents(); +} + +Substitutions SubstitutionsTable::substitutions() const +{ + Substitutions result; + for (auto row = 0, end = rowCount(); row < end; ++row) { + const auto pair = at(row); + SOFT_ASSERT(!pair.first.isEmpty(), continue); + if (pair.second.source.isEmpty()) + continue; + result.emplace(pair.first, pair.second); + } + return result; +} + +void SubstitutionsTable::addRow(const LanguageId &language, + const Substitution &substutution) +{ + QSignalBlocker blocker(this); + auto row = rowCount(); + insertRow(row); + + auto combo = new QComboBox(this); + combo->setModel(languagesModel_); + + using E = Column; + if (!language.isEmpty()) { + LanguageCodes langs; + if (auto lang = langs.findById(language)) + combo->setCurrentText(lang->name); + } else if (rowCount() > 1) { + const auto previousRow = rowCount() - 2; + auto previousCombo = + static_cast(cellWidget(previousRow, int(E::Language))); + SOFT_ASSERT(previousCombo, return ); + combo->setCurrentText(previousCombo->currentText()); + } + + setCellWidget(row, int(E::Language), combo); + setItem(row, int(E::Source), new QTableWidgetItem(substutution.source)); + setItem(row, int(E::Target), new QTableWidgetItem(substutution.target)); +} + +std::pair SubstitutionsTable::at(int row) const +{ + SOFT_ASSERT(row >= 0 && row < rowCount(), return {}); + + using E = Column; + auto combo = static_cast(cellWidget(row, int(E::Language))); + SOFT_ASSERT(combo, return {}); + + LanguageCodes langs; + auto lang = langs.findByName(combo->currentText()); + SOFT_ASSERT(lang, return {}); + + Substitution sub; + auto sourceItem = item(row, int(E::Source)); + SOFT_ASSERT(sourceItem, return {}); + sub.source = sourceItem->text(); + + auto targetItem = item(row, int(E::Target)); + SOFT_ASSERT(targetItem, return {}); + sub.target = targetItem->text(); + + return std::make_pair(lang->id, sub); +} + +void SubstitutionsTable::handleItemChange(QTableWidgetItem *item) +{ + if (item->column() != int(Column::Source)) + return; + + const auto lastRow = rowCount() - 1; + if (!item->text().isEmpty() && item->row() == lastRow) { + addRow(); + return; + } + + if (item->text().isEmpty() && item->row() != lastRow) { + removeRow(item->row()); + return; + } +} diff --git a/src/substitutionstable.h b/src/substitutionstable.h new file mode 100644 index 0000000..165302c --- /dev/null +++ b/src/substitutionstable.h @@ -0,0 +1,27 @@ +#pragma once + +#include "settings.h" + +#include + +class QStringListModel; + +class SubstitutionsTable : public QTableWidget +{ + Q_OBJECT +public: + enum class Column { Language = 0, Source, Target, Count }; + + explicit SubstitutionsTable(QWidget* parent = nullptr); + + void setSubstitutions(const Substitutions& substitutions); + Substitutions substitutions() const; + +private: + void handleItemChange(QTableWidgetItem* item); + void addRow(const LanguageId& language = {}, + const Substitution& substutution = {}); + std::pair at(int row) const; + + QStringListModel* languagesModel_; +};