Allow edit user substitutions

This commit is contained in:
Gres 2020-03-08 13:49:15 +03:00
parent a197edd62b
commit 8a65cddc6f
8 changed files with 237 additions and 32 deletions

View File

@ -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 \

View File

@ -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();

View File

@ -36,7 +36,9 @@ public:
int autoUpdateType{0}; // Never
QString lastUpdateCheck{""};
Substitutions userSubstitutions;
bool useUserSubstitutions{true};
bool debugMode{false};

View File

@ -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,

View File

@ -24,7 +24,6 @@ private:
void updateCurrentPage();
void openTessdataDialog();
void updateTesseractLanguages();
void updateCorrectionsTable();
void updateTranslators(const QString &path, const QStringList &enabled);
void updateTranslationLanguages();

View File

@ -255,37 +255,28 @@
</widget>
<widget class="QWidget" name="pageCorrect">
<layout class="QGridLayout" name="gridLayout_10">
<item row="0" column="0">
<widget class="QLabel" name="label_18">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Заполняется на основании содержания tessdata&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Language:</string>
</property>
<property name="buddy">
<cstring>tesseractLangCombo</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="correctLangCombo"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label_11">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Символы, регулярно распознаваемые с ошибками. При обнаружении будут заменены на указанные.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Corrections</string>
<string>Substitutions</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="useUserSubstitutions">
<property name="text">
<string>Substitute recognized text</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QTableWidget" name="recognizerFixTable">
<widget class="SubstitutionsTable" name="userSubstitutionsTable">
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
@ -510,6 +501,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SubstitutionsTable</class>
<extends>QTableWidget</extends>
<header>substitutionstable.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>captureEdit</tabstop>
<tabstop>repeatCaptureEdit</tabstop>

142
src/substitutionstable.cpp Normal file
View File

@ -0,0 +1,142 @@
#include "substitutionstable.h"
#include "debug.h"
#include "languagecodes.h"
#include <QComboBox>
#include <QPainter>
#include <QStringListModel>
#include <QStyledItemDelegate>
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<QComboBox *>(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<LanguageId, Substitution> SubstitutionsTable::at(int row) const
{
SOFT_ASSERT(row >= 0 && row < rowCount(), return {});
using E = Column;
auto combo = static_cast<QComboBox *>(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;
}
}

27
src/substitutionstable.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include "settings.h"
#include <QTableWidget>
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<LanguageId, Substitution> at(int row) const;
QStringListModel* languagesModel_;
};