Allow edit user substitutions
This commit is contained in:
parent
a197edd62b
commit
8a65cddc6f
@ -47,6 +47,7 @@ HEADERS += \
|
|||||||
src/settings.h \
|
src/settings.h \
|
||||||
src/settingseditor.h \
|
src/settingseditor.h \
|
||||||
src/stfwd.h \
|
src/stfwd.h \
|
||||||
|
src/substitutionstable.h \
|
||||||
src/task.h \
|
src/task.h \
|
||||||
src/translate/translator.h \
|
src/translate/translator.h \
|
||||||
src/translate/webpage.h \
|
src/translate/webpage.h \
|
||||||
@ -72,6 +73,7 @@ SOURCES += \
|
|||||||
src/service/widgetstate.cpp \
|
src/service/widgetstate.cpp \
|
||||||
src/settings.cpp \
|
src/settings.cpp \
|
||||||
src/settingseditor.cpp \
|
src/settingseditor.cpp \
|
||||||
|
src/substitutionstable.cpp \
|
||||||
src/translate/translator.cpp \
|
src/translate/translator.cpp \
|
||||||
src/translate/webpage.cpp \
|
src/translate/webpage.cpp \
|
||||||
src/translate/webpageproxy.cpp \
|
src/translate/webpageproxy.cpp \
|
||||||
|
@ -22,7 +22,10 @@ const QString qs_lastUpdateCheck = "lastUpdateCheck";
|
|||||||
const QString qs_recogntionGroup = "Recognition";
|
const QString qs_recogntionGroup = "Recognition";
|
||||||
const QString qs_tessDataPlace = "tessdata_dir";
|
const QString qs_tessDataPlace = "tessdata_dir";
|
||||||
const QString qs_ocrLanguage = "language";
|
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_translationGroup = "Translation";
|
||||||
const QString qs_doTranslation = "doTranslation";
|
const QString qs_doTranslation = "doTranslation";
|
||||||
@ -47,6 +50,28 @@ QString shuffle(const QString &source)
|
|||||||
return QString::fromUtf8(result.data());
|
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
|
} // namespace
|
||||||
|
|
||||||
void Settings::save()
|
void Settings::save()
|
||||||
@ -82,8 +107,11 @@ void Settings::save()
|
|||||||
settings.setValue(qs_tessDataPlace, tessdataPath);
|
settings.setValue(qs_tessDataPlace, tessdataPath);
|
||||||
settings.setValue(qs_ocrLanguage, sourceLanguage);
|
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.endGroup();
|
||||||
|
|
||||||
settings.beginGroup(qs_translationGroup);
|
settings.beginGroup(qs_translationGroup);
|
||||||
@ -136,6 +164,13 @@ void Settings::load()
|
|||||||
|
|
||||||
settings.endGroup();
|
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);
|
settings.beginGroup(qs_translationGroup);
|
||||||
|
|
||||||
doTranslation = settings.value(qs_doTranslation, doTranslation).toBool();
|
doTranslation = settings.value(qs_doTranslation, doTranslation).toBool();
|
||||||
|
@ -36,7 +36,9 @@ public:
|
|||||||
|
|
||||||
int autoUpdateType{0}; // Never
|
int autoUpdateType{0}; // Never
|
||||||
QString lastUpdateCheck{""};
|
QString lastUpdateCheck{""};
|
||||||
|
|
||||||
Substitutions userSubstitutions;
|
Substitutions userSubstitutions;
|
||||||
|
bool useUserSubstitutions{true};
|
||||||
|
|
||||||
bool debugMode{false};
|
bool debugMode{false};
|
||||||
|
|
||||||
|
@ -45,13 +45,13 @@ SettingsEditor::SettingsEditor()
|
|||||||
connect(ui->tessdataEdit, &QLineEdit::textChanged, //
|
connect(ui->tessdataEdit, &QLineEdit::textChanged, //
|
||||||
this, &SettingsEditor::updateTesseractLanguages);
|
this, &SettingsEditor::updateTesseractLanguages);
|
||||||
|
|
||||||
// connect(ui->recognizerFixTable, SIGNAL(itemChanged(QTableWidgetItem *)),
|
// correction
|
||||||
// SLOT(recognizerFixTableItemChanged(QTableWidgetItem *)));
|
|
||||||
|
|
||||||
// // ui->translateLangCombo->addItems(dictionary_.translateLanguagesUi());
|
ui->userSubstitutionsTable->setEnabled(ui->useUserSubstitutions->isChecked());
|
||||||
|
connect(ui->useUserSubstitutions, &QCheckBox::toggled, //
|
||||||
|
ui->userSubstitutionsTable, &QTableWidget::setEnabled);
|
||||||
|
|
||||||
// translation
|
// translation
|
||||||
|
|
||||||
updateTranslationLanguages();
|
updateTranslationLanguages();
|
||||||
|
|
||||||
// updates
|
// updates
|
||||||
@ -87,6 +87,9 @@ Settings SettingsEditor::settings() const
|
|||||||
if (auto lang = langs.findByName(ui->tesseractLangCombo->currentText()))
|
if (auto lang = langs.findByName(ui->tesseractLangCombo->currentText()))
|
||||||
settings.sourceLanguage = lang->id;
|
settings.sourceLanguage = lang->id;
|
||||||
|
|
||||||
|
settings.useUserSubstitutions = ui->useUserSubstitutions->isChecked();
|
||||||
|
settings.userSubstitutions = ui->userSubstitutionsTable->substitutions();
|
||||||
|
|
||||||
settings.doTranslation = ui->doTranslationCheck->isChecked();
|
settings.doTranslation = ui->doTranslationCheck->isChecked();
|
||||||
settings.ignoreSslErrors = ui->ignoreSslCheck->isChecked();
|
settings.ignoreSslErrors = ui->ignoreSslCheck->isChecked();
|
||||||
settings.debugMode = ui->translatorDebugCheck->isChecked();
|
settings.debugMode = ui->translatorDebugCheck->isChecked();
|
||||||
@ -126,6 +129,9 @@ void SettingsEditor::setSettings(const Settings &settings)
|
|||||||
if (auto lang = langs.findById(settings.sourceLanguage))
|
if (auto lang = langs.findById(settings.sourceLanguage))
|
||||||
ui->tesseractLangCombo->setCurrentText(lang->name);
|
ui->tesseractLangCombo->setCurrentText(lang->name);
|
||||||
|
|
||||||
|
ui->useUserSubstitutions->setChecked(settings.useUserSubstitutions);
|
||||||
|
ui->userSubstitutionsTable->setSubstitutions(settings.userSubstitutions);
|
||||||
|
|
||||||
ui->doTranslationCheck->setChecked(settings.doTranslation);
|
ui->doTranslationCheck->setChecked(settings.doTranslation);
|
||||||
ui->ignoreSslCheck->setChecked(settings.ignoreSslErrors);
|
ui->ignoreSslCheck->setChecked(settings.ignoreSslErrors);
|
||||||
ui->translatorDebugCheck->setChecked(settings.debugMode);
|
ui->translatorDebugCheck->setChecked(settings.debugMode);
|
||||||
@ -157,7 +163,6 @@ void SettingsEditor::openTessdataDialog()
|
|||||||
void SettingsEditor::updateTesseractLanguages()
|
void SettingsEditor::updateTesseractLanguages()
|
||||||
{
|
{
|
||||||
ui->tesseractLangCombo->clear();
|
ui->tesseractLangCombo->clear();
|
||||||
ui->correctLangCombo->clear();
|
|
||||||
|
|
||||||
QDir dir(ui->tessdataEdit->text());
|
QDir dir(ui->tessdataEdit->text());
|
||||||
if (!dir.exists())
|
if (!dir.exists())
|
||||||
@ -178,11 +183,6 @@ void SettingsEditor::updateTesseractLanguages()
|
|||||||
|
|
||||||
std::sort(names.begin(), names.end());
|
std::sort(names.begin(), names.end());
|
||||||
ui->tesseractLangCombo->addItems(names);
|
ui->tesseractLangCombo->addItems(names);
|
||||||
ui->correctLangCombo->addItems(names);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsEditor::updateCorrectionsTable()
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsEditor::updateTranslators(const QString &path,
|
void SettingsEditor::updateTranslators(const QString &path,
|
||||||
|
@ -24,7 +24,6 @@ private:
|
|||||||
void updateCurrentPage();
|
void updateCurrentPage();
|
||||||
void openTessdataDialog();
|
void openTessdataDialog();
|
||||||
void updateTesseractLanguages();
|
void updateTesseractLanguages();
|
||||||
void updateCorrectionsTable();
|
|
||||||
void updateTranslators(const QString &path, const QStringList &enabled);
|
void updateTranslators(const QString &path, const QStringList &enabled);
|
||||||
void updateTranslationLanguages();
|
void updateTranslationLanguages();
|
||||||
|
|
||||||
|
@ -255,37 +255,28 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="pageCorrect">
|
<widget class="QWidget" name="pageCorrect">
|
||||||
<layout class="QGridLayout" name="gridLayout_10">
|
<layout class="QGridLayout" name="gridLayout_10">
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label_18">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>Заполняется на основании содержания tessdata</p></body></html></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">
|
<item row="1" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="label_11">
|
<widget class="QLabel" name="label_11">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Символы, регулярно распознаваемые с ошибками. При обнаружении будут заменены на указанные.</p></body></html></string>
|
<string><html><head/><body><p>Символы, регулярно распознаваемые с ошибками. При обнаружении будут заменены на указанные.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Corrections</string>
|
<string>Substitutions</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignCenter</set>
|
<set>Qt::AlignCenter</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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">
|
<item row="2" column="0" colspan="2">
|
||||||
<widget class="QTableWidget" name="recognizerFixTable">
|
<widget class="SubstitutionsTable" name="userSubstitutionsTable">
|
||||||
<property name="selectionBehavior">
|
<property name="selectionBehavior">
|
||||||
<enum>QAbstractItemView::SelectRows</enum>
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
</property>
|
</property>
|
||||||
@ -510,6 +501,13 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>SubstitutionsTable</class>
|
||||||
|
<extends>QTableWidget</extends>
|
||||||
|
<header>substitutionstable.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>captureEdit</tabstop>
|
<tabstop>captureEdit</tabstop>
|
||||||
<tabstop>repeatCaptureEdit</tabstop>
|
<tabstop>repeatCaptureEdit</tabstop>
|
||||||
|
142
src/substitutionstable.cpp
Normal file
142
src/substitutionstable.cpp
Normal 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
27
src/substitutionstable.h
Normal 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_;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user