Allow edit user substitutions
This commit is contained in:
parent
a197edd62b
commit
8a65cddc6f
@ -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 \
|
||||
|
@ -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();
|
||||
|
@ -36,7 +36,9 @@ public:
|
||||
|
||||
int autoUpdateType{0}; // Never
|
||||
QString lastUpdateCheck{""};
|
||||
|
||||
Substitutions userSubstitutions;
|
||||
bool useUserSubstitutions{true};
|
||||
|
||||
bool debugMode{false};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -24,7 +24,6 @@ private:
|
||||
void updateCurrentPage();
|
||||
void openTessdataDialog();
|
||||
void updateTesseractLanguages();
|
||||
void updateCorrectionsTable();
|
||||
void updateTranslators(const QString &path, const QStringList &enabled);
|
||||
void updateTranslationLanguages();
|
||||
|
||||
|
@ -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><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">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Символы, регулярно распознаваемые с ошибками. При обнаружении будут заменены на указанные.</p></body></html></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
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