Compare commits
No commits in common. "master" and "1.2.3" have entirely different histories.
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
BasedOnStyle: Google
|
|
||||||
AccessModifierOffset: '-2'
|
|
||||||
AllowShortCaseLabelsOnASingleLine: 'true'
|
|
||||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
|
||||||
AllowShortIfStatementsOnASingleLine: 'false'
|
|
||||||
BreakBeforeBraces: Linux
|
|
||||||
BreakConstructorInitializers: BeforeComma
|
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: 'false'
|
|
||||||
ConstructorInitializerIndentWidth: '2'
|
|
||||||
Standard: Cpp11
|
|
||||||
TabWidth: '2'
|
|
||||||
UseTab: Never
|
|
||||||
IncludeBlocks: Preserve
|
|
||||||
|
|
||||||
...
|
|
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ''
|
|
||||||
labels: 'bug'
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Logs**
|
|
||||||
Application log file recorded during an error.
|
|
||||||
To start recording a log file, open the settings window,
|
|
||||||
enable the 'write trace file' option and apply the settings.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
|
||||||
- OS: [e.g. windows 10, ubuntu 18.04]
|
|
||||||
- Desktop environment (linux only): [e.g. KDE, Gnome]
|
|
||||||
- App version (release archive name): [e.g. 3.0.0-win32, 3.0.0-compatible-win64]
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: 'enhancement'
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
100
.github/workflows/build.yml
vendored
@ -1,100 +0,0 @@
|
|||||||
name: App build
|
|
||||||
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
name: Create release
|
|
||||||
if: contains(github.ref, '/tags/')
|
|
||||||
runs-on: ubuntu-18.04
|
|
||||||
steps:
|
|
||||||
- name: Create release
|
|
||||||
id: create_release
|
|
||||||
uses: actions/create-release@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
tag_name: ${{ github.ref }}
|
|
||||||
release_name: ${{ github.ref }}
|
|
||||||
draft: false
|
|
||||||
prerelease: false
|
|
||||||
|
|
||||||
- name: Store release url
|
|
||||||
run: echo "${{ steps.create_release.outputs.upload_url }}" > ./release_upload_url
|
|
||||||
|
|
||||||
- name: Upload release url
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
path: ./release_upload_url
|
|
||||||
name: release_upload_url
|
|
||||||
|
|
||||||
build:
|
|
||||||
name: Build ${{ matrix.config.name }}${{ matrix.config.tag }}
|
|
||||||
runs-on: ${{ matrix.config.os }}
|
|
||||||
env:
|
|
||||||
OS: ${{ matrix.config.name }}
|
|
||||||
MSVC_VERSION: C:/Program Files/Microsoft Visual Studio/2022/Enterprise
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
config:
|
|
||||||
- { name: "win64", os: windows-latest }
|
|
||||||
- { name: "win32", os: windows-latest }
|
|
||||||
- { name: "linux", os: ubuntu-18.04 }
|
|
||||||
# - { name: "macos", os: macos-latest }
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 10
|
|
||||||
|
|
||||||
- name: Setup python
|
|
||||||
uses: actions/setup-python@v1
|
|
||||||
with:
|
|
||||||
python-version: "3.x"
|
|
||||||
|
|
||||||
- name: Install system libs
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: |
|
|
||||||
sudo apt-get install libgl1-mesa-dev libxkbcommon-x11-0 libxcb-*
|
|
||||||
echo "QMAKE_FLAGS=QMAKE_CXX=g++-10 QMAKE_CC=gcc-10 QMAKE_LINK=g++-10" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Cache dependencies
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: deps
|
|
||||||
key: ${{ env.OS }}-${{ hashFiles('./share/ci/*.py') }}
|
|
||||||
|
|
||||||
- name: Make a release
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
python ./share/ci/release.py
|
|
||||||
echo "artifact=`python ./share/ci/release.py artifact_name`" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Upload build artifact
|
|
||||||
if: env.artifact != ''
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: ${{ env.artifact }}
|
|
||||||
path: ./${{ env.artifact }}
|
|
||||||
|
|
||||||
- name: Download release url
|
|
||||||
if: contains(github.ref, '/tags/')
|
|
||||||
uses: actions/download-artifact@v4.1.7
|
|
||||||
with:
|
|
||||||
name: release_upload_url
|
|
||||||
path: ./
|
|
||||||
|
|
||||||
- name: Set release env
|
|
||||||
if: contains(github.ref, '/tags/')
|
|
||||||
shell: bash
|
|
||||||
run: echo "upload_url=`cat ./release_upload_url`" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Upload release artifacts
|
|
||||||
if: contains(github.ref, '/tags/')
|
|
||||||
uses: actions/upload-release-asset@v1.0.1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
upload_url: ${{ env.upload_url }}
|
|
||||||
asset_path: ./${{ env.artifact }}
|
|
||||||
asset_name: ${{ env.artifact }}
|
|
||||||
asset_content_type: application/zip
|
|
3
.gitignore
vendored
@ -6,6 +6,3 @@
|
|||||||
|
|
||||||
*.exe
|
*.exe
|
||||||
distr/content/
|
distr/content/
|
||||||
|
|
||||||
*.tar.gz
|
|
||||||
__pycache__
|
|
||||||
|
276
GlobalActionHelper.cpp
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
#include "GlobalActionHelper.h"
|
||||||
|
|
||||||
|
#include <qt_windows.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
QHash<QPair<quint32, quint32>, QAction*> GlobalActionHelper::actions_;
|
||||||
|
|
||||||
|
bool GlobalActionHelper::nativeEventFilter(const QByteArray& eventType,
|
||||||
|
void* message, long* result)
|
||||||
|
{
|
||||||
|
Q_UNUSED (eventType);
|
||||||
|
Q_UNUSED (result);
|
||||||
|
MSG* msg = static_cast<MSG*>(message);
|
||||||
|
if (msg->message == WM_HOTKEY)
|
||||||
|
{
|
||||||
|
const quint32 keycode = HIWORD(msg->lParam);
|
||||||
|
const quint32 modifiers = LOWORD(msg->lParam);
|
||||||
|
QAction* action = actions_.value (qMakePair (keycode, modifiers));
|
||||||
|
Q_CHECK_PTR (action);
|
||||||
|
action->activate (QAction::Trigger);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalActionHelper::init()
|
||||||
|
{
|
||||||
|
qApp->installNativeEventFilter (new GlobalActionHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GlobalActionHelper::makeGlobal(QAction* action)
|
||||||
|
{
|
||||||
|
QKeySequence hotKey = action->shortcut ();
|
||||||
|
if (hotKey.isEmpty ())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Qt::KeyboardModifiers allMods = Qt::ShiftModifier | Qt::ControlModifier |
|
||||||
|
Qt::AltModifier | Qt::MetaModifier;
|
||||||
|
Qt::Key key = hotKey.isEmpty() ?
|
||||||
|
Qt::Key(0) :
|
||||||
|
Qt::Key((hotKey[0] ^ allMods) & hotKey[0]);
|
||||||
|
Qt::KeyboardModifiers mods = hotKey.isEmpty() ?
|
||||||
|
Qt::KeyboardModifiers(0) :
|
||||||
|
Qt::KeyboardModifiers(hotKey[0] & allMods);
|
||||||
|
const quint32 nativeKey = nativeKeycode(key);
|
||||||
|
const quint32 nativeMods = nativeModifiers(mods);
|
||||||
|
const bool res = registerHotKey(nativeKey, nativeMods);
|
||||||
|
if (res)
|
||||||
|
actions_.insert (qMakePair(nativeKey, nativeMods), action);
|
||||||
|
else
|
||||||
|
qWarning() << "Failed to register global hotkey:" << hotKey.toString();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GlobalActionHelper::removeGlobal(QAction *action)
|
||||||
|
{
|
||||||
|
QKeySequence hotKey = action->shortcut ();
|
||||||
|
if (hotKey.isEmpty ())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Qt::KeyboardModifiers allMods = Qt::ShiftModifier | Qt::ControlModifier |
|
||||||
|
Qt::AltModifier | Qt::MetaModifier;
|
||||||
|
Qt::Key key = hotKey.isEmpty() ?
|
||||||
|
Qt::Key(0) :
|
||||||
|
Qt::Key((hotKey[0] ^ allMods) & hotKey[0]);
|
||||||
|
Qt::KeyboardModifiers mods = hotKey.isEmpty() ?
|
||||||
|
Qt::KeyboardModifiers(0) :
|
||||||
|
Qt::KeyboardModifiers(hotKey[0] & allMods);
|
||||||
|
const quint32 nativeKey = nativeKeycode(key);
|
||||||
|
const quint32 nativeMods = nativeModifiers(mods);
|
||||||
|
if (!actions_.contains (qMakePair(nativeKey, nativeMods)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const bool res = unregisterHotKey(nativeKey, nativeMods);
|
||||||
|
if (res)
|
||||||
|
actions_.remove (qMakePair(nativeKey, nativeMods));
|
||||||
|
else
|
||||||
|
qWarning() << "Failed to unregister global hotkey:" << hotKey.toString();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 GlobalActionHelper::nativeKeycode(Qt::Key key)
|
||||||
|
{
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case Qt::Key_Escape:
|
||||||
|
return VK_ESCAPE;
|
||||||
|
case Qt::Key_Tab:
|
||||||
|
case Qt::Key_Backtab:
|
||||||
|
return VK_TAB;
|
||||||
|
case Qt::Key_Backspace:
|
||||||
|
return VK_BACK;
|
||||||
|
case Qt::Key_Return:
|
||||||
|
case Qt::Key_Enter:
|
||||||
|
return VK_RETURN;
|
||||||
|
case Qt::Key_Insert:
|
||||||
|
return VK_INSERT;
|
||||||
|
case Qt::Key_Delete:
|
||||||
|
return VK_DELETE;
|
||||||
|
case Qt::Key_Pause:
|
||||||
|
return VK_PAUSE;
|
||||||
|
case Qt::Key_Print:
|
||||||
|
return VK_PRINT;
|
||||||
|
case Qt::Key_Clear:
|
||||||
|
return VK_CLEAR;
|
||||||
|
case Qt::Key_Home:
|
||||||
|
return VK_HOME;
|
||||||
|
case Qt::Key_End:
|
||||||
|
return VK_END;
|
||||||
|
case Qt::Key_Left:
|
||||||
|
return VK_LEFT;
|
||||||
|
case Qt::Key_Up:
|
||||||
|
return VK_UP;
|
||||||
|
case Qt::Key_Right:
|
||||||
|
return VK_RIGHT;
|
||||||
|
case Qt::Key_Down:
|
||||||
|
return VK_DOWN;
|
||||||
|
case Qt::Key_PageUp:
|
||||||
|
return VK_PRIOR;
|
||||||
|
case Qt::Key_PageDown:
|
||||||
|
return VK_NEXT;
|
||||||
|
case Qt::Key_F1:
|
||||||
|
return VK_F1;
|
||||||
|
case Qt::Key_F2:
|
||||||
|
return VK_F2;
|
||||||
|
case Qt::Key_F3:
|
||||||
|
return VK_F3;
|
||||||
|
case Qt::Key_F4:
|
||||||
|
return VK_F4;
|
||||||
|
case Qt::Key_F5:
|
||||||
|
return VK_F5;
|
||||||
|
case Qt::Key_F6:
|
||||||
|
return VK_F6;
|
||||||
|
case Qt::Key_F7:
|
||||||
|
return VK_F7;
|
||||||
|
case Qt::Key_F8:
|
||||||
|
return VK_F8;
|
||||||
|
case Qt::Key_F9:
|
||||||
|
return VK_F9;
|
||||||
|
case Qt::Key_F10:
|
||||||
|
return VK_F10;
|
||||||
|
case Qt::Key_F11:
|
||||||
|
return VK_F11;
|
||||||
|
case Qt::Key_F12:
|
||||||
|
return VK_F12;
|
||||||
|
case Qt::Key_F13:
|
||||||
|
return VK_F13;
|
||||||
|
case Qt::Key_F14:
|
||||||
|
return VK_F14;
|
||||||
|
case Qt::Key_F15:
|
||||||
|
return VK_F15;
|
||||||
|
case Qt::Key_F16:
|
||||||
|
return VK_F16;
|
||||||
|
case Qt::Key_F17:
|
||||||
|
return VK_F17;
|
||||||
|
case Qt::Key_F18:
|
||||||
|
return VK_F18;
|
||||||
|
case Qt::Key_F19:
|
||||||
|
return VK_F19;
|
||||||
|
case Qt::Key_F20:
|
||||||
|
return VK_F20;
|
||||||
|
case Qt::Key_F21:
|
||||||
|
return VK_F21;
|
||||||
|
case Qt::Key_F22:
|
||||||
|
return VK_F22;
|
||||||
|
case Qt::Key_F23:
|
||||||
|
return VK_F23;
|
||||||
|
case Qt::Key_F24:
|
||||||
|
return VK_F24;
|
||||||
|
case Qt::Key_Space:
|
||||||
|
return VK_SPACE;
|
||||||
|
case Qt::Key_Asterisk:
|
||||||
|
return VK_MULTIPLY;
|
||||||
|
case Qt::Key_Plus:
|
||||||
|
return VK_ADD;
|
||||||
|
case Qt::Key_Comma:
|
||||||
|
return VK_SEPARATOR;
|
||||||
|
case Qt::Key_Minus:
|
||||||
|
return VK_SUBTRACT;
|
||||||
|
case Qt::Key_Slash:
|
||||||
|
return VK_DIVIDE;
|
||||||
|
case Qt::Key_MediaNext:
|
||||||
|
return VK_MEDIA_NEXT_TRACK;
|
||||||
|
case Qt::Key_MediaPrevious:
|
||||||
|
return VK_MEDIA_PREV_TRACK;
|
||||||
|
case Qt::Key_MediaPlay:
|
||||||
|
return VK_MEDIA_PLAY_PAUSE;
|
||||||
|
case Qt::Key_MediaStop:
|
||||||
|
return VK_MEDIA_STOP;
|
||||||
|
// couldn't find those in VK_*
|
||||||
|
//case Qt::Key_MediaLast:
|
||||||
|
//case Qt::Key_MediaRecord:
|
||||||
|
case Qt::Key_VolumeDown:
|
||||||
|
return VK_VOLUME_DOWN;
|
||||||
|
case Qt::Key_VolumeUp:
|
||||||
|
return VK_VOLUME_UP;
|
||||||
|
case Qt::Key_VolumeMute:
|
||||||
|
return VK_VOLUME_MUTE;
|
||||||
|
|
||||||
|
// numbers
|
||||||
|
case Qt::Key_0:
|
||||||
|
case Qt::Key_1:
|
||||||
|
case Qt::Key_2:
|
||||||
|
case Qt::Key_3:
|
||||||
|
case Qt::Key_4:
|
||||||
|
case Qt::Key_5:
|
||||||
|
case Qt::Key_6:
|
||||||
|
case Qt::Key_7:
|
||||||
|
case Qt::Key_8:
|
||||||
|
case Qt::Key_9:
|
||||||
|
return key;
|
||||||
|
|
||||||
|
// letters
|
||||||
|
case Qt::Key_A:
|
||||||
|
case Qt::Key_B:
|
||||||
|
case Qt::Key_C:
|
||||||
|
case Qt::Key_D:
|
||||||
|
case Qt::Key_E:
|
||||||
|
case Qt::Key_F:
|
||||||
|
case Qt::Key_G:
|
||||||
|
case Qt::Key_H:
|
||||||
|
case Qt::Key_I:
|
||||||
|
case Qt::Key_J:
|
||||||
|
case Qt::Key_K:
|
||||||
|
case Qt::Key_L:
|
||||||
|
case Qt::Key_M:
|
||||||
|
case Qt::Key_N:
|
||||||
|
case Qt::Key_O:
|
||||||
|
case Qt::Key_P:
|
||||||
|
case Qt::Key_Q:
|
||||||
|
case Qt::Key_R:
|
||||||
|
case Qt::Key_S:
|
||||||
|
case Qt::Key_T:
|
||||||
|
case Qt::Key_U:
|
||||||
|
case Qt::Key_V:
|
||||||
|
case Qt::Key_W:
|
||||||
|
case Qt::Key_X:
|
||||||
|
case Qt::Key_Y:
|
||||||
|
case Qt::Key_Z:
|
||||||
|
return key;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 GlobalActionHelper::nativeModifiers(Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
// MOD_ALT, MOD_CONTROL, (MOD_KEYUP), MOD_SHIFT, MOD_WIN
|
||||||
|
quint32 native = 0;
|
||||||
|
if (modifiers & Qt::ShiftModifier)
|
||||||
|
native |= MOD_SHIFT;
|
||||||
|
if (modifiers & Qt::ControlModifier)
|
||||||
|
native |= MOD_CONTROL;
|
||||||
|
if (modifiers & Qt::AltModifier)
|
||||||
|
native |= MOD_ALT;
|
||||||
|
if (modifiers & Qt::MetaModifier)
|
||||||
|
native |= MOD_WIN;
|
||||||
|
//if (modifiers & Qt::KeypadModifier)
|
||||||
|
//if (modifiers & Qt::GroupSwitchModifier)
|
||||||
|
return native;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GlobalActionHelper::registerHotKey(quint32 nativeKey, quint32 nativeMods)
|
||||||
|
{
|
||||||
|
return RegisterHotKey(0, nativeMods ^ nativeKey, nativeMods, nativeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GlobalActionHelper::unregisterHotKey(quint32 nativeKey, quint32 nativeMods)
|
||||||
|
{
|
||||||
|
return UnregisterHotKey(0, nativeMods ^ nativeKey);
|
||||||
|
}
|
29
GlobalActionHelper.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef GLOBALACTIONHELPER_H
|
||||||
|
#define GLOBALACTIONHELPER_H
|
||||||
|
|
||||||
|
// Some functions copied from QXT lib
|
||||||
|
|
||||||
|
#include <QAbstractNativeEventFilter>
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
|
class GlobalActionHelper : public QAbstractNativeEventFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool nativeEventFilter (const QByteArray &eventType, void *message,
|
||||||
|
long *result);
|
||||||
|
|
||||||
|
static void init ();
|
||||||
|
static bool makeGlobal (QAction* action);
|
||||||
|
static bool removeGlobal (QAction* action);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QHash<QPair<quint32, quint32>, QAction*> actions_;
|
||||||
|
|
||||||
|
static quint32 nativeKeycode (Qt::Key key);
|
||||||
|
static quint32 nativeModifiers (Qt::KeyboardModifiers modifiers);
|
||||||
|
static bool registerHotKey (quint32 nativeKey, quint32 nativeMods);
|
||||||
|
static bool unregisterHotKey (quint32 nativeKey, quint32 nativeMods);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GLOBALACTIONHELPER_H
|
93
GoogleWebTranslator.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#include <QWebView>
|
||||||
|
#include <QWebFrame>
|
||||||
|
#include <QWebElement>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "GoogleWebTranslator.h"
|
||||||
|
#include "Settings.h"
|
||||||
|
#include "StAssert.h"
|
||||||
|
|
||||||
|
GoogleWebTranslator::GoogleWebTranslator()
|
||||||
|
: QObject (), view_ (new QWebView),
|
||||||
|
isLoadFinished_ (true), isTranslationFinished_ (false) {
|
||||||
|
|
||||||
|
view_->settings()->setAttribute(QWebSettings::AutoLoadImages, false);
|
||||||
|
connect (view_, SIGNAL (loadStarted()), this, SLOT (loadStarted()));
|
||||||
|
connect (view_, SIGNAL (loadFinished(bool)), this, SLOT (loadFinished(bool)));
|
||||||
|
|
||||||
|
connect (view_->page()->networkAccessManager(), SIGNAL (finished(QNetworkReply*)),
|
||||||
|
this, SLOT(replyFinished(QNetworkReply*)));
|
||||||
|
applySettings ();
|
||||||
|
}
|
||||||
|
|
||||||
|
GoogleWebTranslator::~GoogleWebTranslator() {
|
||||||
|
delete view_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GoogleWebTranslator::translate(ProcessingItem item) {
|
||||||
|
queue_.push_back (item);
|
||||||
|
if (isLoadFinished_) {
|
||||||
|
load (item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GoogleWebTranslator::applySettings(){
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup (settings_names::translationGroup);
|
||||||
|
translationLanguage_ = settings.value (settings_names::translationLanguage,
|
||||||
|
settings_values::translationLanguage).toString ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GoogleWebTranslator::loadStarted() {
|
||||||
|
isLoadFinished_ = false;
|
||||||
|
isTranslationFinished_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GoogleWebTranslator::loadFinished(bool ok) {
|
||||||
|
isLoadFinished_ = true;
|
||||||
|
if (ok && !isTranslationFinished_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!queue_.isEmpty()) {
|
||||||
|
ProcessingItem item = queue_.front();
|
||||||
|
queue_.pop_front();
|
||||||
|
if (ok) {
|
||||||
|
QWebElementCollection result = view_->page()->mainFrame()->findAllElements("#result_box > span");
|
||||||
|
item.translated = "";
|
||||||
|
foreach (const QWebElement& element, result) {
|
||||||
|
item.translated += element.toInnerXml() + " ";
|
||||||
|
}
|
||||||
|
emit translated(item, !item.translated.isEmpty());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
emit translated (item, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!queue_.isEmpty()) {
|
||||||
|
load (queue_.front());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GoogleWebTranslator::replyFinished(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
if (reply->url().toString().contains ("/translate_a/single")) {
|
||||||
|
isTranslationFinished_ = true;
|
||||||
|
if (isLoadFinished_) {
|
||||||
|
QTimer::singleShot(2000, this, SLOT(loadFinished()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GoogleWebTranslator::load(const ProcessingItem &item) {
|
||||||
|
ST_ASSERT (!item.recognized.isEmpty ());
|
||||||
|
if (translationLanguage_.isEmpty ()) {
|
||||||
|
emit error (tr ("Неверные парметры для перевода."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QUrl url (QString ("https://translate.google.com/#auto/%1/%2").arg(translationLanguage_, item.recognized));
|
||||||
|
view_->setUrl(url);
|
||||||
|
}
|
43
GoogleWebTranslator.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#ifndef GOOGLEWEBTRANSLATOR_H
|
||||||
|
#define GOOGLEWEBTRANSLATOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "ProcessingItem.h"
|
||||||
|
|
||||||
|
class QWebView;
|
||||||
|
class QUrl;
|
||||||
|
class QNetworkReply;
|
||||||
|
|
||||||
|
class GoogleWebTranslator : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
GoogleWebTranslator();
|
||||||
|
~GoogleWebTranslator();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void translated (ProcessingItem item, bool success);
|
||||||
|
void error (QString text);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void translate (ProcessingItem item);
|
||||||
|
void applySettings ();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void loadStarted ();
|
||||||
|
void loadFinished(bool ok=true);
|
||||||
|
void replyFinished(QNetworkReply * reply);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void load (const ProcessingItem& item);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<ProcessingItem> queue_;
|
||||||
|
QString translationLanguage_;
|
||||||
|
QWebView *view_;
|
||||||
|
bool isLoadFinished_;
|
||||||
|
bool isTranslationFinished_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GOOGLEWEBTRANSLATOR_H
|
152
ImageProcessing.cpp
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <leptonica/allheaders.h>
|
||||||
|
|
||||||
|
#include <tesseract/host.h>
|
||||||
|
|
||||||
|
#include "ImageProcessing.h"
|
||||||
|
#include "StAssert.h"
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
qint64 getFreeMemory ()
|
||||||
|
{
|
||||||
|
MEMORYSTATUSEX statex;
|
||||||
|
statex.dwLength = sizeof (statex);
|
||||||
|
if (GlobalMemoryStatusEx (&statex))
|
||||||
|
{
|
||||||
|
return statex.ullAvailPhys;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Pix *convertImage(const QImage& image)
|
||||||
|
{
|
||||||
|
PIX *pix;
|
||||||
|
|
||||||
|
QImage swapped = image.rgbSwapped();
|
||||||
|
int width = swapped.width();
|
||||||
|
int height = swapped.height();
|
||||||
|
int depth = swapped.depth();
|
||||||
|
int wpl = swapped.bytesPerLine() / 4;
|
||||||
|
|
||||||
|
pix = pixCreate(width, height, depth);
|
||||||
|
pixSetWpl(pix, wpl);
|
||||||
|
pixSetColormap(pix, NULL);
|
||||||
|
l_uint32 *outData = pix->data;
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
l_uint32 *lines = outData + y * wpl;
|
||||||
|
QByteArray a((const char*)swapped.scanLine(y), swapped.bytesPerLine());
|
||||||
|
for (int j = 0; j < a.size(); j++)
|
||||||
|
{
|
||||||
|
*((l_uint8 *)lines + j) = a[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const qreal toDPM = 1.0 / 0.0254;
|
||||||
|
int resolutionX = swapped.dotsPerMeterX() / toDPM;
|
||||||
|
int resolutionY = swapped.dotsPerMeterY() / toDPM;
|
||||||
|
|
||||||
|
if (resolutionX < 300) resolutionX = 300;
|
||||||
|
if (resolutionY < 300) resolutionY = 300;
|
||||||
|
pixSetResolution(pix, resolutionX, resolutionY);
|
||||||
|
|
||||||
|
return pixEndianByteSwapNew(pix);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage convertImage(Pix &image)
|
||||||
|
{
|
||||||
|
int width = pixGetWidth(&image);
|
||||||
|
int height = pixGetHeight(&image);
|
||||||
|
int depth = pixGetDepth(&image);
|
||||||
|
int bytesPerLine = pixGetWpl(&image) * 4;
|
||||||
|
l_uint32 * datas = pixGetData(pixEndianByteSwapNew(&image));
|
||||||
|
|
||||||
|
QImage::Format format;
|
||||||
|
if (depth == 1)
|
||||||
|
format = QImage::Format_Mono;
|
||||||
|
else if (depth == 8)
|
||||||
|
format = QImage::Format_Indexed8;
|
||||||
|
else
|
||||||
|
format = QImage::Format_RGB32;
|
||||||
|
|
||||||
|
QImage result((uchar*)datas, width, height, bytesPerLine, format);
|
||||||
|
|
||||||
|
// Set resolution
|
||||||
|
l_int32 xres, yres;
|
||||||
|
pixGetResolution(&image, &xres, &yres);
|
||||||
|
const qreal toDPM = 1.0 / 0.0254;
|
||||||
|
result.setDotsPerMeterX(xres * toDPM);
|
||||||
|
result.setDotsPerMeterY(yres * toDPM);
|
||||||
|
|
||||||
|
// Handle pallete
|
||||||
|
QVector<QRgb> _bwCT;
|
||||||
|
_bwCT.append(qRgb(255,255,255));
|
||||||
|
_bwCT.append(qRgb(0,0,0));
|
||||||
|
|
||||||
|
QVector<QRgb> _grayscaleCT(256);
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
_grayscaleCT.append(qRgb(i, i, i));
|
||||||
|
}
|
||||||
|
switch (depth) {
|
||||||
|
case 1:
|
||||||
|
result.setColorTable(_bwCT);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
result.setColorTable(_grayscaleCT);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result.setColorTable(_grayscaleCT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isNull()) {
|
||||||
|
static QImage none(0,0,QImage::Format_Invalid);
|
||||||
|
qDebug("Invalid format!!!\n");
|
||||||
|
return none;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.rgbSwapped();
|
||||||
|
}
|
||||||
|
|
||||||
|
Pix *prepareImage(const QImage &image, int preferredScale)
|
||||||
|
{
|
||||||
|
Pix* pix = convertImage (image);
|
||||||
|
ST_ASSERT (pix != NULL);
|
||||||
|
|
||||||
|
Pix* gray = pixConvertRGBToGray (pix, 0.0, 0.0, 0.0);
|
||||||
|
ST_ASSERT (gray != NULL);
|
||||||
|
pixDestroy (&pix);
|
||||||
|
|
||||||
|
Pix* scaled = gray;
|
||||||
|
if (preferredScale > 0)
|
||||||
|
{
|
||||||
|
float maxScaleX = MAX_INT16 / double (gray->w);
|
||||||
|
float scaleX = std::min (float (preferredScale), maxScaleX);
|
||||||
|
float maxScaleY = MAX_INT16 / double (gray->h);
|
||||||
|
float scaleY = std::min (float (preferredScale), maxScaleY);
|
||||||
|
float scale = std::min (scaleX, scaleY);
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
qint64 availableMemory = getFreeMemory () * 0.95;
|
||||||
|
qint32 actualSize = gray->w * gray->h * gray->d / 8;
|
||||||
|
float maxScaleMemory = float (availableMemory) / actualSize;
|
||||||
|
scale = std::min (scale, maxScaleMemory);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
scaled = pixScale (gray, scale, scale);
|
||||||
|
}
|
||||||
|
ST_ASSERT (scaled != NULL);
|
||||||
|
if (scaled != gray)
|
||||||
|
{
|
||||||
|
pixDestroy (&gray);
|
||||||
|
}
|
||||||
|
return scaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanupImage(Pix **image)
|
||||||
|
{
|
||||||
|
pixDestroy (image);
|
||||||
|
}
|
18
ImageProcessing.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef IMAGEPROCESSING_H
|
||||||
|
#define IMAGEPROCESSING_H
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
class Pix;
|
||||||
|
|
||||||
|
//! Convert QImage to Leptonica's PIX.
|
||||||
|
Pix* convertImage(const QImage& image);
|
||||||
|
//! Convert Leptonica's PIX to QImage.
|
||||||
|
QImage convertImage(Pix &image);
|
||||||
|
|
||||||
|
//! Propare image for OCR.
|
||||||
|
Pix* prepareImage (const QImage& image, int preferredScale);
|
||||||
|
//! Free allocated resources for image.
|
||||||
|
void cleanupImage (Pix** image);
|
||||||
|
|
||||||
|
#endif // IMAGEPROCESSING_H
|
@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2017 Gres (gres@gres.biz)
|
Copyright (c) 2014 Gres (gres@gres.biz)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
244
LanguageHelper.cpp
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
#include <QDir>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
#include "LanguageHelper.h"
|
||||||
|
#include "Settings.h"
|
||||||
|
|
||||||
|
LanguageHelper::LanguageHelper()
|
||||||
|
{
|
||||||
|
init ();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList LanguageHelper::availableOcrLanguagesUi() const
|
||||||
|
{
|
||||||
|
QStringList uiItems;
|
||||||
|
foreach (const QString& item, availableOcrLanguages_)
|
||||||
|
{
|
||||||
|
uiItems << ocrCodeToUi (item);
|
||||||
|
}
|
||||||
|
uiItems.sort ();
|
||||||
|
return uiItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList &LanguageHelper::availableOcrLanguages() const
|
||||||
|
{
|
||||||
|
return availableOcrLanguages_;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList LanguageHelper::availableOcrLanguages(const QString &path) const
|
||||||
|
{
|
||||||
|
QDir dir (path + "/tessdata/");
|
||||||
|
if (!dir.exists ())
|
||||||
|
{
|
||||||
|
return QStringList ();
|
||||||
|
}
|
||||||
|
QStringList items;
|
||||||
|
QStringList files = dir.entryList (QStringList () << "*.traineddata", QDir::Files);
|
||||||
|
foreach (const QString& file, files)
|
||||||
|
{
|
||||||
|
QString lang = file.left (file.indexOf ("."));
|
||||||
|
items << lang;
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList LanguageHelper::availableOcrLanguagesUi(const QString &path) const
|
||||||
|
{
|
||||||
|
QStringList uiItems, items;
|
||||||
|
items = availableOcrLanguages (path);
|
||||||
|
foreach (const QString& item, items)
|
||||||
|
{
|
||||||
|
uiItems << ocrCodeToUi (item);
|
||||||
|
}
|
||||||
|
uiItems.sort ();
|
||||||
|
return uiItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList LanguageHelper::translateLanguagesUi() const
|
||||||
|
{
|
||||||
|
QStringList uiItems = translateLanguages_.keys ();
|
||||||
|
uiItems.sort ();
|
||||||
|
return uiItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList LanguageHelper::translateLanguages() const
|
||||||
|
{
|
||||||
|
return translateLanguages_.values ();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LanguageHelper::translateCodeToUi(const QString &text) const
|
||||||
|
{
|
||||||
|
return translateLanguages_.key (text, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LanguageHelper::translateUiToCode(const QString &text) const
|
||||||
|
{
|
||||||
|
return translateLanguages_.value (text, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LanguageHelper::ocrCodeToUi(const QString &text) const
|
||||||
|
{
|
||||||
|
return ocrLanguages_.key (text, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LanguageHelper::ocrUiToCode(const QString &text) const
|
||||||
|
{
|
||||||
|
return ocrLanguages_.value (text, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LanguageHelper::translateForOcrCode(const QString &text) const
|
||||||
|
{
|
||||||
|
QString ocrUi = ocrUiToCode (text);
|
||||||
|
QString translate = translateCodeToUi (ocrUi);
|
||||||
|
if (translate == ocrUi)
|
||||||
|
{
|
||||||
|
translate = "auto";
|
||||||
|
}
|
||||||
|
return translate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LanguageHelper::init()
|
||||||
|
{
|
||||||
|
initOcrLanguages ();
|
||||||
|
initTranslateLanguages ();
|
||||||
|
updateAvailableOcrLanguages ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LanguageHelper::updateAvailableOcrLanguages()
|
||||||
|
{
|
||||||
|
availableOcrLanguages_.clear ();
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup (settings_names::recogntionGroup);
|
||||||
|
QString tessDataPlace = settings.value (settings_names::tessDataPlace,
|
||||||
|
settings_values::tessDataPlace).toString ();
|
||||||
|
availableOcrLanguages_ = availableOcrLanguages (tessDataPlace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LanguageHelper::initTranslateLanguages()
|
||||||
|
{
|
||||||
|
translateLanguages_.insert(QObject::tr("Afrikaans"),"af");
|
||||||
|
translateLanguages_.insert(QObject::tr("Albanian"),"sq");
|
||||||
|
translateLanguages_.insert(QObject::tr("Arabic"),"ar");
|
||||||
|
translateLanguages_.insert(QObject::tr("Armenian"),"hy");
|
||||||
|
translateLanguages_.insert(QObject::tr("Azerbaijani"),"az");
|
||||||
|
translateLanguages_.insert(QObject::tr("Basque"),"eu");
|
||||||
|
translateLanguages_.insert(QObject::tr("Belarusian"),"be");
|
||||||
|
translateLanguages_.insert(QObject::tr("Bulgarian"),"bg");
|
||||||
|
translateLanguages_.insert(QObject::tr("Catalan"),"ca");
|
||||||
|
translateLanguages_.insert(QObject::tr("Chinese (Simplified)"),"zh-CN");
|
||||||
|
translateLanguages_.insert(QObject::tr("Chinese (Traditional)"),"zh-TW");
|
||||||
|
translateLanguages_.insert(QObject::tr("Croatian"),"hr");
|
||||||
|
translateLanguages_.insert(QObject::tr("Czech"),"cs");
|
||||||
|
translateLanguages_.insert(QObject::tr("Danish"),"da");
|
||||||
|
translateLanguages_.insert(QObject::tr("Dutch"),"nl");
|
||||||
|
translateLanguages_.insert(QObject::tr("English"),"en");
|
||||||
|
translateLanguages_.insert(QObject::tr("Estonian"),"et");
|
||||||
|
translateLanguages_.insert(QObject::tr("Filipino"),"tl");
|
||||||
|
translateLanguages_.insert(QObject::tr("Finnish"),"fi");
|
||||||
|
translateLanguages_.insert(QObject::tr("French"),"fr");
|
||||||
|
translateLanguages_.insert(QObject::tr("Galician"),"gl");
|
||||||
|
translateLanguages_.insert(QObject::tr("Georgian"),"ka");
|
||||||
|
translateLanguages_.insert(QObject::tr("German"),"de");
|
||||||
|
translateLanguages_.insert(QObject::tr("Greek"),"el");
|
||||||
|
translateLanguages_.insert(QObject::tr("Haitian Creole"),"ht");
|
||||||
|
translateLanguages_.insert(QObject::tr("Hebrew"),"iw");
|
||||||
|
translateLanguages_.insert(QObject::tr("Hindi"),"hi");
|
||||||
|
translateLanguages_.insert(QObject::tr("Hungarian"),"hu");
|
||||||
|
translateLanguages_.insert(QObject::tr("Icelandic"),"is");
|
||||||
|
translateLanguages_.insert(QObject::tr("Indonesian"),"id");
|
||||||
|
translateLanguages_.insert(QObject::tr("Irish"),"ga");
|
||||||
|
translateLanguages_.insert(QObject::tr("Italian"),"it");
|
||||||
|
translateLanguages_.insert(QObject::tr("Japanese"),"ja");
|
||||||
|
translateLanguages_.insert(QObject::tr("Korean"),"ko");
|
||||||
|
translateLanguages_.insert(QObject::tr("Latvian"),"lv");
|
||||||
|
translateLanguages_.insert(QObject::tr("Lithuanian"),"lt");
|
||||||
|
translateLanguages_.insert(QObject::tr("Macedonian"),"mk");
|
||||||
|
translateLanguages_.insert(QObject::tr("Malay"),"ms");
|
||||||
|
translateLanguages_.insert(QObject::tr("Maltese"),"mt");
|
||||||
|
translateLanguages_.insert(QObject::tr("Norwegian"),"no");
|
||||||
|
translateLanguages_.insert(QObject::tr("Persian"),"fa");
|
||||||
|
translateLanguages_.insert(QObject::tr("Polish"),"pl");
|
||||||
|
translateLanguages_.insert(QObject::tr("Portuguese"),"pt");
|
||||||
|
translateLanguages_.insert(QObject::tr("Romanian"),"ro");
|
||||||
|
translateLanguages_.insert(QObject::tr("Russian"),"ru");
|
||||||
|
translateLanguages_.insert(QObject::tr("Serbian"),"sr");
|
||||||
|
translateLanguages_.insert(QObject::tr("Slovak"),"sk");
|
||||||
|
translateLanguages_.insert(QObject::tr("Slovenian"),"sl");
|
||||||
|
translateLanguages_.insert(QObject::tr("Spanish"),"es");
|
||||||
|
translateLanguages_.insert(QObject::tr("Swahili"),"sw");
|
||||||
|
translateLanguages_.insert(QObject::tr("Swedish"),"sv");
|
||||||
|
translateLanguages_.insert(QObject::tr("Thai"),"th");
|
||||||
|
translateLanguages_.insert(QObject::tr("Turkish"),"tr");
|
||||||
|
translateLanguages_.insert(QObject::tr("Ukrainian"),"uk");
|
||||||
|
translateLanguages_.insert(QObject::tr("Urdu"),"ur");
|
||||||
|
translateLanguages_.insert(QObject::tr("Vietnamese"),"vi");
|
||||||
|
translateLanguages_.insert(QObject::tr("Welsh"),"cy");
|
||||||
|
translateLanguages_.insert(QObject::tr("Yiddish"),"yi");
|
||||||
|
}
|
||||||
|
|
||||||
|
void LanguageHelper::initOcrLanguages()
|
||||||
|
{
|
||||||
|
ocrLanguages_.insert(QObject::tr("Ancient Greek"),"grc");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Esperanto alternative"),"epo_alt");
|
||||||
|
ocrLanguages_.insert(QObject::tr("English"),"eng");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Ukrainian"),"ukr");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Turkish"),"tur");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Thai"),"tha");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Tagalog"),"tgl");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Telugu"),"tel");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Tamil"),"tam");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Swedish"),"swe");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Swahili"),"swa");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Serbian"),"srp");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Albanian"),"sqi");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Spanish"),"spa");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Slovenian"),"slv");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Slovakian"),"slk");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Romanian"),"ron");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Portuguese"),"por");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Polish"),"pol");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Norwegian"),"nor");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Dutch"),"nld");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Malay"),"msa");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Maltese"),"mlt");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Macedonian"),"mkd");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Malayalam"),"mal");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Lithuanian"),"lit");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Latvian"),"lav");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Korean"),"kor");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Kannada"),"kan");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Italian"),"ita");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Icelandic"),"isl");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Indonesian"),"ind");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Cherokee"),"chr");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Hungarian"),"hun");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Croatian"),"hrv");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Hindi"),"hin");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Hebrew"),"heb");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Galician"),"glg");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Middle French (ca. 1400-1600)"),"frm");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Frankish"),"frk");
|
||||||
|
ocrLanguages_.insert(QObject::tr("French"),"fra");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Finnish"),"fin");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Basque"),"eus");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Estonian"),"est");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Math / equation"),"equ");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Esperanto"),"epo");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Middle English (1100-1500)"),"enm");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Greek"),"ell");
|
||||||
|
ocrLanguages_.insert(QObject::tr("German"),"deu");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Danish"),"dan");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Czech"),"ces");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Catalan"),"cat");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Bulgarian"),"bul");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Bengali"),"ben");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Belarusian"),"bel");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Azerbaijani"),"aze");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Arabic"),"ara");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Afrikaans"),"afr");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Japanese"),"jpn");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Chinese (Simplified)"),"chi_sim");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Chinese (Traditional)"),"chi_tra");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Russian"),"rus");
|
||||||
|
ocrLanguages_.insert(QObject::tr("Vietnamese"),"vie");
|
||||||
|
}
|
39
LanguageHelper.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef LANGUAGEHELPER_H
|
||||||
|
#define LANGUAGEHELPER_H
|
||||||
|
|
||||||
|
#include <QMap>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
class LanguageHelper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LanguageHelper ();
|
||||||
|
|
||||||
|
QStringList availableOcrLanguagesUi () const;
|
||||||
|
const QStringList& availableOcrLanguages () const;
|
||||||
|
QStringList availableOcrLanguagesUi (const QString& path) const;
|
||||||
|
QStringList translateLanguagesUi () const;
|
||||||
|
QStringList translateLanguages () const;
|
||||||
|
|
||||||
|
QString translateCodeToUi (const QString& text) const;
|
||||||
|
QString translateUiToCode (const QString& text) const;
|
||||||
|
QString ocrCodeToUi (const QString& text) const;
|
||||||
|
QString ocrUiToCode (const QString& text) const;
|
||||||
|
QString translateForOcrCode (const QString& text) const;
|
||||||
|
|
||||||
|
void updateAvailableOcrLanguages ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QStringList availableOcrLanguages (const QString& path) const;
|
||||||
|
void init ();
|
||||||
|
void initTranslateLanguages ();
|
||||||
|
void initOcrLanguages ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QStringList availableOcrLanguages_;
|
||||||
|
QMap<QString, QString> translateLanguages_;
|
||||||
|
QMap<QString, QString> ocrLanguages_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // LANGUAGEHELPER_H
|
227
Manager.cpp
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
#include "Manager.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDesktopWidget>
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include "Settings.h"
|
||||||
|
#include "SettingsEditor.h"
|
||||||
|
#include "SelectionDialog.h"
|
||||||
|
#include "GlobalActionHelper.h"
|
||||||
|
#include "Recognizer.h"
|
||||||
|
#include "Translator.h"
|
||||||
|
#include "ResultDialog.h"
|
||||||
|
#include "LanguageHelper.h"
|
||||||
|
#include "StAssert.h"
|
||||||
|
|
||||||
|
Manager::Manager(QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
trayIcon_ (new QSystemTrayIcon (QIcon (":/images/icon.png"), this)),
|
||||||
|
dictionary_ (new LanguageHelper),
|
||||||
|
selection_ (new SelectionDialog (*dictionary_)),
|
||||||
|
resultDialog_ (new ResultDialog),
|
||||||
|
captureAction_ (NULL), repeatAction_ (NULL), clipboardAction_ (NULL),
|
||||||
|
useResultDialog_ (true)
|
||||||
|
{
|
||||||
|
GlobalActionHelper::init ();
|
||||||
|
qRegisterMetaType<ProcessingItem>();
|
||||||
|
|
||||||
|
// Recognizer
|
||||||
|
Recognizer* recognizer = new Recognizer;
|
||||||
|
connect (selection_, SIGNAL (selected (ProcessingItem)),
|
||||||
|
recognizer, SLOT (recognize (ProcessingItem)));
|
||||||
|
connect (recognizer, SIGNAL (error (QString)),
|
||||||
|
SLOT (showError (QString)));
|
||||||
|
connect (this, SIGNAL (settingsEdited ()),
|
||||||
|
recognizer, SLOT (applySettings ()));
|
||||||
|
QThread* recognizerThread = new QThread (this);
|
||||||
|
recognizer->moveToThread (recognizerThread);
|
||||||
|
recognizerThread->start ();
|
||||||
|
connect (qApp, SIGNAL (aboutToQuit ()), recognizerThread, SLOT (quit ()));
|
||||||
|
|
||||||
|
|
||||||
|
// Translator
|
||||||
|
Translator* translator = new Translator;
|
||||||
|
connect (recognizer, SIGNAL (recognized (ProcessingItem)),
|
||||||
|
translator, SLOT (translate (ProcessingItem)));
|
||||||
|
connect (translator, SIGNAL (error (QString)),
|
||||||
|
SLOT (showError (QString)));
|
||||||
|
connect (this, SIGNAL (settingsEdited ()),
|
||||||
|
translator, SLOT (applySettings ()));
|
||||||
|
QThread* translatorThread = new QThread (this);
|
||||||
|
translator->moveToThread (translatorThread);
|
||||||
|
translatorThread->start ();
|
||||||
|
connect (qApp, SIGNAL (aboutToQuit ()), translatorThread, SLOT (quit ()));
|
||||||
|
|
||||||
|
connect (translator, SIGNAL (translated (ProcessingItem)),
|
||||||
|
SLOT (showResult (ProcessingItem)));
|
||||||
|
|
||||||
|
connect (this, SIGNAL (showPixmap (QPixmap)),
|
||||||
|
selection_, SLOT (setPixmap (QPixmap)));
|
||||||
|
|
||||||
|
connect (this, SIGNAL (settingsEdited ()), selection_, SLOT (updateMenu ()));
|
||||||
|
connect (this, SIGNAL (settingsEdited ()), this, SLOT (applySettings ()));
|
||||||
|
selection_->setWindowIcon (trayIcon_->icon ());
|
||||||
|
resultDialog_->setWindowIcon (trayIcon_->icon ());
|
||||||
|
|
||||||
|
|
||||||
|
connect (trayIcon_, SIGNAL (activated (QSystemTrayIcon::ActivationReason)),
|
||||||
|
SLOT (processTrayAction (QSystemTrayIcon::ActivationReason)));
|
||||||
|
|
||||||
|
trayIcon_->setContextMenu (trayContextMenu ());
|
||||||
|
trayIcon_->show ();
|
||||||
|
|
||||||
|
applySettings ();
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu*Manager::trayContextMenu()
|
||||||
|
{
|
||||||
|
QMenu* menu = new QMenu ();
|
||||||
|
captureAction_ = menu->addAction (tr ("Захват"), this, SLOT (capture ()));
|
||||||
|
QMenu* translateMenu = menu->addMenu (tr ("Перевод"));
|
||||||
|
repeatAction_ = translateMenu->addAction (tr ("Повторить"), this,
|
||||||
|
SLOT (showLast ()));
|
||||||
|
clipboardAction_ = translateMenu->addAction (tr ("Скопировать"), this,
|
||||||
|
SLOT (copyLastToClipboard ()));
|
||||||
|
menu->addAction (tr ("Настройки"), this, SLOT (settings ()));
|
||||||
|
menu->addAction (tr ("О программе"), this, SLOT (about ()));
|
||||||
|
menu->addAction (tr ("Выход"), this, SLOT (close ()));
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::applySettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup (settings_names::guiGroup);
|
||||||
|
QString captureHotkey = settings.value (settings_names::captureHotkey,
|
||||||
|
settings_values::captureHotkey).toString ();
|
||||||
|
Q_CHECK_PTR (captureAction_);
|
||||||
|
GlobalActionHelper::removeGlobal (captureAction_);
|
||||||
|
captureAction_->setShortcut (captureHotkey);
|
||||||
|
GlobalActionHelper::makeGlobal (captureAction_);
|
||||||
|
|
||||||
|
QString repeatHotkey = settings.value (settings_names::repeatHotkey,
|
||||||
|
settings_values::repeatHotkey).toString ();
|
||||||
|
Q_CHECK_PTR (repeatAction_);
|
||||||
|
GlobalActionHelper::removeGlobal (repeatAction_);
|
||||||
|
repeatAction_->setShortcut (repeatHotkey);
|
||||||
|
GlobalActionHelper::makeGlobal (repeatAction_);
|
||||||
|
|
||||||
|
QString clipboardHotkey = settings.value (settings_names::clipboardHotkey,
|
||||||
|
settings_values::clipboardHotkey).toString ();
|
||||||
|
Q_CHECK_PTR (clipboardAction_);
|
||||||
|
GlobalActionHelper::removeGlobal (clipboardAction_);
|
||||||
|
clipboardAction_->setShortcut (clipboardHotkey);
|
||||||
|
GlobalActionHelper::makeGlobal (clipboardAction_);
|
||||||
|
|
||||||
|
// Depends on SettingsEditor button indexes. 1==dialog
|
||||||
|
useResultDialog_ = settings.value (settings_names::resultShowType,
|
||||||
|
settings_values::resultShowType).toBool ();
|
||||||
|
|
||||||
|
Q_CHECK_PTR (dictionary_);
|
||||||
|
dictionary_->updateAvailableOcrLanguages ();
|
||||||
|
}
|
||||||
|
|
||||||
|
Manager::~Manager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::capture()
|
||||||
|
{
|
||||||
|
QList<QScreen*> screens = QApplication::screens ();
|
||||||
|
ST_ASSERT (!screens.isEmpty ());
|
||||||
|
QScreen* screen = screens.first ();
|
||||||
|
Q_CHECK_PTR (screen);
|
||||||
|
WId desktopId = QApplication::desktop ()->winId ();
|
||||||
|
QPixmap pixmap = screen->grabWindow (desktopId);
|
||||||
|
ST_ASSERT (!pixmap.isNull ());
|
||||||
|
emit showPixmap (pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::settings()
|
||||||
|
{
|
||||||
|
SettingsEditor editor (*dictionary_);
|
||||||
|
editor.setWindowIcon (trayIcon_->icon ());
|
||||||
|
connect (&editor, SIGNAL (settingsEdited ()), SIGNAL (settingsEdited ()));
|
||||||
|
editor.exec ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::close()
|
||||||
|
{
|
||||||
|
QApplication::quit ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::about()
|
||||||
|
{
|
||||||
|
QString version = "1.2.3";
|
||||||
|
QString text = tr ("Программа для распознавания текста на экране.\n"\
|
||||||
|
"Создана с использованием Qt, tesseract-ocr, Google Translate.\n"
|
||||||
|
"Автор: Gres (translator@gres.biz)\n"
|
||||||
|
"Версия: %1 от %2 %3").arg (version)
|
||||||
|
.arg (__DATE__).arg (__TIME__);
|
||||||
|
|
||||||
|
QMessageBox message (QMessageBox::Information, tr ("О программе"), text,
|
||||||
|
QMessageBox::Ok);
|
||||||
|
message.setIconPixmap (trayIcon_->icon ().pixmap (QSize (64, 64)));
|
||||||
|
message.exec ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::processTrayAction(QSystemTrayIcon::ActivationReason reason)
|
||||||
|
{
|
||||||
|
if (reason == QSystemTrayIcon::Trigger)
|
||||||
|
{
|
||||||
|
showLast ();
|
||||||
|
}
|
||||||
|
else if (reason == QSystemTrayIcon::MiddleClick)
|
||||||
|
{
|
||||||
|
copyLastToClipboard ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::showLast()
|
||||||
|
{
|
||||||
|
if (lastItem_.isValid ())
|
||||||
|
{
|
||||||
|
showResult (lastItem_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::copyLastToClipboard()
|
||||||
|
{
|
||||||
|
if (lastItem_.isValid ())
|
||||||
|
{
|
||||||
|
QClipboard* clipboard = QApplication::clipboard ();
|
||||||
|
QString message = lastItem_.recognized + " - " + lastItem_.translated;
|
||||||
|
clipboard->setText (message);
|
||||||
|
trayIcon_->showMessage (tr ("Перевод"),
|
||||||
|
tr ("Последний перевод был скопирован в буфер обмена."),
|
||||||
|
QSystemTrayIcon::Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::showResult(ProcessingItem item)
|
||||||
|
{
|
||||||
|
ST_ASSERT (item.isValid ());
|
||||||
|
lastItem_ = item;
|
||||||
|
if (useResultDialog_)
|
||||||
|
{
|
||||||
|
resultDialog_->showResult (item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QString message = item.recognized + " - " + item.translated;
|
||||||
|
trayIcon_->showMessage (tr ("Перевод"), message, QSystemTrayIcon::Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::showError(QString text)
|
||||||
|
{
|
||||||
|
qCritical () << text;
|
||||||
|
trayIcon_->showMessage (tr ("Ошибка"), text, QSystemTrayIcon::Critical);
|
||||||
|
}
|
57
Manager.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#ifndef MANAGER_H
|
||||||
|
#define MANAGER_H
|
||||||
|
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QSystemTrayIcon>
|
||||||
|
|
||||||
|
#include "ProcessingItem.h"
|
||||||
|
|
||||||
|
class QAction;
|
||||||
|
class QMenu;
|
||||||
|
|
||||||
|
class SelectionDialog;
|
||||||
|
class ResultDialog;
|
||||||
|
class LanguageHelper;
|
||||||
|
|
||||||
|
class Manager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Manager(QObject *parent = 0);
|
||||||
|
~Manager ();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void showPixmap (QPixmap pixmap);
|
||||||
|
void settingsEdited ();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void capture ();
|
||||||
|
void settings ();
|
||||||
|
void close ();
|
||||||
|
void about ();
|
||||||
|
void showLast ();
|
||||||
|
void copyLastToClipboard ();
|
||||||
|
|
||||||
|
void applySettings ();
|
||||||
|
|
||||||
|
void processTrayAction (QSystemTrayIcon::ActivationReason reason);
|
||||||
|
|
||||||
|
void showResult (ProcessingItem item);
|
||||||
|
void showError (QString text);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMenu* trayContextMenu ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSystemTrayIcon* trayIcon_;
|
||||||
|
LanguageHelper* dictionary_;
|
||||||
|
SelectionDialog* selection_;
|
||||||
|
ResultDialog* resultDialog_;
|
||||||
|
QAction* captureAction_;
|
||||||
|
QAction* repeatAction_;
|
||||||
|
QAction* clipboardAction_;
|
||||||
|
ProcessingItem lastItem_;
|
||||||
|
bool useResultDialog_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MANAGER_H
|
11
ProcessingItem.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "ProcessingItem.h"
|
||||||
|
|
||||||
|
bool ProcessingItem::isValid() const
|
||||||
|
{
|
||||||
|
bool valid = true;
|
||||||
|
valid &= (!screenPos.isNull ());
|
||||||
|
valid &= (!source.isNull ());
|
||||||
|
valid &= (!recognized.isEmpty ());
|
||||||
|
valid &= (!translated.isEmpty ());
|
||||||
|
return valid;
|
||||||
|
}
|
20
ProcessingItem.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#ifndef PROCESSINGITEM_H
|
||||||
|
#define PROCESSINGITEM_H
|
||||||
|
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
struct ProcessingItem
|
||||||
|
{
|
||||||
|
QPoint screenPos;
|
||||||
|
QPixmap source;
|
||||||
|
QString recognized;
|
||||||
|
QString translated;
|
||||||
|
|
||||||
|
QString ocrLanguage;
|
||||||
|
QString sourceLanguage;
|
||||||
|
|
||||||
|
bool isValid () const;
|
||||||
|
};
|
||||||
|
Q_DECLARE_METATYPE(ProcessingItem)
|
||||||
|
|
||||||
|
#endif // PROCESSINGITEM_H
|
112
README.md
@ -1,92 +1,38 @@
|
|||||||
# Screen Translator
|
Screen Translator
|
||||||
|
=================
|
||||||
**The project is almost abandoned. I don't have time for it and I can only fix minor issues**
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
This software allows you to translate any text on screen.
|
This software allows you to translate any text on screen.
|
||||||
Basically it is a combination of screen capture, OCR and translation tools.
|
Basically it is a combination of screen capture, OCR and translation tools.
|
||||||
Translation is currently done via online services.
|
|
||||||
|
|
||||||
## Installation
|
Usage
|
||||||
|
-----
|
||||||
|
1. Press capture hotkey.
|
||||||
|
2. Select region on screen.
|
||||||
|
3. Get translation of recognized text.
|
||||||
|
|
||||||
**Windows**: download archive from [github releases](https://github.com/OneMoreGres/ScreenTranslator/releases) page, extract it and run `.exe` file.
|
Features
|
||||||
|
--------
|
||||||
|
* Many OCR languages (can be modified dynamicly)
|
||||||
|
* Global hotkeys for main actions
|
||||||
|
* Copy last translation to clipboard
|
||||||
|
* Repeat last translation
|
||||||
|
* Show result in 2 ways (widget or tray baloon)
|
||||||
|
* Preprocess (scale) recognizeable image
|
||||||
|
* Interface languages (ru, eng)
|
||||||
|
|
||||||
If the app fails to start complaining about missing dlls or there are any update errors related to SSL/TLS then install or repair `vs_redist*.exe` from the release archive.
|
|
||||||
|
|
||||||
**Linux**: download `.AppImage` file from [github releases](https://github.com/OneMoreGres/ScreenTranslator/releases), make executable (`chmod +x <file>`) and run it.
|
Limitations
|
||||||
|
-----------
|
||||||
|
* Works only on primary screen
|
||||||
|
* Can not capture some dynamic web-pages
|
||||||
|
* Not very precise OCR (need better preprocessing steps)
|
||||||
|
|
||||||
**OS X**: currently not supported.
|
Used software
|
||||||
|
-------------
|
||||||
|
* see [Qt 5](http://qt-project.org/)
|
||||||
|
* see [Tesseract](https://code.google.com/p/tesseract-ocr/)
|
||||||
|
* see [Leptonica](http://leptonica.com/) (Tesseract dependency)
|
||||||
|
* Google Translate
|
||||||
|
|
||||||
### App translation
|
|
||||||
|
|
||||||
To install Hebrew translation of the app (thanks to [Y-PLONI](https://github.com/Y-PLONI)),
|
|
||||||
download [this](https://github.com/OneMoreGres/ScreenTranslator/releases/download/3.3.0/screentranslator_he.qm)
|
|
||||||
file and place it into the `translations` folder next to `screen-translator.exe`.
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
The app doesn't have a main window.
|
|
||||||
After start it shows only the tray icon.
|
|
||||||
|
|
||||||
If the app detects invalid settings, it will show the error message via system tray.
|
|
||||||
It will also highlight the section name in red on the left panel of the settings window.
|
|
||||||
Clicking on that section name will show a more detailed error message in the right panel (also in red).
|
|
||||||
|
|
||||||
The packages downloaded from this site do not include resources, such as recognition language packs or scripts to interact with online translation services.
|
|
||||||
|
|
||||||
To download them, open the settings window and go to the `Update` section.
|
|
||||||
In the right panel, expand the `recognizers` and `translators` sections.
|
|
||||||
Select preferred items, then right click and choose `Install/Update`.
|
|
||||||
After the progress bar reaches `100%`, the resource's state will change to `Up to Date`.
|
|
||||||
|
|
||||||
You must download at least one `recognizers` resource and one `translators` resource.
|
|
||||||
|
|
||||||
After finishing downloads, go to the `Recognition` section and update the default recognition language setting (the source to be translated).
|
|
||||||
Then go to the `Translation` section, update the default translation language setting (the language to be translated into) and enable some or all translation sevices (you may also change their order by dragging).
|
|
||||||
|
|
||||||
After that all sections in the left panel should be black.
|
|
||||||
Then click `Ok` to close settings.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
1. Run program (note that it doesn't have main window).
|
|
||||||
2. Press capture hotkey.
|
|
||||||
3. Select region on screen. Customize it if needed.
|
|
||||||
4. Get translation of recognized text.
|
|
||||||
5. Check for updates if something is not working.
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
By default resources are downloaded to the one of the user's folders.
|
|
||||||
If `Portable` setting in `General` section is checked, then resources will be downloaded to the app's folder.
|
|
||||||
|
|
||||||
Set `QTWEBENGINE_DISABLE_SANDBOX=1` environment variable when fail to start due to crash.
|
|
||||||
|
|
||||||
Answers to some frequently asked questions can be found in issues or
|
|
||||||
[wiki](https://github.com/OneMoreGres/ScreenTranslator/wiki/FAQ)
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
* Can not capture some dynamic web-pages/full screen applications
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
* see [Qt 5](https://qt-project.org/)
|
|
||||||
* see [Tesseract](https://github.com/tesseract-ocr/tesseract/)
|
|
||||||
* see [Leptonica](https://leptonica.com/)
|
|
||||||
* several online translation services
|
|
||||||
|
|
||||||
## Build from source
|
|
||||||
|
|
||||||
Look at the scripts (python3) in the `share/ci` folder.
|
|
||||||
Normally, you should only edit the `config.py` file.
|
|
||||||
|
|
||||||
Build dependencies at first, then build the app.
|
|
||||||
|
|
||||||
## Attributions
|
|
||||||
|
|
||||||
* icons made by
|
|
||||||
[Smashicons](https://www.flaticon.com/authors/smashicons),
|
|
||||||
[Freepik](https://www.flaticon.com/authors/freepik),
|
|
||||||
from [Flaticon](https://www.flaticon.com/)
|
|
||||||
|
100
Recognizer.cpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#include "Recognizer.h"
|
||||||
|
|
||||||
|
#include <tesseract/baseapi.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
#include "Settings.h"
|
||||||
|
#include "ImageProcessing.h"
|
||||||
|
#include "StAssert.h"
|
||||||
|
|
||||||
|
Recognizer::Recognizer(QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
engine_ (NULL), imageScale_ (0)
|
||||||
|
{
|
||||||
|
applySettings ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Recognizer::applySettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup (settings_names::recogntionGroup);
|
||||||
|
|
||||||
|
tessDataDir_ = settings.value (settings_names::tessDataPlace,
|
||||||
|
settings_values::tessDataPlace).toString ();
|
||||||
|
if (tessDataDir_.right (1) != "/")
|
||||||
|
{
|
||||||
|
tessDataDir_ += "/";
|
||||||
|
}
|
||||||
|
ocrLanguage_ = settings.value (settings_names::ocrLanguage,
|
||||||
|
settings_values::ocrLanguage).toString ();
|
||||||
|
imageScale_ = settings.value (settings_names::imageScale,
|
||||||
|
settings_values::imageScale).toInt ();
|
||||||
|
|
||||||
|
initEngine (engine_, ocrLanguage_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Recognizer::initEngine(tesseract::TessBaseAPI *&engine, const QString& language)
|
||||||
|
{
|
||||||
|
if (tessDataDir_.isEmpty () || language.isEmpty ())
|
||||||
|
{
|
||||||
|
emit error (tr ("Неверные параметры для OCR"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (engine != NULL)
|
||||||
|
{
|
||||||
|
delete engine;
|
||||||
|
}
|
||||||
|
engine = new tesseract::TessBaseAPI();
|
||||||
|
int result = engine->Init(qPrintable (tessDataDir_), qPrintable (language),
|
||||||
|
tesseract::OEM_DEFAULT);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
emit error (tr ("Ошибка инициализации OCR: %1").arg (result));
|
||||||
|
delete engine;
|
||||||
|
engine = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Recognizer::recognize(ProcessingItem item)
|
||||||
|
{
|
||||||
|
ST_ASSERT (!item.source.isNull ());
|
||||||
|
bool isCustomLanguage = (!item.ocrLanguage.isEmpty () &&
|
||||||
|
item.ocrLanguage != ocrLanguage_);
|
||||||
|
tesseract::TessBaseAPI* engine = (isCustomLanguage) ? NULL : engine_;
|
||||||
|
if (engine == NULL)
|
||||||
|
{
|
||||||
|
QString language = (isCustomLanguage) ? item.ocrLanguage : ocrLanguage_;
|
||||||
|
if (!initEngine (engine, language))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pix* image = prepareImage (item.source.toImage (), imageScale_);
|
||||||
|
ST_ASSERT (image != NULL);
|
||||||
|
engine->SetImage (image);
|
||||||
|
char* outText = engine->GetUTF8Text();
|
||||||
|
engine->Clear();
|
||||||
|
cleanupImage (&image);
|
||||||
|
|
||||||
|
QString result = QString (outText).trimmed ();
|
||||||
|
delete [] outText;
|
||||||
|
if (isCustomLanguage)
|
||||||
|
{
|
||||||
|
delete engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.isEmpty ())
|
||||||
|
{
|
||||||
|
item.recognized = result;
|
||||||
|
emit recognized (item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emit error (tr ("Текст не распознан."));
|
||||||
|
}
|
||||||
|
}
|
40
Recognizer.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef RECOGNIZER_H
|
||||||
|
#define RECOGNIZER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include "QPixmap"
|
||||||
|
|
||||||
|
#include "ProcessingItem.h"
|
||||||
|
|
||||||
|
namespace tesseract
|
||||||
|
{
|
||||||
|
class TessBaseAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Recognizer : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Recognizer(QObject *parent = 0);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void recognized (ProcessingItem item);
|
||||||
|
void error (QString text);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void recognize (ProcessingItem item);
|
||||||
|
void applySettings ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool initEngine (tesseract::TessBaseAPI*&engine, const QString &language);
|
||||||
|
|
||||||
|
private:
|
||||||
|
tesseract::TessBaseAPI* engine_;
|
||||||
|
|
||||||
|
QString tessDataDir_;
|
||||||
|
QString ocrLanguage_;
|
||||||
|
int imageScale_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // RECOGNIZER_H
|
7
Recources.qrc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>translations/translation_en.qm</file>
|
||||||
|
<file>translations/translation_ru.qm</file>
|
||||||
|
<file>images/icon.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
69
ResultDialog.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#include "ResultDialog.h"
|
||||||
|
#include "ui_ResultDialog.h"
|
||||||
|
#include "StAssert.h"
|
||||||
|
|
||||||
|
#include <QDesktopWidget>
|
||||||
|
|
||||||
|
ResultDialog::ResultDialog(QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::ResultDialog),
|
||||||
|
isShowAtCapturePos_ (true)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
setWindowFlags (Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint |
|
||||||
|
Qt::WindowStaysOnTopHint);
|
||||||
|
|
||||||
|
installEventFilter (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultDialog::~ResultDialog()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResultDialog::eventFilter(QObject* object, QEvent* event)
|
||||||
|
{
|
||||||
|
Q_UNUSED (object);
|
||||||
|
if (event->type () == QEvent::MouseButtonRelease)
|
||||||
|
{
|
||||||
|
hide ();
|
||||||
|
}
|
||||||
|
return QDialog::eventFilter (object, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResultDialog::showResult(ProcessingItem item)
|
||||||
|
{
|
||||||
|
ST_ASSERT (!item.source.isNull ());
|
||||||
|
ST_ASSERT (!item.recognized.isEmpty ());
|
||||||
|
ST_ASSERT (!item.translated.isEmpty ());
|
||||||
|
ST_ASSERT (!item.screenPos.isNull ());
|
||||||
|
|
||||||
|
ui->sourceLabel->setPixmap (item.source);
|
||||||
|
ui->recognizeLabel->setText (item.recognized);
|
||||||
|
ui->translateLabel->setText (item.translated);
|
||||||
|
|
||||||
|
show ();
|
||||||
|
adjustSize ();
|
||||||
|
|
||||||
|
QDesktopWidget* desktop = QApplication::desktop ();
|
||||||
|
Q_CHECK_PTR (desktop);
|
||||||
|
if (isShowAtCapturePos_)
|
||||||
|
{
|
||||||
|
QPoint correction = QPoint (ui->frame->lineWidth (), ui->frame->lineWidth ());
|
||||||
|
move (item.screenPos - correction);
|
||||||
|
QRect screenRect = desktop->screenGeometry (this);
|
||||||
|
int minY = screenRect.bottom () - height ();
|
||||||
|
if (y () > minY)
|
||||||
|
{
|
||||||
|
move (x (), minY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
QRect screenRect = desktop->availableGeometry (this);
|
||||||
|
ST_ASSERT (screenRect.isValid ());
|
||||||
|
QPoint newPos (screenRect.width () - width (), screenRect.height () - height ());
|
||||||
|
move (newPos);
|
||||||
|
}
|
||||||
|
}
|
31
ResultDialog.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef RESULTDIALOG_H
|
||||||
|
#define RESULTDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
#include "ProcessingItem.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ResultDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResultDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ResultDialog(QWidget *parent = 0);
|
||||||
|
~ResultDialog();
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool eventFilter (QObject *object, QEvent *event);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void showResult (ProcessingItem item);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::ResultDialog *ui;
|
||||||
|
bool isShowAtCapturePos_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // RESULTDIALOG_H
|
98
ResultDialog.ui
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ResultDialog</class>
|
||||||
|
<widget class="QDialog" name="ResultDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>260</width>
|
||||||
|
<height>222</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Результат</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QFrame" name="frame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Plain</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="sourceLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="Line" name="line_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="recognizeLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="Line" name="line">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="translateLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
66
ScreenTranslator.pro
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#-------------------------------------------------
|
||||||
|
#
|
||||||
|
# Project created by QtCreator 2013-11-22T12:00:23
|
||||||
|
#
|
||||||
|
#-------------------------------------------------
|
||||||
|
|
||||||
|
QT += core gui network webkitwidgets
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
TARGET = ScreenTranslator
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
INCLUDEPATH += ../include
|
||||||
|
|
||||||
|
LIBS += -L../bin -ltesseract -llept -ltiff -lgif -ljpeg -lz
|
||||||
|
LIBS += -lWs2_32
|
||||||
|
|
||||||
|
SOURCES += main.cpp\
|
||||||
|
Manager.cpp \
|
||||||
|
SettingsEditor.cpp \
|
||||||
|
SelectionDialog.cpp \
|
||||||
|
GlobalActionHelper.cpp \
|
||||||
|
Recognizer.cpp \
|
||||||
|
Translator.cpp \
|
||||||
|
ResultDialog.cpp \
|
||||||
|
ProcessingItem.cpp \
|
||||||
|
ImageProcessing.cpp \
|
||||||
|
LanguageHelper.cpp \
|
||||||
|
GoogleWebTranslator.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
Manager.h \
|
||||||
|
SettingsEditor.h \
|
||||||
|
SelectionDialog.h \
|
||||||
|
GlobalActionHelper.h \
|
||||||
|
Recognizer.h \
|
||||||
|
Translator.h \
|
||||||
|
Settings.h \
|
||||||
|
ProcessingItem.h \
|
||||||
|
ResultDialog.h \
|
||||||
|
ImageProcessing.h \
|
||||||
|
LanguageHelper.h \
|
||||||
|
GoogleWebTranslator.h \
|
||||||
|
StAssert.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
SettingsEditor.ui \
|
||||||
|
SelectionDialog.ui \
|
||||||
|
ResultDialog.ui
|
||||||
|
|
||||||
|
RESOURCES += \
|
||||||
|
Recources.qrc
|
||||||
|
|
||||||
|
TRANSLATIONS += \
|
||||||
|
translations/translation_en.ts \
|
||||||
|
translations/translation_ru.ts
|
||||||
|
|
||||||
|
win32{
|
||||||
|
RC_FILE = app.rc
|
||||||
|
}
|
||||||
|
|
||||||
|
OTHER_FILES += \
|
||||||
|
app.rc \
|
||||||
|
images/icon.ico \
|
||||||
|
README.md
|
166
SelectionDialog.cpp
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
#include "SelectionDialog.h"
|
||||||
|
#include "ui_SelectionDialog.h"
|
||||||
|
#include "LanguageHelper.h"
|
||||||
|
#include "StAssert.h"
|
||||||
|
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
|
SelectionDialog::SelectionDialog(const LanguageHelper &dictionary, QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::SelectionDialog), dictionary_ (dictionary),
|
||||||
|
languageMenu_ (new QMenu)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
setWindowFlags (Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint |
|
||||||
|
Qt::WindowStaysOnTopHint);
|
||||||
|
|
||||||
|
ui->label->setAutoFillBackground(false);
|
||||||
|
ui->label->installEventFilter (this);
|
||||||
|
|
||||||
|
updateMenu ();
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectionDialog::~SelectionDialog()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelectionDialog::updateMenu()
|
||||||
|
{
|
||||||
|
Q_CHECK_PTR (languageMenu_);
|
||||||
|
languageMenu_->clear ();
|
||||||
|
QStringList languages = dictionary_.availableOcrLanguagesUi ();
|
||||||
|
if (languages.isEmpty ())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int max = 10;
|
||||||
|
|
||||||
|
if (languages.size () <= max)
|
||||||
|
{
|
||||||
|
foreach (const QString& language, languages)
|
||||||
|
{
|
||||||
|
languageMenu_->addAction (language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int subIndex = max;
|
||||||
|
QMenu* subMenu = NULL;
|
||||||
|
QString prevLetter;
|
||||||
|
foreach (const QString& language, languages)
|
||||||
|
{
|
||||||
|
QString curLetter = language.left (1);
|
||||||
|
if (++subIndex >= max && prevLetter != curLetter)
|
||||||
|
{
|
||||||
|
if (subMenu != NULL)
|
||||||
|
{
|
||||||
|
subMenu->setTitle (subMenu->title () + " - " + prevLetter);
|
||||||
|
}
|
||||||
|
subMenu = languageMenu_->addMenu (curLetter);
|
||||||
|
subIndex = 0;
|
||||||
|
}
|
||||||
|
prevLetter = curLetter;
|
||||||
|
subMenu->addAction (language);
|
||||||
|
}
|
||||||
|
subMenu->setTitle (subMenu->title () + " - " + prevLetter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SelectionDialog::eventFilter(QObject* object, QEvent* event)
|
||||||
|
{
|
||||||
|
if (object != ui->label)
|
||||||
|
{
|
||||||
|
return QDialog::eventFilter (object, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->type () == QEvent::Show)
|
||||||
|
{
|
||||||
|
startSelectPos_ = currentSelectPos_ = QPoint ();
|
||||||
|
}
|
||||||
|
else if (event->type () == QEvent::MouseButtonPress)
|
||||||
|
{
|
||||||
|
QMouseEvent* mouseEvent = static_cast <QMouseEvent*> (event);
|
||||||
|
if ((mouseEvent->button () == Qt::LeftButton ||
|
||||||
|
mouseEvent->button () == Qt::RightButton) && startSelectPos_.isNull ())
|
||||||
|
{
|
||||||
|
startSelectPos_ = mouseEvent->pos ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event->type () == QEvent::MouseMove)
|
||||||
|
{
|
||||||
|
QMouseEvent* mouseEvent = static_cast <QMouseEvent*> (event);
|
||||||
|
if ((mouseEvent->buttons () & Qt::LeftButton ||
|
||||||
|
mouseEvent->buttons () & Qt::RightButton) && !startSelectPos_.isNull ())
|
||||||
|
{
|
||||||
|
currentSelectPos_ = mouseEvent->pos ();
|
||||||
|
ui->label->repaint ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event->type () == QEvent::Paint)
|
||||||
|
{
|
||||||
|
QRect selection = QRect (startSelectPos_, currentSelectPos_).normalized ();
|
||||||
|
if (selection.isValid ())
|
||||||
|
{
|
||||||
|
QPainter painter (ui->label);
|
||||||
|
painter.setPen (Qt::red);
|
||||||
|
painter.drawRect (selection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event->type () == QEvent::MouseButtonRelease)
|
||||||
|
{
|
||||||
|
QMouseEvent* mouseEvent = static_cast <QMouseEvent*> (event);
|
||||||
|
if (mouseEvent->button () == Qt::LeftButton ||
|
||||||
|
mouseEvent->button () == Qt::RightButton)
|
||||||
|
{
|
||||||
|
if (startSelectPos_.isNull () || currentPixmap_.isNull ())
|
||||||
|
{
|
||||||
|
return QDialog::eventFilter (object, event);
|
||||||
|
}
|
||||||
|
QPoint endPos = mouseEvent->pos ();
|
||||||
|
QRect selection = QRect (startSelectPos_, endPos).normalized ();
|
||||||
|
QPixmap selectedPixmap = currentPixmap_.copy (selection);
|
||||||
|
if (!selectedPixmap.isNull ())
|
||||||
|
{
|
||||||
|
ProcessingItem item;
|
||||||
|
item.source = selectedPixmap;
|
||||||
|
item.screenPos = selection.topLeft ();
|
||||||
|
|
||||||
|
if (mouseEvent->button () == Qt::RightButton &&
|
||||||
|
!languageMenu_->children ().isEmpty ())
|
||||||
|
{
|
||||||
|
QAction* action = languageMenu_->exec (QCursor::pos ());
|
||||||
|
if (action == NULL)
|
||||||
|
{
|
||||||
|
reject ();
|
||||||
|
return QDialog::eventFilter (object, event);
|
||||||
|
}
|
||||||
|
item.ocrLanguage = dictionary_.ocrUiToCode (action->text ());
|
||||||
|
ST_ASSERT (!item.ocrLanguage.isEmpty ());
|
||||||
|
item.sourceLanguage = dictionary_.translateForOcrCode (item.ocrLanguage);
|
||||||
|
ST_ASSERT (!item.sourceLanguage.isEmpty ());
|
||||||
|
}
|
||||||
|
emit selected (item);
|
||||||
|
accept ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QDialog::eventFilter (object, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelectionDialog::setPixmap(QPixmap pixmap)
|
||||||
|
{
|
||||||
|
ST_ASSERT (!pixmap.isNull ());
|
||||||
|
currentPixmap_ = pixmap;
|
||||||
|
QPalette palette = this->palette ();
|
||||||
|
palette.setBrush (this->backgroundRole (), pixmap);
|
||||||
|
this->setPalette (palette);
|
||||||
|
this->resize (pixmap.size ());
|
||||||
|
|
||||||
|
show ();
|
||||||
|
setFocus ();
|
||||||
|
}
|
41
SelectionDialog.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#ifndef SELECTIONDIALOG_H
|
||||||
|
#define SELECTIONDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
|
#include "ProcessingItem.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class SelectionDialog;
|
||||||
|
}
|
||||||
|
class LanguageHelper;
|
||||||
|
|
||||||
|
class SelectionDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SelectionDialog(const LanguageHelper& dictionary, QWidget *parent = 0);
|
||||||
|
~SelectionDialog();
|
||||||
|
|
||||||
|
bool eventFilter (QObject *object, QEvent *event);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void selected (ProcessingItem pixmap);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setPixmap (QPixmap pixmap);
|
||||||
|
void updateMenu ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::SelectionDialog *ui;
|
||||||
|
const LanguageHelper& dictionary_;
|
||||||
|
QPoint startSelectPos_;
|
||||||
|
QPoint currentSelectPos_;
|
||||||
|
QPixmap currentPixmap_;
|
||||||
|
QMenu* languageMenu_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SELECTIONDIALOG_H
|
43
SelectionDialog.ui
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>SelectionDialog</class>
|
||||||
|
<widget class="QDialog" name="SelectionDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="cursor">
|
||||||
|
<cursorShape>CrossCursor</cursorShape>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
50
Settings.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#ifndef SETTINGS_H
|
||||||
|
#define SETTINGS_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace settings_names
|
||||||
|
{
|
||||||
|
//! UI
|
||||||
|
const QString guiGroup = "GUI";
|
||||||
|
const QString geometry = "geometry";
|
||||||
|
const QString captureHotkey = "captureHotkey";
|
||||||
|
const QString repeatHotkey = "repeatHotkey";
|
||||||
|
const QString clipboardHotkey = "clipboardHotkey";
|
||||||
|
const QString resultShowType = "resultShowType";
|
||||||
|
|
||||||
|
//! Recognition
|
||||||
|
const QString recogntionGroup = "Recognition";
|
||||||
|
const QString tessDataPlace = "tessdata_dir";
|
||||||
|
const QString ocrLanguage = "language";
|
||||||
|
const QString imageScale = "image_scale";
|
||||||
|
|
||||||
|
//! Translation
|
||||||
|
const QString translationGroup = "Translation";
|
||||||
|
const QString sourceLanguage = "source_language";
|
||||||
|
const QString translationLanguage = "translation_language";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace settings_values
|
||||||
|
{
|
||||||
|
const QString appName = "ScreenTranslator";
|
||||||
|
const QString companyName = "Gres";
|
||||||
|
|
||||||
|
//! UI
|
||||||
|
const QString captureHotkey = "Ctrl+Alt+Z";
|
||||||
|
const QString repeatHotkey = "Ctrl+Alt+X";
|
||||||
|
const QString clipboardHotkey = "Ctrl+Alt+C";
|
||||||
|
const QString resultShowType = "1";//dialog
|
||||||
|
|
||||||
|
//! Recognition
|
||||||
|
const QString tessDataPlace = "./";
|
||||||
|
const QString ocrLanguage = "eng";
|
||||||
|
const int imageScale = 5;
|
||||||
|
|
||||||
|
//! Translation
|
||||||
|
const QString sourceLanguage = "auto";
|
||||||
|
const QString translationLanguage = "ru";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SETTINGS_H
|
133
SettingsEditor.cpp
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#include "SettingsEditor.h"
|
||||||
|
#include "ui_SettingsEditor.h"
|
||||||
|
#include "LanguageHelper.h"
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
|
#include "Settings.h"
|
||||||
|
|
||||||
|
SettingsEditor::SettingsEditor(const LanguageHelper &dictionary, QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::SettingsEditor), dictionary_ (dictionary),
|
||||||
|
buttonGroup_ (new QButtonGroup (this))
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
buttonGroup_->addButton (ui->trayRadio, 0);
|
||||||
|
buttonGroup_->addButton (ui->dialogRadio, 1);
|
||||||
|
|
||||||
|
connect (ui->tessdataButton, SIGNAL (clicked ()), SLOT (openTessdataDialog ()));
|
||||||
|
connect (ui->tessdataEdit, SIGNAL (textChanged (const QString&)),
|
||||||
|
SLOT (initOcrLangCombo (const QString&)));
|
||||||
|
|
||||||
|
ui->translateLangCombo->addItems (dictionary_.translateLanguagesUi ());
|
||||||
|
loadSettings ();
|
||||||
|
loadState ();
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsEditor::~SettingsEditor()
|
||||||
|
{
|
||||||
|
saveState ();
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsEditor::done(int result)
|
||||||
|
{
|
||||||
|
if (result == QDialog::Accepted)
|
||||||
|
{
|
||||||
|
saveSettings ();
|
||||||
|
emit settingsEdited ();
|
||||||
|
}
|
||||||
|
QDialog::done (result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsEditor::saveSettings() const
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup (settings_names::guiGroup);
|
||||||
|
settings.setValue (settings_names::captureHotkey, ui->captureEdit->text ());
|
||||||
|
settings.setValue (settings_names::repeatHotkey, ui->repeatEdit->text ());
|
||||||
|
settings.setValue (settings_names::clipboardHotkey, ui->clipboardEdit->text ());
|
||||||
|
settings.setValue (settings_names::resultShowType, buttonGroup_->checkedId ());
|
||||||
|
settings.endGroup ();
|
||||||
|
|
||||||
|
|
||||||
|
settings.beginGroup (settings_names::recogntionGroup);
|
||||||
|
settings.setValue (settings_names::tessDataPlace, ui->tessdataEdit->text ());
|
||||||
|
QString ocrLanguage = dictionary_.ocrUiToCode (ui->ocrLangCombo->currentText ());
|
||||||
|
settings.setValue (settings_names::ocrLanguage, ocrLanguage);
|
||||||
|
settings.setValue (settings_names::imageScale, ui->imageScaleSpin->value ());
|
||||||
|
settings.endGroup ();
|
||||||
|
|
||||||
|
|
||||||
|
settings.beginGroup (settings_names::translationGroup);
|
||||||
|
QString trLanguage = dictionary_.translateUiToCode (ui->translateLangCombo->currentText ());
|
||||||
|
settings.setValue (settings_names::translationLanguage, trLanguage);
|
||||||
|
QString sourceLanguage = dictionary_.translateForOcrCode (ocrLanguage);
|
||||||
|
settings.setValue (settings_names::sourceLanguage, sourceLanguage);
|
||||||
|
settings.endGroup ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsEditor::openTessdataDialog()
|
||||||
|
{
|
||||||
|
QString path = QFileDialog::getExistingDirectory (this, tr ("Путь к tessdata"));
|
||||||
|
if (path.isEmpty ())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QDir dir (path);
|
||||||
|
if (dir.dirName () == QString ("tessdata"))
|
||||||
|
{
|
||||||
|
dir.cdUp ();
|
||||||
|
}
|
||||||
|
ui->tessdataEdit->setText (dir.path ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsEditor::loadSettings()
|
||||||
|
{
|
||||||
|
#define GET(FIELD) settings.value (settings_names::FIELD, settings_values::FIELD)
|
||||||
|
QSettings settings;
|
||||||
|
|
||||||
|
settings.beginGroup (settings_names::guiGroup);
|
||||||
|
ui->captureEdit->setText (GET(captureHotkey).toString ());
|
||||||
|
ui->repeatEdit->setText (GET(repeatHotkey).toString ());
|
||||||
|
ui->clipboardEdit->setText (GET(clipboardHotkey).toString ());
|
||||||
|
QAbstractButton* button = buttonGroup_->button (GET(resultShowType).toInt ());
|
||||||
|
Q_CHECK_PTR (button);
|
||||||
|
button->setChecked (true);
|
||||||
|
settings.endGroup ();
|
||||||
|
|
||||||
|
settings.beginGroup (settings_names::recogntionGroup);
|
||||||
|
ui->tessdataEdit->setText (GET(tessDataPlace).toString ());
|
||||||
|
QString ocrLanguage = dictionary_.ocrCodeToUi (GET(ocrLanguage).toString ());
|
||||||
|
ui->ocrLangCombo->setCurrentText (ocrLanguage);
|
||||||
|
ui->imageScaleSpin->setValue (GET(imageScale).toInt ());
|
||||||
|
settings.endGroup ();
|
||||||
|
|
||||||
|
settings.beginGroup (settings_names::translationGroup);
|
||||||
|
QString trLanguage = dictionary_.translateCodeToUi (GET(translationLanguage).toString ());
|
||||||
|
ui->translateLangCombo->setCurrentText (trLanguage);
|
||||||
|
settings.endGroup ();
|
||||||
|
#undef GET
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsEditor::saveState() const
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup (settings_names::guiGroup);
|
||||||
|
settings.setValue (objectName () + "_" + settings_names::geometry, saveGeometry ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsEditor::loadState()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup (settings_names::guiGroup);
|
||||||
|
restoreGeometry (settings.value (objectName () + "_" + settings_names::geometry).toByteArray ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsEditor::initOcrLangCombo(const QString &path)
|
||||||
|
{
|
||||||
|
ui->ocrLangCombo->clear ();
|
||||||
|
ui->ocrLangCombo->addItems (dictionary_.availableOcrLanguagesUi (path));
|
||||||
|
}
|
43
SettingsEditor.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#ifndef SETTINGSEDITOR_H
|
||||||
|
#define SETTINGSEDITOR_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QButtonGroup>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class SettingsEditor;
|
||||||
|
}
|
||||||
|
class LanguageHelper;
|
||||||
|
|
||||||
|
class SettingsEditor : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SettingsEditor(const LanguageHelper& dictionary, QWidget *parent = 0);
|
||||||
|
~SettingsEditor();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void settingsEdited ();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void done (int result);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void saveSettings () const;
|
||||||
|
void openTessdataDialog ();
|
||||||
|
void initOcrLangCombo (const QString& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loadSettings ();
|
||||||
|
void saveState () const;
|
||||||
|
void loadState ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::SettingsEditor *ui;
|
||||||
|
const LanguageHelper& dictionary_;
|
||||||
|
QButtonGroup* buttonGroup_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SETTINGSEDITOR_H
|
271
SettingsEditor.ui
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>SettingsEditor</class>
|
||||||
|
<widget class="QDialog" name="SettingsEditor">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>435</width>
|
||||||
|
<height>221</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Настройки</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_5">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Горячие клавиши</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Сочетание клавиш для перехода в режим захвата.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Захватить</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>captureEdit</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" colspan="2">
|
||||||
|
<widget class="QLineEdit" name="captureEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Сочетание клавиш для перехода в режим захвата.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Скопировать</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>captureEdit</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1" colspan="2">
|
||||||
|
<widget class="QLineEdit" name="clipboardEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Сочетание клавиш для перехода в режим захвата.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Повторить</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>captureEdit</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1" colspan="2">
|
||||||
|
<widget class="QLineEdit" name="repeatEdit"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
|
<property name="title">
|
||||||
|
<string>Распознавание</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Необходимо для распознавания.</p><p>Скачивается отсюда: <a href="https://code.google.com/p/tesseract-ocr/downloads/list"><span style=" text-decoration: underline; color:#0000ff;">https://code.google.com/p/tesseract-ocr/downloads/list</span></a></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Путь к tessdata</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>tessdataEdit</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="tessdataEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QToolButton" name="tessdataButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Заполняется на основании содержания tessdata</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Язык распознавания</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>ocrLangCombo</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Масштабирование изображения для улучшения распознания. Больше - лучше (до определенных пределов), но медленнее.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Увеличение масштаба</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>imageScaleSpin</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1" colspan="2">
|
||||||
|
<widget class="QComboBox" name="ocrLangCombo"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1" colspan="2">
|
||||||
|
<widget class="QSpinBox" name="imageScaleSpin"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QGroupBox" name="resultGroup">
|
||||||
|
<property name="title">
|
||||||
|
<string>Вывод результата</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_4">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QRadioButton" name="trayRadio">
|
||||||
|
<property name="text">
|
||||||
|
<string>Трей</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QRadioButton" name="dialogRadio">
|
||||||
|
<property name="text">
|
||||||
|
<string>Окно</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QGroupBox" name="groupBox_3">
|
||||||
|
<property name="title">
|
||||||
|
<string>Перевод</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Язык, на который осуществляется перевод.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Язык результата</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>translateLangCombo</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="translateLangCombo"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>1</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>captureEdit</tabstop>
|
||||||
|
<tabstop>translateLangCombo</tabstop>
|
||||||
|
<tabstop>tessdataEdit</tabstop>
|
||||||
|
<tabstop>tessdataButton</tabstop>
|
||||||
|
<tabstop>ocrLangCombo</tabstop>
|
||||||
|
<tabstop>imageScaleSpin</tabstop>
|
||||||
|
<tabstop>buttonBox</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>SettingsEditor</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>SettingsEditor</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
15
StAssert.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef ST_ASSERT_H
|
||||||
|
#define ST_ASSERT_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#if !defined(ST_ASSERT)
|
||||||
|
# if defined(ST_NO_ASSERT)
|
||||||
|
# define ST_ASSERT(CONDITION)
|
||||||
|
# else
|
||||||
|
# define ST_ASSERT(CONDITION) assert(CONDITION)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // ST_ASSERT_H
|
||||||
|
|
121
Translator.cpp
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#include "Translator.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonParseError>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
#include "Settings.h"
|
||||||
|
#include "GoogleWebTranslator.h"
|
||||||
|
#include "StAssert.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const QString translateBaseUrl = "http://translate.google.com/translate_a/"
|
||||||
|
"t?client=t&text=%1&sl=%2&tl=%3";
|
||||||
|
}
|
||||||
|
|
||||||
|
Translator::Translator(QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
network_ (this),
|
||||||
|
useAlternativeTranslation_ (false)
|
||||||
|
{
|
||||||
|
connect (&network_, SIGNAL (finished (QNetworkReply*)),
|
||||||
|
SLOT (replyFinished (QNetworkReply*)));
|
||||||
|
|
||||||
|
GoogleWebTranslator* googleWeb = new GoogleWebTranslator;
|
||||||
|
connect (this, SIGNAL (translateAlternative (ProcessingItem)),
|
||||||
|
googleWeb, SLOT (translate (ProcessingItem)));
|
||||||
|
connect (googleWeb, SIGNAL (translated (ProcessingItem, bool)),
|
||||||
|
this, SLOT (translatedAlternative(ProcessingItem, bool)));
|
||||||
|
connect (googleWeb, SIGNAL (error (QString)), this, SIGNAL (error (QString)));
|
||||||
|
|
||||||
|
applySettings ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Translator::applySettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup (settings_names::translationGroup);
|
||||||
|
translationLanguage_ = settings.value (settings_names::translationLanguage,
|
||||||
|
settings_values::translationLanguage).toString ();
|
||||||
|
sourceLanguage_ = settings.value (settings_names::sourceLanguage,
|
||||||
|
settings_values::sourceLanguage).toString ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Translator::translate(ProcessingItem item)
|
||||||
|
{
|
||||||
|
if (useAlternativeTranslation_) {
|
||||||
|
emit translateAlternative(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ST_ASSERT (!item.recognized.isEmpty ());
|
||||||
|
QString sourceLanguage = item.sourceLanguage.isEmpty () ? sourceLanguage_ :
|
||||||
|
item.sourceLanguage;
|
||||||
|
if (translationLanguage_.isEmpty () || sourceLanguage.isEmpty ())
|
||||||
|
{
|
||||||
|
emit error (tr ("Неверные парметры для перевода."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QUrl url (translateBaseUrl.arg (item.recognized, sourceLanguage, translationLanguage_));
|
||||||
|
QNetworkReply* reply = network_.get (QNetworkRequest (url));
|
||||||
|
items_.insert (reply, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Translator::translatedAlternative(ProcessingItem item, bool success)
|
||||||
|
{
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
emit translated(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emit error (tr ("Ошибка альтернативного перевода текста: %1").arg (item.recognized));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Translator::replyFinished(QNetworkReply* reply)
|
||||||
|
{
|
||||||
|
ST_ASSERT (items_.contains (reply));
|
||||||
|
ProcessingItem item = items_.take (reply);
|
||||||
|
ST_ASSERT (reply->isFinished ());
|
||||||
|
if (reply->error () != QNetworkReply::NoError)
|
||||||
|
{
|
||||||
|
useAlternativeTranslation_ = true;
|
||||||
|
emit translateAlternative(item);
|
||||||
|
reply->deleteLater ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QByteArray data = reply->readAll ();
|
||||||
|
reply->deleteLater ();
|
||||||
|
|
||||||
|
while (data.indexOf (",,") != -1)//make json valid
|
||||||
|
{
|
||||||
|
data.replace (",,", ",");
|
||||||
|
}
|
||||||
|
QJsonParseError parseError;
|
||||||
|
QJsonDocument document = QJsonDocument::fromJson (data, &parseError);
|
||||||
|
if (document.isEmpty ())
|
||||||
|
{
|
||||||
|
useAlternativeTranslation_ = true;
|
||||||
|
emit translateAlternative(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QJsonArray answerArray = document.array ();
|
||||||
|
QJsonArray fullTranslation = answerArray.first ().toArray ();
|
||||||
|
QString translation = "";
|
||||||
|
foreach (QJsonValue part, fullTranslation)
|
||||||
|
{
|
||||||
|
QJsonArray partTranslation = part.toArray ();
|
||||||
|
if (partTranslation.isEmpty ())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
translation += partTranslation.at (0).toString ();
|
||||||
|
}
|
||||||
|
item.translated = translation;
|
||||||
|
emit translated (item);
|
||||||
|
}
|
36
Translator.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef TRANSLATOR_H
|
||||||
|
#define TRANSLATOR_H
|
||||||
|
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
|
||||||
|
#include "ProcessingItem.h"
|
||||||
|
|
||||||
|
class Translator : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Translator(QObject *parent = 0);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void translated (ProcessingItem item);
|
||||||
|
void translateAlternative (ProcessingItem item);
|
||||||
|
void error (QString text);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void translate (ProcessingItem item);
|
||||||
|
void translatedAlternative (ProcessingItem item, bool success);
|
||||||
|
void applySettings ();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void replyFinished (QNetworkReply* reply);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager network_;
|
||||||
|
QString translationLanguage_;
|
||||||
|
QString sourceLanguage_;
|
||||||
|
QHash<QNetworkReply*, ProcessingItem> items_;
|
||||||
|
bool useAlternativeTranslation_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TRANSLATOR_H
|
BIN
distr/Files.xlsx
Normal file
269
distr/InnoSetup.iss
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
; Script generated by the Inno Setup Script Wizard.
|
||||||
|
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||||
|
|
||||||
|
#define MyAppName "Screen Translator"
|
||||||
|
#define MyAppVersion "1.2.3"
|
||||||
|
#define MyAppPublisher "Gres"
|
||||||
|
#define MyAppURL "http://gres.biz/screen-translator/"
|
||||||
|
#define MyAppExeName "ScreenTranslator.exe"
|
||||||
|
#define MyAppDescription "Screen capture and translation tool"
|
||||||
|
|
||||||
|
[Setup]
|
||||||
|
; NOTE: The value of AppId uniquely identifies this application.
|
||||||
|
; Do not use the same AppId value in installers for other applications.
|
||||||
|
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||||
|
AppId={{016F399E-0EED-476C-AB00-8AD0FF5BFD77}
|
||||||
|
AppName={#MyAppName}
|
||||||
|
AppVersion={#MyAppVersion}
|
||||||
|
;AppVerName={#MyAppName} {#MyAppVersion}
|
||||||
|
AppPublisher={#MyAppPublisher}
|
||||||
|
AppPublisherURL={#MyAppURL}
|
||||||
|
AppSupportURL={#MyAppURL}
|
||||||
|
AppUpdatesURL={#MyAppURL}
|
||||||
|
DefaultDirName={pf}\{#MyAppName}
|
||||||
|
DefaultGroupName={#MyAppName}
|
||||||
|
AllowNoIcons=yes
|
||||||
|
PrivilegesRequired=admin
|
||||||
|
CloseApplications=yes
|
||||||
|
OutputDir=.
|
||||||
|
OutputBaseFilename=ScreenTranslator-{#MyAppVersion}
|
||||||
|
SetupIconFile=..\images\icon.ico
|
||||||
|
SolidCompression=yes
|
||||||
|
RestartIfNeededByRun=False
|
||||||
|
ShowLanguageDialog=auto
|
||||||
|
VersionInfoCompany={#MyAppPublisher}
|
||||||
|
VersionInfoDescription={#MyAppDescription}
|
||||||
|
VersionInfoProductName={#MyAppName}
|
||||||
|
VersionInfoProductVersion={#MyAppVersion}
|
||||||
|
VersionInfoVersion={#MyAppVersion}
|
||||||
|
Compression=lzma2/ultra64
|
||||||
|
InternalCompressLevel=max
|
||||||
|
|
||||||
|
[Languages]
|
||||||
|
Name: "english"; MessagesFile: "compiler:Default.isl"; InfoBeforeFile: "eng\Changelog.txt"
|
||||||
|
Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl,ru\Russian.isl"; InfoBeforeFile: "ru\Changelog.txt"
|
||||||
|
|
||||||
|
[Tasks]
|
||||||
|
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||||
|
Name: "startupicon"; Description: "{cm:CreateStartupIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||||
|
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
|
||||||
|
|
||||||
|
[Icons]
|
||||||
|
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||||
|
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
|
||||||
|
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||||
|
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon
|
||||||
|
Name: "{commonstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: startupicon
|
||||||
|
|
||||||
|
[Run]
|
||||||
|
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
||||||
|
|
||||||
|
[Files]
|
||||||
|
Source: "content\ScreenTranslator.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: Executable
|
||||||
|
|
||||||
|
Source: "content\*.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: Libraries
|
||||||
|
Source: "content\platforms\*"; DestDir: "{app}\platforms"; Flags: ignoreversion; Components: Libraries
|
||||||
|
|
||||||
|
Source: "content\tessdata\afr.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Afrikaans
|
||||||
|
Source: "content\tessdata\sqi.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Albanian
|
||||||
|
Source: "content\tessdata\grc.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\AncientGreek
|
||||||
|
Source: "content\tessdata\ara.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Arabic
|
||||||
|
Source: "content\tessdata\aze.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Azerbaijani
|
||||||
|
Source: "content\tessdata\eus.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Basque
|
||||||
|
Source: "content\tessdata\bel.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Belarusian
|
||||||
|
Source: "content\tessdata\ben.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Bengali
|
||||||
|
Source: "content\tessdata\bul.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Bulgarian
|
||||||
|
Source: "content\tessdata\cat.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Catalan
|
||||||
|
Source: "content\tessdata\chr.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Cherokee
|
||||||
|
Source: "content\tessdata\chi_sim.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\ChineseSimplified
|
||||||
|
Source: "content\tessdata\chi_tra.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\ChineseTraditional
|
||||||
|
Source: "content\tessdata\hrv.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Croatian
|
||||||
|
Source: "content\tessdata\ces.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Czech
|
||||||
|
Source: "content\tessdata\dan.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Danish
|
||||||
|
Source: "content\tessdata\nld.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Dutch
|
||||||
|
Source: "content\tessdata\eng.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\English
|
||||||
|
Source: "content\tessdata\epo.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Esperanto
|
||||||
|
Source: "content\tessdata\epo_alt.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Esperantoalternative
|
||||||
|
Source: "content\tessdata\est.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Estonian
|
||||||
|
Source: "content\tessdata\fin.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Finnish
|
||||||
|
Source: "content\tessdata\frk.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Frankish
|
||||||
|
Source: "content\tessdata\fra.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\French
|
||||||
|
Source: "content\tessdata\glg.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Galician
|
||||||
|
Source: "content\tessdata\deu.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\German
|
||||||
|
Source: "content\tessdata\ell.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Greek
|
||||||
|
Source: "content\tessdata\heb.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Hebrew
|
||||||
|
Source: "content\tessdata\hin.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Hindi
|
||||||
|
Source: "content\tessdata\hun.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Hungarian
|
||||||
|
Source: "content\tessdata\isl.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Icelandic
|
||||||
|
Source: "content\tessdata\ind.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Indonesian
|
||||||
|
Source: "content\tessdata\ita.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Italian
|
||||||
|
Source: "content\tessdata\jpn.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Japanese
|
||||||
|
Source: "content\tessdata\kan.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Kannada
|
||||||
|
Source: "content\tessdata\kor.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Korean
|
||||||
|
Source: "content\tessdata\lav.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Latvian
|
||||||
|
Source: "content\tessdata\lit.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Lithuanian
|
||||||
|
Source: "content\tessdata\mkd.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Macedonian
|
||||||
|
Source: "content\tessdata\msa.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Malay
|
||||||
|
Source: "content\tessdata\mal.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Malayalam
|
||||||
|
Source: "content\tessdata\mlt.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Maltese
|
||||||
|
Source: "content\tessdata\equ.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\MathEquation
|
||||||
|
Source: "content\tessdata\enm.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\MiddleEnglish
|
||||||
|
Source: "content\tessdata\frm.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\MiddleFrench
|
||||||
|
Source: "content\tessdata\nor.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Norwegian
|
||||||
|
Source: "content\tessdata\pol.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Polish
|
||||||
|
Source: "content\tessdata\por.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Portuguese
|
||||||
|
Source: "content\tessdata\ron.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Romanian
|
||||||
|
Source: "content\tessdata\rus.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Russian
|
||||||
|
Source: "content\tessdata\srp.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Serbian
|
||||||
|
Source: "content\tessdata\slk.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Slovakian
|
||||||
|
Source: "content\tessdata\slv.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Slovenian
|
||||||
|
Source: "content\tessdata\spa.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Spanish
|
||||||
|
Source: "content\tessdata\swa.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Swahili
|
||||||
|
Source: "content\tessdata\swe.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Swedish
|
||||||
|
Source: "content\tessdata\tgl.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Tagalog
|
||||||
|
Source: "content\tessdata\tam.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Tamil
|
||||||
|
Source: "content\tessdata\tel.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Telugu
|
||||||
|
Source: "content\tessdata\tha.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Thai
|
||||||
|
Source: "content\tessdata\tur.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Turkish
|
||||||
|
Source: "content\tessdata\ukr.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Ukrainian
|
||||||
|
Source: "content\tessdata\vie.*"; DestDir: "{app}\tessdata"; Flags: ignoreversion; Components: Languages\Vietnamese
|
||||||
|
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||||
|
|
||||||
|
[CustomMessages]
|
||||||
|
english.CreateStartupIcon=Create autostart icon
|
||||||
|
english.Executables=Executables
|
||||||
|
english.Libraries=Libraries
|
||||||
|
english.Languages=OCR Languages
|
||||||
|
|
||||||
|
english.Afrikaans=Afrikaans
|
||||||
|
english.Albanian=Albanian
|
||||||
|
english.AncientGreek=AncientGreek
|
||||||
|
english.Arabic=Arabic
|
||||||
|
english.Azerbaijani=Azerbaijani
|
||||||
|
english.Basque=Basque
|
||||||
|
english.Belarusian=Belarusian
|
||||||
|
english.Bengali=Bengali
|
||||||
|
english.Bulgarian=Bulgarian
|
||||||
|
english.Catalan=Catalan
|
||||||
|
english.Cherokee=Cherokee
|
||||||
|
english.ChineseSimplified=ChineseSimplified
|
||||||
|
english.ChineseTraditional=ChineseTraditional
|
||||||
|
english.Croatian=Croatian
|
||||||
|
english.Czech=Czech
|
||||||
|
english.Danish=Danish
|
||||||
|
english.Dutch=Dutch
|
||||||
|
english.English=English
|
||||||
|
english.Esperanto=Esperanto
|
||||||
|
english.Esperantoalternative=Esperantoalternative
|
||||||
|
english.Estonian=Estonian
|
||||||
|
english.Finnish=Finnish
|
||||||
|
english.Frankish=Frankish
|
||||||
|
english.French=French
|
||||||
|
english.Galician=Galician
|
||||||
|
english.German=German
|
||||||
|
english.Greek=Greek
|
||||||
|
english.Hebrew=Hebrew
|
||||||
|
english.Hindi=Hindi
|
||||||
|
english.Hungarian=Hungarian
|
||||||
|
english.Icelandic=Icelandic
|
||||||
|
english.Indonesian=Indonesian
|
||||||
|
english.Italian=Italian
|
||||||
|
english.Japanese=Japanese
|
||||||
|
english.Kannada=Kannada
|
||||||
|
english.Korean=Korean
|
||||||
|
english.Latvian=Latvian
|
||||||
|
english.Lithuanian=Lithuanian
|
||||||
|
english.Macedonian=Macedonian
|
||||||
|
english.Malay=Malay
|
||||||
|
english.Malayalam=Malayalam
|
||||||
|
english.Maltese=Maltese
|
||||||
|
english.MathEquation=MathEquation
|
||||||
|
english.MiddleEnglish=MiddleEnglish
|
||||||
|
english.MiddleFrench=MiddleFrench
|
||||||
|
english.Norwegian=Norwegian
|
||||||
|
english.Polish=Polish
|
||||||
|
english.Portuguese=Portuguese
|
||||||
|
english.Romanian=Romanian
|
||||||
|
english.Russian=Russian
|
||||||
|
english.Serbian=Serbian
|
||||||
|
english.Slovakian=Slovakian
|
||||||
|
english.Slovenian=Slovenian
|
||||||
|
english.Spanish=Spanish
|
||||||
|
english.Swahili=Swahili
|
||||||
|
english.Swedish=Swedish
|
||||||
|
english.Tagalog=Tagalog
|
||||||
|
english.Tamil=Tamil
|
||||||
|
english.Telugu=Telugu
|
||||||
|
english.Thai=Thai
|
||||||
|
english.Turkish=Turkish
|
||||||
|
english.Ukrainian=Ukrainian
|
||||||
|
english.Vietnamese=Vietnamese
|
||||||
|
|
||||||
|
[Components]
|
||||||
|
Name: "Executable"; Description: "{cm:Executables}"; Types: compact custom full; Flags: fixed;
|
||||||
|
Name: "Libraries"; Description: "{cm:Libraries}"; Types: compact custom full; Flags: fixed;
|
||||||
|
Name: "Languages"; Description: "{cm:Languages}"; Types: custom full
|
||||||
|
|
||||||
|
Name: "Languages\Afrikaans"; Description: "{cm:Afrikaans}"; Types: full
|
||||||
|
Name: "Languages\Albanian"; Description: "{cm:Albanian}"; Types: full
|
||||||
|
Name: "Languages\AncientGreek"; Description: "{cm:AncientGreek}"; Types: full
|
||||||
|
Name: "Languages\Arabic"; Description: "{cm:Arabic}"; Types: full
|
||||||
|
Name: "Languages\Azerbaijani"; Description: "{cm:Azerbaijani}"; Types: full
|
||||||
|
Name: "Languages\Basque"; Description: "{cm:Basque}"; Types: full
|
||||||
|
Name: "Languages\Belarusian"; Description: "{cm:Belarusian}"; Types: full
|
||||||
|
Name: "Languages\Bengali"; Description: "{cm:Bengali}"; Types: full
|
||||||
|
Name: "Languages\Bulgarian"; Description: "{cm:Bulgarian}"; Types: full
|
||||||
|
Name: "Languages\Catalan"; Description: "{cm:Catalan}"; Types: full
|
||||||
|
Name: "Languages\Cherokee"; Description: "{cm:Cherokee}"; Types: full
|
||||||
|
Name: "Languages\ChineseSimplified"; Description: "{cm:ChineseSimplified}"; Types: full
|
||||||
|
Name: "Languages\ChineseTraditional"; Description: "{cm:ChineseTraditional}"; Types: compact custom full
|
||||||
|
Name: "Languages\Croatian"; Description: "{cm:Croatian}"; Types: full
|
||||||
|
Name: "Languages\Czech"; Description: "{cm:Czech}"; Types: full
|
||||||
|
Name: "Languages\Danish"; Description: "{cm:Danish}"; Types: full
|
||||||
|
Name: "Languages\Dutch"; Description: "{cm:Dutch}"; Types: full
|
||||||
|
Name: "Languages\English"; Description: "{cm:English}"; Types: compact custom full
|
||||||
|
Name: "Languages\Esperanto"; Description: "{cm:Esperanto}"; Types: full
|
||||||
|
Name: "Languages\Esperantoalternative"; Description: "{cm:Esperantoalternative}"; Types: full
|
||||||
|
Name: "Languages\Estonian"; Description: "{cm:Estonian}"; Types: full
|
||||||
|
Name: "Languages\Finnish"; Description: "{cm:Finnish}"; Types: full
|
||||||
|
Name: "Languages\Frankish"; Description: "{cm:Frankish}"; Types: full
|
||||||
|
Name: "Languages\French"; Description: "{cm:French}"; Types: compact custom full
|
||||||
|
Name: "Languages\Galician"; Description: "{cm:Galician}"; Types: full
|
||||||
|
Name: "Languages\German"; Description: "{cm:German}"; Types: compact custom full
|
||||||
|
Name: "Languages\Greek"; Description: "{cm:Greek}"; Types: full
|
||||||
|
Name: "Languages\Hebrew"; Description: "{cm:Hebrew}"; Types: full
|
||||||
|
Name: "Languages\Hindi"; Description: "{cm:Hindi}"; Types: full
|
||||||
|
Name: "Languages\Hungarian"; Description: "{cm:Hungarian}"; Types: full
|
||||||
|
Name: "Languages\Icelandic"; Description: "{cm:Icelandic}"; Types: full
|
||||||
|
Name: "Languages\Indonesian"; Description: "{cm:Indonesian}"; Types: full
|
||||||
|
Name: "Languages\Italian"; Description: "{cm:Italian}"; Types: full
|
||||||
|
Name: "Languages\Japanese"; Description: "{cm:Japanese}"; Types: compact custom full
|
||||||
|
Name: "Languages\Kannada"; Description: "{cm:Kannada}"; Types: full
|
||||||
|
Name: "Languages\Korean"; Description: "{cm:Korean}"; Types: compact custom full
|
||||||
|
Name: "Languages\Latvian"; Description: "{cm:Latvian}"; Types: full
|
||||||
|
Name: "Languages\Lithuanian"; Description: "{cm:Lithuanian}"; Types: full
|
||||||
|
Name: "Languages\Macedonian"; Description: "{cm:Macedonian}"; Types: full
|
||||||
|
Name: "Languages\Malay"; Description: "{cm:Malay}"; Types: full
|
||||||
|
Name: "Languages\Malayalam"; Description: "{cm:Malayalam}"; Types: full
|
||||||
|
Name: "Languages\Maltese"; Description: "{cm:Maltese}"; Types: full
|
||||||
|
Name: "Languages\MathEquation"; Description: "{cm:MathEquation}"; Types: compact custom full
|
||||||
|
Name: "Languages\MiddleEnglish"; Description: "{cm:MiddleEnglish}"; Types: full
|
||||||
|
Name: "Languages\MiddleFrench"; Description: "{cm:MiddleFrench}"; Types: full
|
||||||
|
Name: "Languages\Norwegian"; Description: "{cm:Norwegian}"; Types: full
|
||||||
|
Name: "Languages\Polish"; Description: "{cm:Polish}"; Types: full
|
||||||
|
Name: "Languages\Portuguese"; Description: "{cm:Portuguese}"; Types: full
|
||||||
|
Name: "Languages\Romanian"; Description: "{cm:Romanian}"; Types: full
|
||||||
|
Name: "Languages\Russian"; Description: "{cm:Russian}"; Types: compact custom full
|
||||||
|
Name: "Languages\Serbian"; Description: "{cm:Serbian}"; Types: full
|
||||||
|
Name: "Languages\Slovakian"; Description: "{cm:Slovakian}"; Types: full
|
||||||
|
Name: "Languages\Slovenian"; Description: "{cm:Slovenian}"; Types: full
|
||||||
|
Name: "Languages\Spanish"; Description: "{cm:Spanish}"; Types: compact custom full
|
||||||
|
Name: "Languages\Swahili"; Description: "{cm:Swahili}"; Types: full
|
||||||
|
Name: "Languages\Swedish"; Description: "{cm:Swedish}"; Types: full
|
||||||
|
Name: "Languages\Tagalog"; Description: "{cm:Tagalog}"; Types: full
|
||||||
|
Name: "Languages\Tamil"; Description: "{cm:Tamil}"; Types: full
|
||||||
|
Name: "Languages\Telugu"; Description: "{cm:Telugu}"; Types: full
|
||||||
|
Name: "Languages\Thai"; Description: "{cm:Thai}"; Types: full
|
||||||
|
Name: "Languages\Turkish"; Description: "{cm:Turkish}"; Types: full
|
||||||
|
Name: "Languages\Ukrainian"; Description: "{cm:Ukrainian}"; Types: full
|
||||||
|
Name: "Languages\Vietnamese"; Description: "{cm:Vietnamese}"; Types: full
|
26
distr/eng/Changelog.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Changes.
|
||||||
|
1.2.3:
|
||||||
|
* Fixed possible crash.
|
||||||
|
* Added version information and some error messages.
|
||||||
|
1.2.2:
|
||||||
|
* Added alternative translation source.
|
||||||
|
1.2.1:
|
||||||
|
* Fixed the bug with the lack of translation.
|
||||||
|
* Fixed the bug with the use of language recognition by default when you select another one in OCR region selection mode.
|
||||||
|
1.2.0:
|
||||||
|
+ Changed installer.
|
||||||
|
+ Added all available languages for recognition.
|
||||||
|
+ Added ability to specify language when selecting the field of recognition using right click.
|
||||||
|
+ Human readable language names.
|
||||||
|
* Reduced memory usage.
|
||||||
|
* Updated libraries.
|
||||||
|
1.1.3:
|
||||||
|
* Added library libgcc_s_dw2-1.dll.
|
||||||
|
* Updated libraries.
|
||||||
|
1.1.2:
|
||||||
|
* If you specify in the settings the path to tessdata characters "\" or "/" at the end of the path are no longer required.
|
||||||
|
1.1.1:
|
||||||
|
* Fixed an issue with incorrect window size when display results.
|
||||||
|
1.1.0:
|
||||||
|
+ Displays the result in the window, along with the picture.
|
||||||
|
+ Context menu expanded. Added buttons display the last result and copy it to the clipboard.
|
26
distr/ru/Changelog.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Изменения.
|
||||||
|
1.2.3:
|
||||||
|
* Устранена возможная причина падения.
|
||||||
|
* Добавлена информация о версии и некоторые сообщения об ошибках.
|
||||||
|
1.2.2:
|
||||||
|
* Добавлен альтернативный источник перевода.
|
||||||
|
1.2.1:
|
||||||
|
* Устранена ошибка отсутствия перевода.
|
||||||
|
* Устранена ошибка использования языка распознавания по умолчанию при выборе другого в окне выделения области распознавания.
|
||||||
|
1.2.0:
|
||||||
|
+ Изменен установщик.
|
||||||
|
+ В установщик добавлены все доступные языки для распознавания.
|
||||||
|
+ Добавлена возможность указания языка при выборе области распознавания при помощи выделения с правым кликом.
|
||||||
|
+ Человекочитаемые названия языков.
|
||||||
|
* Уменьшено потребление памяти.
|
||||||
|
* Обновлены библиотеки.
|
||||||
|
1.1.3:
|
||||||
|
- В установщик добавлена библиотека libgcc_s_dw2-1.dll.
|
||||||
|
- Обновлены библиотеки.
|
||||||
|
1.1.2:
|
||||||
|
- При задании в настройках пути к tessdata символы «\» или «/» в конце пути теперь не обязательны.
|
||||||
|
1.1.1:
|
||||||
|
- Пофиксен баг с неверным размером окна отображения результатов.
|
||||||
|
1.1.0:
|
||||||
|
- Отображение результата в окошке, вместе с картинкой.
|
||||||
|
- Контекстное меню расширено. Добавлены кнопки отображения последнего результата и копирования его в буфер обмена.
|
78
distr/ru/Russian.isl
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
[LangOptions]
|
||||||
|
LanguageName=<0420><0443><0441><0441><043A><0438><0439>
|
||||||
|
LanguageID=$0419
|
||||||
|
LanguageCodePage=1251
|
||||||
|
|
||||||
|
[CustomMessages]
|
||||||
|
|
||||||
|
; *** Components
|
||||||
|
CreateStartupIcon=Äîáàâèòü â àâòîçàïóñê
|
||||||
|
Executables=Èñïîëíÿåìûå ôàéëû
|
||||||
|
Libraries=Áèáëèîòåêè
|
||||||
|
Languages=ßçûêè
|
||||||
|
|
||||||
|
AncientGreek=Äðåâíåãðå÷åñêèé
|
||||||
|
Esperantoalternative=Ýñïåðàíòî àëüòåðíàòèâíûé
|
||||||
|
English=Àíãëèéñêèé
|
||||||
|
Ukrainian=Óêðàèíñêèé
|
||||||
|
Turkish=Òóðåöêèé
|
||||||
|
Thai=Òàéñêèé
|
||||||
|
Tagalog=Òàãàëüñêèé
|
||||||
|
Telugu=Òåëóãó
|
||||||
|
Tamil=Òàìèëüñêèé
|
||||||
|
Swedish=Øâåäñêèé
|
||||||
|
Swahili=Ñóàõèëè
|
||||||
|
Serbian=Ñåðáñêèé
|
||||||
|
Albanian=Àëáàíñêèé
|
||||||
|
Spanish=Èñïàíñêèé
|
||||||
|
Slovenian=Ñëîâåíñêèé
|
||||||
|
Slovakian=Ñëîâàöêèé
|
||||||
|
Romanian=Ðóìûíñêèé
|
||||||
|
Portuguese=Ïîðòóãàëüñêèé
|
||||||
|
Polish=Ïîëüñêèé
|
||||||
|
Norwegian=Íîðâåæñêèé
|
||||||
|
Dutch=Ãîëëàíäñêèé
|
||||||
|
Malay=Ìàëàéñêèé
|
||||||
|
Maltese=Ìàëüòèéñêèé
|
||||||
|
Macedonian=Ìàêåäîíñêèé
|
||||||
|
Malayalam=Ìàëàÿëàì
|
||||||
|
Lithuanian=Ëèòîâñêèé
|
||||||
|
Latvian=Ëàòûøñêèé
|
||||||
|
Korean=Êîðåéñêèé
|
||||||
|
Kannada=Êàííàäà
|
||||||
|
Italian=Èòàëüÿíñêèé
|
||||||
|
Icelandic=Èñëàíäñêèé
|
||||||
|
Indonesian=Èíäîíåçèéñêèé
|
||||||
|
Cherokee=×åðîêè
|
||||||
|
Hungarian=Âåíãåðñêèé
|
||||||
|
Croatian=Õîðâàòñêèé
|
||||||
|
Hindi=Õèíäè
|
||||||
|
Hebrew=Èâðèò
|
||||||
|
Galician=Ãàëèöêèé
|
||||||
|
MiddleFrench=Ñðåäíåâåêîâûé Ôðàíöóçñêèé
|
||||||
|
Frankish=Ôðàíêñêèé
|
||||||
|
French=Ôðàíöóçñêèé
|
||||||
|
Finnish=Ôèíñêèé
|
||||||
|
Basque=Áàñêñêèé
|
||||||
|
Estonian=Ýñòîíñêèé
|
||||||
|
MathEquation=Ìàòåìàòèêà / óðàâíåíèå
|
||||||
|
Esperanto=Ýñïåðàíòî
|
||||||
|
MiddleEnglish=Ñðåäíåâåêîâûé Àíãëèéñêèé
|
||||||
|
Greek=Ãðå÷åñêèé
|
||||||
|
German=Íåìåöêèé
|
||||||
|
Danish=Äàòñêèé
|
||||||
|
Czech=×åøñêèé
|
||||||
|
Catalan=Êàòàëîíñêèé
|
||||||
|
Bulgarian=Áîëãàðñêèé
|
||||||
|
Bengali=Áåíãàëüñêèé
|
||||||
|
Belarusian=Áåëîðóññêèé
|
||||||
|
Azerbaijani=Àçåðáàéäæàíñêèé
|
||||||
|
Arabic=Àðàáñêèé
|
||||||
|
Afrikaans=Àôðèêààíñ
|
||||||
|
Japanese=ßïîíñêèé
|
||||||
|
ChineseSimplified=Êèòàéñêèé (óïðîùåííûé)
|
||||||
|
ChineseTraditional=Êèòàéñêèé (òðàäèöèîííûé)
|
||||||
|
Russian=Ðóññêèé
|
||||||
|
Vietnamese=Âüåòíàìñêèé
|
||||||
|
|
||||||
|
|
26
distr/sf/readme.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#Changes.
|
||||||
|
## 1.2.3:
|
||||||
|
* Fixed possible crash.
|
||||||
|
* Added version information and some error messages.
|
||||||
|
## 1.2.2:
|
||||||
|
* Added alternative translation source.
|
||||||
|
## 1.2.1:
|
||||||
|
* Fixed the bug with the lack of translation.
|
||||||
|
* Fixed the bug with the use of language recognition by default when you select another one in OCR region selection mode.
|
||||||
|
## 1.2.0:
|
||||||
|
* Changed installer.
|
||||||
|
* Added all available languages for recognition.
|
||||||
|
* Added ability to specify language when selecting the field of recognition using right click.
|
||||||
|
* Human readable language names.
|
||||||
|
* Reduced memory usage.
|
||||||
|
* Updated libraries.
|
||||||
|
## 1.1.3:
|
||||||
|
* Added library libgcc_s_dw2-1.dll.
|
||||||
|
* Updated libraries.
|
||||||
|
## 1.1.2:
|
||||||
|
* If you specify in the settings the path to tessdata characters "\" or "/" at the end of the path are no longer required.
|
||||||
|
## 1.1.1:
|
||||||
|
* Fixed an issue with incorrect window size when display results.
|
||||||
|
## 1.1.0:
|
||||||
|
* Displays the result in the window, along with the picture.
|
||||||
|
* Context menu expanded. Added buttons display the last result and copy it to the clipboard.
|
28
external/gtest/LICENSE
vendored
@ -1,28 +0,0 @@
|
|||||||
Copyright 2008, Google Inc.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
11824
external/gtest/gtest-all.cc
vendored
14813
external/gtest/gtest.h
vendored
22
external/miniz/LICENSE
vendored
@ -1,22 +0,0 @@
|
|||||||
Copyright 2013-2014 RAD Game Tools and Valve Software
|
|
||||||
Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
|
|
||||||
|
|
||||||
All Rights Reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
7657
external/miniz/miniz.c
vendored
1338
external/miniz/miniz.h
vendored
BIN
images/icon.ico
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
images/icon.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
25
main.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include <QApplication>
|
||||||
|
#include <QTranslator>
|
||||||
|
|
||||||
|
#include <Manager.h>
|
||||||
|
#include <Settings.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
a.setQuitOnLastWindowClosed (false);
|
||||||
|
a.setApplicationName (settings_values::appName);
|
||||||
|
a.setOrganizationName (settings_values::companyName);
|
||||||
|
|
||||||
|
QTranslator translator;
|
||||||
|
// Set default to english.
|
||||||
|
if (translator.load (QLocale::system (), "translation", "_", ":/translations") ||
|
||||||
|
translator.load (":/translations/translation_en"))
|
||||||
|
{
|
||||||
|
a.installTranslator(&translator);
|
||||||
|
}
|
||||||
|
|
||||||
|
Manager manager;
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
<RCC>
|
|
||||||
<qresource prefix="/icons">
|
|
||||||
<file alias="app.png">share/images/STIconBlue.png</file>
|
|
||||||
<file alias="st_success.png">share/images/STIconGreen.png</file>
|
|
||||||
<file alias="st_busy.png">share/images/STIconOrange.png</file>
|
|
||||||
<file alias="st_error.png">share/images/STIconRed.png</file>
|
|
||||||
<file alias="loadImages.png">share/images/loadImages.png</file>
|
|
||||||
<file alias="loadImages@2x.png">share/images/loadImages@2x.png</file>
|
|
||||||
<file alias="debug.png">share/images/debug.png</file>
|
|
||||||
<file alias="debug@2x.png">share/images/debug@2x.png</file>
|
|
||||||
</qresource>
|
|
||||||
<qresource prefix="/translations">
|
|
||||||
<file alias="screentranslator_ru.qm">share/translations/screentranslator_ru.qm</file>
|
|
||||||
<file alias="screentranslator_he.qm">share/translations/screentranslator_he.qm</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
|
@ -1,142 +0,0 @@
|
|||||||
QT = core gui widgets network webenginewidgets
|
|
||||||
|
|
||||||
TARGET = screen-translator
|
|
||||||
TEMPLATE = app
|
|
||||||
CONFIG += c++17
|
|
||||||
|
|
||||||
DEPS_DIR=$$(ST_DEPS_DIR)
|
|
||||||
isEmpty(DEPS_DIR):DEPS_DIR=$$PWD/../deps
|
|
||||||
INCLUDEPATH += $$DEPS_DIR/include
|
|
||||||
LIBS += -L$$DEPS_DIR/lib
|
|
||||||
LIBS += -lhunspell -lleptonica -ltesseract
|
|
||||||
|
|
||||||
win32{
|
|
||||||
LIBS += -lUser32
|
|
||||||
}
|
|
||||||
linux{
|
|
||||||
QT += x11extras
|
|
||||||
LIBS += -lX11
|
|
||||||
}
|
|
||||||
|
|
||||||
SOURCES += $$PWD/external/miniz/miniz.c
|
|
||||||
INCLUDEPATH += $$PWD/external
|
|
||||||
|
|
||||||
VER=3.3.0
|
|
||||||
DEFINES += VERSION="$$VER"
|
|
||||||
VERSION = $$VER.0
|
|
||||||
QMAKE_TARGET_COMPANY = Gres
|
|
||||||
QMAKE_TARGET_PRODUCT = Screen Translator
|
|
||||||
QMAKE_TARGET_COPYRIGHT = Copyright (c) Gres
|
|
||||||
RC_ICONS = $$PWD/share/images/icon.ico
|
|
||||||
|
|
||||||
INCLUDEPATH += src src/service src/capture src/ocr \
|
|
||||||
src/represent src/translate src/correct
|
|
||||||
|
|
||||||
HEADERS += \
|
|
||||||
src/capture/capturearea.h \
|
|
||||||
src/capture/captureareaeditor.h \
|
|
||||||
src/capture/captureareaselector.h \
|
|
||||||
src/capture/capturer.h \
|
|
||||||
src/commonmodels.h \
|
|
||||||
src/correct/corrector.h \
|
|
||||||
src/correct/correctorworker.h \
|
|
||||||
src/correct/hunspellcorrector.h \
|
|
||||||
src/languagecodes.h \
|
|
||||||
src/manager.h \
|
|
||||||
src/ocr/recognizer.h \
|
|
||||||
src/ocr/recognizerworker.h \
|
|
||||||
src/ocr/tesseract.h \
|
|
||||||
src/represent/representer.h \
|
|
||||||
src/represent/resulteditor.h \
|
|
||||||
src/represent/resultwidget.h \
|
|
||||||
src/service/apptranslator.h \
|
|
||||||
src/service/debug.h \
|
|
||||||
src/service/geometryutils.h \
|
|
||||||
src/service/globalaction.h \
|
|
||||||
src/service/keysequenceedit.h \
|
|
||||||
src/service/runatsystemstart.h \
|
|
||||||
src/service/singleapplication.h \
|
|
||||||
src/service/updates.h \
|
|
||||||
src/service/widgetstate.h \
|
|
||||||
src/settings.h \
|
|
||||||
src/settingseditor.h \
|
|
||||||
src/settingsvalidator.h \
|
|
||||||
src/stfwd.h \
|
|
||||||
src/substitutionstable.h \
|
|
||||||
src/task.h \
|
|
||||||
src/translate/translator.h \
|
|
||||||
src/translate/webpage.h \
|
|
||||||
src/translate/webpageproxy.h \
|
|
||||||
src/trayicon.h
|
|
||||||
|
|
||||||
SOURCES += \
|
|
||||||
src/capture/capturearea.cpp \
|
|
||||||
src/capture/captureareaeditor.cpp \
|
|
||||||
src/capture/captureareaselector.cpp \
|
|
||||||
src/capture/capturer.cpp \
|
|
||||||
src/commonmodels.cpp \
|
|
||||||
src/correct/corrector.cpp \
|
|
||||||
src/correct/correctorworker.cpp \
|
|
||||||
src/correct/hunspellcorrector.cpp \
|
|
||||||
src/languagecodes.cpp \
|
|
||||||
src/main.cpp \
|
|
||||||
src/manager.cpp \
|
|
||||||
src/ocr/recognizer.cpp \
|
|
||||||
src/ocr/recognizerworker.cpp \
|
|
||||||
src/ocr/tesseract.cpp \
|
|
||||||
src/represent/representer.cpp \
|
|
||||||
src/represent/resulteditor.cpp \
|
|
||||||
src/represent/resultwidget.cpp \
|
|
||||||
src/service/apptranslator.cpp \
|
|
||||||
src/service/debug.cpp \
|
|
||||||
src/service/geometryutils.cpp \
|
|
||||||
src/service/globalaction.cpp \
|
|
||||||
src/service/keysequenceedit.cpp \
|
|
||||||
src/service/runatsystemstart.cpp \
|
|
||||||
src/service/singleapplication.cpp \
|
|
||||||
src/service/updates.cpp \
|
|
||||||
src/service/widgetstate.cpp \
|
|
||||||
src/settings.cpp \
|
|
||||||
src/settingseditor.cpp \
|
|
||||||
src/settingsvalidator.cpp \
|
|
||||||
src/substitutionstable.cpp \
|
|
||||||
src/translate/translator.cpp \
|
|
||||||
src/translate/webpage.cpp \
|
|
||||||
src/translate/webpageproxy.cpp \
|
|
||||||
src/trayicon.cpp
|
|
||||||
|
|
||||||
RESOURCES += \
|
|
||||||
recources.qrc
|
|
||||||
|
|
||||||
FORMS += \
|
|
||||||
src/settingseditor.ui
|
|
||||||
|
|
||||||
OTHER_FILES += \
|
|
||||||
translators/*.js \
|
|
||||||
version.json \
|
|
||||||
updates.json
|
|
||||||
|
|
||||||
TRANSLATIONS += \
|
|
||||||
share/translations/screentranslator_ru.ts \
|
|
||||||
share/translations/screentranslator_he.ts
|
|
||||||
|
|
||||||
linux {
|
|
||||||
PREFIX = /usr
|
|
||||||
|
|
||||||
target.path = $$PREFIX/bin
|
|
||||||
|
|
||||||
shortcuts.files = $$PWD/share/screentranslator.desktop
|
|
||||||
shortcuts.path = $$PREFIX/share/applications/
|
|
||||||
pixmaps.files += $$PWD/share/images/screentranslator.png
|
|
||||||
pixmaps.path = $$PREFIX/share/icons/hicolor/128x128/apps/
|
|
||||||
|
|
||||||
INSTALLS += target shortcuts pixmaps
|
|
||||||
}
|
|
||||||
win32 {
|
|
||||||
RC_ICONS = $$PWD/share/images/icon.ico
|
|
||||||
target.path = /
|
|
||||||
INSTALLS += target
|
|
||||||
}
|
|
||||||
mac {
|
|
||||||
ICON = $$PWD/share/images/icon.icns
|
|
||||||
}
|
|
@ -1,125 +0,0 @@
|
|||||||
# Changes
|
|
||||||
|
|
||||||
## 3.3.0
|
|
||||||
|
|
||||||
* Use single tesseract library (not optimized and compatible versions)
|
|
||||||
* Improved recognition
|
|
||||||
|
|
||||||
## 3.2.3
|
|
||||||
|
|
||||||
* Fixed translators order persistance
|
|
||||||
* Fixed multi-monitor support
|
|
||||||
* Improves result representation near monitor borders
|
|
||||||
* Updated recognition library
|
|
||||||
|
|
||||||
## 3.2.2
|
|
||||||
|
|
||||||
* Disabled hotkeys with several consecutive combinations
|
|
||||||
* Added the ability to use some service buttons for hotkeys
|
|
||||||
* Fixed multiple monitors support if the main one is not at the top left
|
|
||||||
* Automatic selection of the supported version of tesseract
|
|
||||||
|
|
||||||
## 3.2.1
|
|
||||||
|
|
||||||
* Fixed incorrect update install
|
|
||||||
|
|
||||||
## 3.2.0
|
|
||||||
|
|
||||||
* Improved vertical text recognition
|
|
||||||
* Improved incorrect settings notification
|
|
||||||
* Improved update process
|
|
||||||
|
|
||||||
## 3.1.2
|
|
||||||
|
|
||||||
* Fixed manually corrected text translation
|
|
||||||
|
|
||||||
## 3.1.1
|
|
||||||
|
|
||||||
* Fixed portable mode detection
|
|
||||||
|
|
||||||
## 3.1.0
|
|
||||||
|
|
||||||
* Added ability to choose a recognition version from the program
|
|
||||||
* Added some error messages about misconfiguration
|
|
||||||
* Fixed some errors
|
|
||||||
|
|
||||||
## 3.0.1
|
|
||||||
|
|
||||||
* Fixed some errors
|
|
||||||
* Added `compatible` version (fixes crash during recognition)
|
|
||||||
|
|
||||||
## 3.0.0
|
|
||||||
|
|
||||||
* Changed distribution model: a zip archive instead of an installer
|
|
||||||
* Required resources can now be downloaded from the program
|
|
||||||
* Added ability to capture images in saved areas (reuse selection)
|
|
||||||
* Many interface changes
|
|
||||||
* Updated libraries versions
|
|
||||||
|
|
||||||
## 2.0.2
|
|
||||||
|
|
||||||
* Added force translator rotation option.
|
|
||||||
|
|
||||||
## 2.0.1
|
|
||||||
|
|
||||||
* Fixed installer.
|
|
||||||
|
|
||||||
## 2.0.0
|
|
||||||
|
|
||||||
* Added a version for linux.
|
|
||||||
* Added support for multiple monitors.
|
|
||||||
* Added ability of recognition without translation.
|
|
||||||
* Added ability to recapture from old image.
|
|
||||||
* Added ability to recapture without closing capture window.
|
|
||||||
* Added ability to re-recognize other language.
|
|
||||||
* Added ability to display intermediate result when error occured.
|
|
||||||
* Added support for different translation services.
|
|
||||||
* Added ability to copy image to clipboard.
|
|
||||||
* Added ability to edit recognized text.
|
|
||||||
* Added ability to automatically correct common recognition mistakes.
|
|
||||||
* Added ability to use a proxy.
|
|
||||||
* Added ability to swap translation and recognition languages.
|
|
||||||
* Updated icons.
|
|
||||||
* Show progress on icon.
|
|
||||||
* Added ability to automatically update.
|
|
||||||
|
|
||||||
## 1.2.3
|
|
||||||
|
|
||||||
* Fixed possible crash.
|
|
||||||
* Added version information and some error messages.
|
|
||||||
|
|
||||||
## 1.2.2
|
|
||||||
|
|
||||||
* Added alternative translation source.
|
|
||||||
|
|
||||||
## 1.2.1
|
|
||||||
|
|
||||||
* Fixed the bug with the lack of translation.
|
|
||||||
* Fixed the bug with the use of language recognition by default when you select another one in OCR region selection mode.
|
|
||||||
|
|
||||||
## 1.2.0
|
|
||||||
|
|
||||||
* Changed installer.
|
|
||||||
* Added all available languages for recognition.
|
|
||||||
* Added ability to specify language when selecting the field of recognition using right click.
|
|
||||||
* Human readable language names.
|
|
||||||
* Reduced memory usage.
|
|
||||||
* Updated libraries.
|
|
||||||
|
|
||||||
## 1.1.3
|
|
||||||
|
|
||||||
* Added library libgcc_s_dw2-1.dll.
|
|
||||||
* Updated libraries.
|
|
||||||
|
|
||||||
## 1.1.2
|
|
||||||
|
|
||||||
* If you specify in the settings the path to tessdata characters "\" or "/" at the end of the path are no longer required.
|
|
||||||
|
|
||||||
## 1.1.1
|
|
||||||
|
|
||||||
* Fixed an issue with incorrect window size when display results.
|
|
||||||
|
|
||||||
## 1.1.0
|
|
||||||
|
|
||||||
* Displays the result in the window, along with the picture.
|
|
||||||
* Context menu expanded. Added buttons display the last result and copy it to the clipboard.
|
|
@ -1,125 +0,0 @@
|
|||||||
# Изменения
|
|
||||||
|
|
||||||
## 3.3.0
|
|
||||||
|
|
||||||
* Использование единой библиотеки распознавания (без оптимизированной и совместимой версий)
|
|
||||||
* Улучшено распознавание
|
|
||||||
|
|
||||||
## 3.2.3
|
|
||||||
|
|
||||||
* Исправлено сохранение порядка переводчиков в настройках
|
|
||||||
* Исправлена работа с несколькими мониторами
|
|
||||||
* Улучшено отображение результата на границе монитора
|
|
||||||
* Обновлена версия библиотеки распознавания
|
|
||||||
|
|
||||||
## 3.2.2
|
|
||||||
|
|
||||||
* Исключено задание горячих клавиш из нескольких последовательных комбинаций
|
|
||||||
* Добавлена возможность использования некоторых служебных кнопок для горячих клавиш
|
|
||||||
* Исправлена работа с несколькими мониторами, если главный находится не слева-вверху
|
|
||||||
* Автоматический выбор поддерживаемой версии tesseract
|
|
||||||
|
|
||||||
## 3.2.1
|
|
||||||
|
|
||||||
* Исправлена некорректная установка обновления
|
|
||||||
|
|
||||||
## 3.2.0
|
|
||||||
|
|
||||||
* Улучшено распознавание вертикального текста
|
|
||||||
* Улучшено информирование о некорректных настройках
|
|
||||||
* Упрощена работа с обновлениями
|
|
||||||
|
|
||||||
## 3.1.2
|
|
||||||
|
|
||||||
* Исправлен перевод исправленного вручную текста
|
|
||||||
|
|
||||||
## 3.1.1
|
|
||||||
|
|
||||||
* Исправлено определение работы в Portable режиме
|
|
||||||
|
|
||||||
## 3.1.0
|
|
||||||
|
|
||||||
* Добавлена возможность выбора версии библиотеки распознавания из программы
|
|
||||||
* Добавлены сообщения об ошибках при неправильной настройке
|
|
||||||
* Исправлены некоторые ошибки
|
|
||||||
|
|
||||||
## 3.0.1
|
|
||||||
|
|
||||||
* Исправлены некоторые ошибки
|
|
||||||
* Добавлена `совместимая` версия (исправляет падение при распознавании)
|
|
||||||
|
|
||||||
## 3.0.0
|
|
||||||
|
|
||||||
* Изменен порядок распространения: удалены установщики. Для установки достаточно распаковать папку нужного дистрибутива в желаемое место и запустить программу
|
|
||||||
* Необходимые ресурсы скачиваются из программы, а не вручную или через установщик
|
|
||||||
* Добавлена возможность захвата изображений в заранее подготовленных областях
|
|
||||||
* Много мелких изменений в интерфейсе
|
|
||||||
* Обновлены версии библиотек
|
|
||||||
|
|
||||||
## 2.0.2
|
|
||||||
|
|
||||||
* Добавлена настройка принудительной смены переводчиков.
|
|
||||||
|
|
||||||
## 2.0.1
|
|
||||||
|
|
||||||
* Исправлен установщик.
|
|
||||||
|
|
||||||
## 2.0.0
|
|
||||||
|
|
||||||
* Добавлена версия под linux.
|
|
||||||
* Добавлена поддержка нескольких мониторов.
|
|
||||||
* Добавлена возможность распознание без перевода.
|
|
||||||
* Добавлена возможность вызова старого рисунка для выделения.
|
|
||||||
* Добавлена возможность повторного выделения без закрытия окна захвата.
|
|
||||||
* Добавлена возможность повторного распознания на другом языке.
|
|
||||||
* Добавлена возможность отображения промежуточного результата при ошибке перевода.
|
|
||||||
* Добавлена поддержка разных сервисов перевода.
|
|
||||||
* Добавлена возможность копирования изображения в буфер.
|
|
||||||
* Добавлена возможность редакции распознанного текста.
|
|
||||||
* Добавлена возможность автоматической коррекции частых ошибок распознавания.
|
|
||||||
* Добавлена возможность использования прокси.
|
|
||||||
* Добавлена возможность разовой смена языка перевода и распознавания.
|
|
||||||
* Обновлены иконки.
|
|
||||||
* Добавлено отображение статуса работы на иконке.
|
|
||||||
* Добавлена возможность автоматического обновления.
|
|
||||||
|
|
||||||
## 1.2.3
|
|
||||||
|
|
||||||
* Устранена возможная причина падения.
|
|
||||||
* Добавлена информация о версии и некоторые сообщения об ошибках.
|
|
||||||
|
|
||||||
## 1.2.2
|
|
||||||
|
|
||||||
* Добавлен альтернативный источник перевода.
|
|
||||||
|
|
||||||
## 1.2.1
|
|
||||||
|
|
||||||
* Устранена ошибка отсутствия перевода.
|
|
||||||
* Устранена ошибка использования языка распознавания по умолчанию при выборе другого в окне выделения области распознавания.
|
|
||||||
|
|
||||||
## 1.2.0
|
|
||||||
|
|
||||||
* Изменен установщик.
|
|
||||||
* В установщик добавлены все доступные языки для распознавания.
|
|
||||||
* Добавлена возможность указания языка при выборе области распознавания при помощи выделения с правым кликом.
|
|
||||||
* Человекочитаемые названия языков.
|
|
||||||
* Уменьшено потребление памяти.
|
|
||||||
* Обновлены библиотеки.
|
|
||||||
|
|
||||||
## 1.1.3
|
|
||||||
|
|
||||||
* В установщик добавлена библиотека libgcc_s_dw2-1.dll.
|
|
||||||
* Обновлены библиотеки.
|
|
||||||
|
|
||||||
## 1.1.2
|
|
||||||
|
|
||||||
* При задании в настройках пути к tessdata символы «\» или «/» в конце пути теперь не обязательны.
|
|
||||||
|
|
||||||
## 1.1.1
|
|
||||||
|
|
||||||
* Пофиксен баг с неверным размером окна отображения результатов.
|
|
||||||
|
|
||||||
## 1.1.0
|
|
||||||
|
|
||||||
* Отображение результата в окошке, вместе с картинкой.
|
|
||||||
* Контекстное меню расширено. Добавлены кнопки отображения последнего результата и копирования его в буфер обмена.
|
|
@ -1,65 +0,0 @@
|
|||||||
import common as c
|
|
||||||
from config import *
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import subprocess as sub
|
|
||||||
import shutil
|
|
||||||
from glob import glob
|
|
||||||
|
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == 'glibc_version': # subcommand
|
|
||||||
sub.run('ldd --version | head -n 1 | grep -Po "\\d\\.\\d\\d"', shell=True)
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
tag = os.environ.get('TAG', '')
|
|
||||||
artifact_name = '{}-{}{}.AppImage'.format(app_name, app_version, tag)
|
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == 'artifact_name': # subcommand
|
|
||||||
c.print(artifact_name)
|
|
||||||
exit(0)
|
|
||||||
artifact_path = os.path.abspath(artifact_name)
|
|
||||||
|
|
||||||
c.print('>> Making appimage')
|
|
||||||
|
|
||||||
base_url = 'https://github.com/probonopd/linuxdeployqt/releases/download'
|
|
||||||
continuous_url = base_url + '/continuous/linuxdeployqt-continuous-x86_64.AppImage'
|
|
||||||
tagged_url = base_url + '/6/linuxdeployqt-6-x86_64.AppImage'
|
|
||||||
linuxdeployqt_url = continuous_url
|
|
||||||
linuxdeployqt_original = os.path.basename(linuxdeployqt_url)
|
|
||||||
|
|
||||||
c.download(linuxdeployqt_url, linuxdeployqt_original)
|
|
||||||
c.run('chmod a+x {}'.format(linuxdeployqt_original))
|
|
||||||
|
|
||||||
linuxdeployqt_bin = os.path.abspath('linuxdeployqt')
|
|
||||||
c.symlink(linuxdeployqt_original, linuxdeployqt_bin)
|
|
||||||
|
|
||||||
os.chdir(build_dir)
|
|
||||||
|
|
||||||
install_dir = os.path.abspath('appdir')
|
|
||||||
c.recreate_dir(install_dir)
|
|
||||||
c.run('make INSTALL_ROOT={0} DESTDIR={0} install'.format(install_dir))
|
|
||||||
|
|
||||||
if c.is_inside_docker():
|
|
||||||
c.run('{} --appimage-extract'.format(linuxdeployqt_bin))
|
|
||||||
linuxdeployqt_bin = os.path.abspath('squashfs-root/AppRun')
|
|
||||||
|
|
||||||
os.environ['LD_LIBRARY_PATH'] = dependencies_dir + '/lib'
|
|
||||||
os.environ['VERSION'] = app_version
|
|
||||||
# debug flags: -unsupported-bundle-everything -unsupported-allow-new-glibc
|
|
||||||
flags = '' if os.getenv("DEBUG") is None else '-unsupported-allow-new-glibc'
|
|
||||||
|
|
||||||
additional_files = glob(ssl_dir + '/lib/lib*.so.*') + \
|
|
||||||
glob('/usr/lib/x86_64-linux-gnu/nss/*')
|
|
||||||
out_lib_dir = install_dir + '/usr/lib'
|
|
||||||
os.makedirs(out_lib_dir, exist_ok=True)
|
|
||||||
for f in additional_files:
|
|
||||||
c.print('>> Copying {} to {}'.format(f, out_lib_dir))
|
|
||||||
shutil.copy(f, out_lib_dir)
|
|
||||||
|
|
||||||
c.ensure_got_path('{}/usr/share/doc/libc6/copyright'.format(install_dir))
|
|
||||||
|
|
||||||
c.run('{} {}/usr/share/applications/*.desktop {} -appimage -qmake={}/bin/qmake'.format(
|
|
||||||
linuxdeployqt_bin, install_dir, flags, qt_dir))
|
|
||||||
|
|
||||||
c.run('mv {}-{}*.AppImage "{}"'.format(app_name, app_version, artifact_path))
|
|
||||||
|
|
||||||
bin_path = install_dir + '/usr/bin/' + bin_name
|
|
||||||
c.print('>> Md5 {} {}'.format(bin_path, c.md5sum(bin_path)))
|
|
@ -1,26 +0,0 @@
|
|||||||
import common as c
|
|
||||||
from config import *
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
|
|
||||||
c.print('>> Building {} on {}'.format(app_name, os_name))
|
|
||||||
|
|
||||||
c.add_to_path(os.path.abspath(qt_dir + '/bin'))
|
|
||||||
os.environ['ST_DEPS_DIR'] = dependencies_dir
|
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version)
|
|
||||||
c.apply_cmd_env(env_cmd)
|
|
||||||
|
|
||||||
c.recreate_dir(build_dir)
|
|
||||||
os.chdir(build_dir)
|
|
||||||
|
|
||||||
c.run('lupdate "{}"'.format(pro_file))
|
|
||||||
c.run('lrelease "{}"'.format(pro_file))
|
|
||||||
|
|
||||||
c.set_make_threaded()
|
|
||||||
build_type_flag = 'debug' if build_type == 'debug' else 'release'
|
|
||||||
qmake_flags = os.environ.get('QMAKE_FLAGS','') + ' CONFIG+=' + build_type_flag
|
|
||||||
c.run('qmake {} "{}"'.format(qmake_flags, pro_file))
|
|
||||||
make_cmd = c.get_make_cmd()
|
|
||||||
c.run(make_cmd)
|
|
@ -1,213 +0,0 @@
|
|||||||
import os
|
|
||||||
import subprocess as sub
|
|
||||||
import urllib.request
|
|
||||||
from shutil import which
|
|
||||||
import zipfile
|
|
||||||
import tarfile
|
|
||||||
import functools
|
|
||||||
import shutil
|
|
||||||
import multiprocessing
|
|
||||||
import platform
|
|
||||||
import re
|
|
||||||
import ast
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
|
|
||||||
print = functools.partial(print, flush=True)
|
|
||||||
|
|
||||||
|
|
||||||
def run(cmd, capture_output=False, silent=False):
|
|
||||||
print('>> Running', cmd)
|
|
||||||
if capture_output:
|
|
||||||
result = sub.run(cmd, check=True, shell=True, universal_newlines=True,
|
|
||||||
stdout=sub.PIPE, stderr=sub.STDOUT)
|
|
||||||
if not silent:
|
|
||||||
print(result.stdout)
|
|
||||||
else:
|
|
||||||
if not silent:
|
|
||||||
result = sub.run(cmd, check=True, shell=True)
|
|
||||||
else:
|
|
||||||
result = sub.run(cmd, check=True, shell=True,
|
|
||||||
stdout=sub.DEVNULL, stderr=sub.DEVNULL)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def download(url, out, force=False):
|
|
||||||
print('>> Downloading', url, 'as', out)
|
|
||||||
if not force and os.path.exists(out):
|
|
||||||
print('>>', out, 'already exists')
|
|
||||||
return
|
|
||||||
out_path = os.path.dirname(out)
|
|
||||||
if len(out_path) > 0:
|
|
||||||
os.makedirs(out_path, exist_ok=True)
|
|
||||||
urllib.request.urlretrieve(url, out)
|
|
||||||
|
|
||||||
|
|
||||||
def extract(src, dest):
|
|
||||||
abs_path = os.path.abspath(src)
|
|
||||||
print('>> Extracting', abs_path, 'to', dest)
|
|
||||||
if len(dest) > 0:
|
|
||||||
os.makedirs(dest, exist_ok=True)
|
|
||||||
|
|
||||||
if which('cmake'):
|
|
||||||
out = run('cmake -E tar t "{}"'.format(abs_path),
|
|
||||||
capture_output=True, silent=True)
|
|
||||||
files = out.stdout.split('\n')
|
|
||||||
already_exist = True
|
|
||||||
for file in files:
|
|
||||||
if not os.path.exists(os.path.join(dest, file)):
|
|
||||||
already_exist = False
|
|
||||||
break
|
|
||||||
if already_exist:
|
|
||||||
print('>> All files already exist')
|
|
||||||
return
|
|
||||||
sub.run('cmake -E tar xvf "{}"'.format(abs_path),
|
|
||||||
check=True, shell=True, cwd=dest)
|
|
||||||
return
|
|
||||||
|
|
||||||
is_tar_smth = src.endswith('.tar', 0, src.rfind('.'))
|
|
||||||
if which('7z'):
|
|
||||||
sub.run('7z x "{}" -o"{}"'.format(abs_path, dest),
|
|
||||||
check=True, shell=True, input=b'S\n')
|
|
||||||
|
|
||||||
if is_tar_smth:
|
|
||||||
inner_name = abs_path[:abs_path.rfind('.')]
|
|
||||||
sub.run('7z x "{}" -o"{}"'.format(inner_name, dest),
|
|
||||||
check=True, shell=True, input=b'S\n')
|
|
||||||
return
|
|
||||||
|
|
||||||
if src.endswith('.tar') or is_tar_smth:
|
|
||||||
path = abs_path if platform.system() != "Windows" else os.path.relpath(abs_path)
|
|
||||||
if which('tar'):
|
|
||||||
sub.run('tar xf "{}" --keep-newer-files -C "{}"'.format(path, dest),
|
|
||||||
check=True, shell=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
raise RuntimeError('No archiver to extract {} file'.format(src))
|
|
||||||
|
|
||||||
|
|
||||||
def get_folder_files(path):
|
|
||||||
result = []
|
|
||||||
for root, _, files in os.walk(path):
|
|
||||||
for file in files:
|
|
||||||
result.append(os.path.join(root, file))
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def get_archive_top_dir(path):
|
|
||||||
"""Return first top level folder name in given archive or raises RuntimeError"""
|
|
||||||
with tarfile.open(path) as tar:
|
|
||||||
first = tar.next()
|
|
||||||
if not first is None:
|
|
||||||
result = os.path.dirname(first.path)
|
|
||||||
if len(result) == 0:
|
|
||||||
result = first.path
|
|
||||||
return result
|
|
||||||
raise RuntimeError('Failed to open file or empty archive ' + path)
|
|
||||||
|
|
||||||
|
|
||||||
def archive(files, out):
|
|
||||||
print('>> Archiving', files, 'into', out)
|
|
||||||
if out.endswith('.zip'):
|
|
||||||
arc = zipfile.ZipFile(out, 'w', zipfile.ZIP_DEFLATED)
|
|
||||||
for f in files:
|
|
||||||
arc.write(f)
|
|
||||||
arc.close()
|
|
||||||
return
|
|
||||||
|
|
||||||
if out.endswith('.tar.gz'):
|
|
||||||
arc = tarfile.open(out, 'w|gz')
|
|
||||||
for f in files:
|
|
||||||
arc.add(f)
|
|
||||||
arc.close()
|
|
||||||
return
|
|
||||||
|
|
||||||
raise RuntimeError('No archiver to create {} file'.format(out))
|
|
||||||
|
|
||||||
|
|
||||||
def symlink(src, dest):
|
|
||||||
print('>> Creating symlink', src, '=>', dest)
|
|
||||||
norm_src = os.path.normcase(src)
|
|
||||||
norm_dest = os.path.normcase(dest)
|
|
||||||
if os.path.lexists(norm_dest):
|
|
||||||
os.remove(norm_dest)
|
|
||||||
os.symlink(norm_src, norm_dest,
|
|
||||||
target_is_directory=os.path.isdir(norm_src))
|
|
||||||
|
|
||||||
|
|
||||||
def recreate_dir(path):
|
|
||||||
shutil.rmtree(path, ignore_errors=True)
|
|
||||||
os.mkdir(path)
|
|
||||||
|
|
||||||
|
|
||||||
def add_to_path(entry, prepend=True):
|
|
||||||
path_separator = ';' if platform.system() == "Windows" else ':'
|
|
||||||
os.environ['PATH'] = entry + path_separator + os.environ['PATH']
|
|
||||||
|
|
||||||
|
|
||||||
def get_msvc_env_cmd(bitness='64', msvc_version=''):
|
|
||||||
"""Return environment setup command for running msvc compiler for current platform"""
|
|
||||||
if platform.system() != "Windows":
|
|
||||||
return None
|
|
||||||
|
|
||||||
env_script = msvc_version + '/VC/Auxiliary/Build/vcvars{}.bat'.format(bitness)
|
|
||||||
return '"' + env_script + '"'
|
|
||||||
|
|
||||||
|
|
||||||
def get_cmake_arch_args(bitness='64'):
|
|
||||||
if platform.system() != "Windows":
|
|
||||||
return ''
|
|
||||||
return '-A {}'.format('Win32' if bitness == '32' else 'x64')
|
|
||||||
|
|
||||||
|
|
||||||
def get_make_cmd():
|
|
||||||
"""Return `make` command for current platform"""
|
|
||||||
return 'nmake' if platform.system() == "Windows" else 'make'
|
|
||||||
|
|
||||||
|
|
||||||
def set_make_threaded():
|
|
||||||
"""Adjust environment to run threaded make command"""
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
os.environ['CL'] = '/MP'
|
|
||||||
else:
|
|
||||||
os.environ['MAKEFLAGS'] = '-j{}'.format(multiprocessing.cpu_count())
|
|
||||||
|
|
||||||
|
|
||||||
def is_inside_docker():
|
|
||||||
""" Return True if running in a Docker container """
|
|
||||||
with open('/proc/1/cgroup', 'rt') as f:
|
|
||||||
return 'docker' in f.read()
|
|
||||||
|
|
||||||
|
|
||||||
def ensure_got_path(path):
|
|
||||||
os.makedirs(path, exist_ok=True)
|
|
||||||
|
|
||||||
|
|
||||||
def apply_cmd_env(cmd):
|
|
||||||
"""Run cmd and apply its modified environment"""
|
|
||||||
print('>> Applying env after', cmd)
|
|
||||||
separator = 'env follows'
|
|
||||||
script = 'import os,sys;sys.stdout.buffer.write(str(dict(os.environ)).encode(\\\"utf-8\\\"))'
|
|
||||||
env = sub.run('{} && echo "{}" && python -c "{}"'.format(cmd, separator, script),
|
|
||||||
shell=True, stdout=sub.PIPE, encoding='utf-8')
|
|
||||||
|
|
||||||
stringed = env.stdout[env.stdout.index(separator) + len(separator) + 1:]
|
|
||||||
parsed = ast.literal_eval(stringed)
|
|
||||||
|
|
||||||
for key, value in parsed.items():
|
|
||||||
if key in os.environ and os.environ[key] == value:
|
|
||||||
continue
|
|
||||||
if key in os.environ:
|
|
||||||
print('>>> Changing env', key, '\nfrom\n',
|
|
||||||
os.environ[key], '\nto\n', value)
|
|
||||||
os.environ[key] = value
|
|
||||||
|
|
||||||
|
|
||||||
def md5sum(path):
|
|
||||||
if not os.path.exists(path):
|
|
||||||
return ''
|
|
||||||
md5 = hashlib.md5()
|
|
||||||
with open(path, 'rb') as f:
|
|
||||||
md5.update(f.read())
|
|
||||||
return md5.hexdigest()
|
|
||||||
return ''
|
|
@ -1,35 +0,0 @@
|
|||||||
from os import getenv, path
|
|
||||||
import re
|
|
||||||
|
|
||||||
app_name = 'ScreenTranslator'
|
|
||||||
|
|
||||||
target_name = app_name
|
|
||||||
qt_version = '5.15.2'
|
|
||||||
qt_modules = ['qtbase', 'qttools', 'icu',
|
|
||||||
'qttranslations', 'qtx11extras', 'qtwebengine', 'qtwebchannel',
|
|
||||||
'qtdeclarative', 'qtlocation', 'opengl32sw', 'd3dcompiler_47',
|
|
||||||
'qtserialport']
|
|
||||||
qt_dir = path.abspath('qt')
|
|
||||||
ssl_dir = path.abspath('ssl')
|
|
||||||
|
|
||||||
build_dir = path.abspath('build')
|
|
||||||
dependencies_dir = path.abspath('deps')
|
|
||||||
pro_file = path.abspath(path.dirname(__file__) +
|
|
||||||
'/../../screen-translator.pro')
|
|
||||||
test_pro_file = path.abspath(path.dirname(__file__) +
|
|
||||||
'/../../tests/tests.pro')
|
|
||||||
bin_name = 'screen-translator'
|
|
||||||
app_version = 'testing'
|
|
||||||
with open(pro_file, 'r') as f:
|
|
||||||
match = re.search(r'VER=(.*)', f.read())
|
|
||||||
if match:
|
|
||||||
app_version = match.group(1)
|
|
||||||
ts_files_dir = path.abspath(path.dirname(__file__) + '/../../translations')
|
|
||||||
|
|
||||||
os_name = getenv('OS', 'linux')
|
|
||||||
app_version += {'linux': '', 'macos': '-experimental',
|
|
||||||
'win32': '', 'win64': ''}[os_name]
|
|
||||||
bitness = '32' if os_name == 'win32' else '64'
|
|
||||||
msvc_version = getenv('MSVC_VERSION', 'C:/Program Files (x86)/Microsoft Visual Studio/2019/Community')
|
|
||||||
|
|
||||||
build_type = 'release' # 'debug'
|
|
@ -1,131 +0,0 @@
|
|||||||
import common as c
|
|
||||||
from config import bitness, msvc_version, build_dir, dependencies_dir, build_type
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
|
|
||||||
c.print('>> Installing hunspell')
|
|
||||||
|
|
||||||
install_dir = dependencies_dir
|
|
||||||
url = 'https://github.com/hunspell/hunspell/files/2573619/hunspell-1.7.0.tar.gz'
|
|
||||||
required_version = '1.7.0'
|
|
||||||
|
|
||||||
|
|
||||||
build_type_flag = 'Debug' if build_type == 'debug' else 'Release'
|
|
||||||
|
|
||||||
cache_file = install_dir + '/hunspell.cache'
|
|
||||||
cache_file_data = required_version + build_type_flag
|
|
||||||
|
|
||||||
|
|
||||||
def check_existing():
|
|
||||||
if not os.path.exists(cache_file):
|
|
||||||
return False
|
|
||||||
with open(cache_file, 'r') as f:
|
|
||||||
cached = f.read()
|
|
||||||
if cached != cache_file_data:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
dll = install_dir + '/bin/hunspell.dll'
|
|
||||||
lib = install_dir + '/lib/hunspell.lib'
|
|
||||||
if not os.path.exists(dll) or not os.path.exists(lib):
|
|
||||||
return False
|
|
||||||
elif platform.system() == "Darwin":
|
|
||||||
lib = install_dir + '/lib/libhunspell.dylib'
|
|
||||||
if not os.path.exists(lib):
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
lib = install_dir + '/lib/libhunspell.so'
|
|
||||||
if not os.path.exists(lib):
|
|
||||||
return False
|
|
||||||
|
|
||||||
includes_path = install_dir + '/include/hunspell'
|
|
||||||
if len(c.get_folder_files(includes_path)) == 0:
|
|
||||||
return False
|
|
||||||
|
|
||||||
version_file = install_dir + '/lib/pkgconfig/hunspell.pc'
|
|
||||||
if not os.path.exists(version_file):
|
|
||||||
return False
|
|
||||||
|
|
||||||
with open(version_file, 'rt') as f:
|
|
||||||
lines = f.readlines()
|
|
||||||
for l in lines:
|
|
||||||
if not l.startswith('Version'):
|
|
||||||
continue
|
|
||||||
existing_version = l[9:14] # Version: 1.7.0
|
|
||||||
if existing_version != required_version:
|
|
||||||
return False
|
|
||||||
break
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
if check_existing():
|
|
||||||
c.print('>> Using cached')
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
archive = os.path.basename(url)
|
|
||||||
c.download(url, archive)
|
|
||||||
|
|
||||||
src_dir = os.path.abspath('hunspell_src')
|
|
||||||
c.extract(archive, '.')
|
|
||||||
c.symlink(c.get_archive_top_dir(archive), src_dir)
|
|
||||||
|
|
||||||
c.ensure_got_path(install_dir)
|
|
||||||
|
|
||||||
c.recreate_dir(build_dir)
|
|
||||||
os.chdir(build_dir)
|
|
||||||
|
|
||||||
c.set_make_threaded()
|
|
||||||
|
|
||||||
lib_src = os.path.join(src_dir, 'src', 'hunspell')
|
|
||||||
sources = []
|
|
||||||
with os.scandir(lib_src) as it:
|
|
||||||
for f in it:
|
|
||||||
if not f.is_file() or not f.name.endswith('.cxx'):
|
|
||||||
continue
|
|
||||||
sources.append('${SRC_DIR}/' + f.name)
|
|
||||||
headers = ['${SRC_DIR}/atypes.hxx', '${SRC_DIR}/hunspell.h', '${SRC_DIR}/hunspell.hxx',
|
|
||||||
'${SRC_DIR}/hunvisapi.h', '${SRC_DIR}/w_char.hxx']
|
|
||||||
cmake_file = os.path.join(build_dir, 'CMakeLists.txt')
|
|
||||||
with open(cmake_file, 'w') as f:
|
|
||||||
f.write('project(hunspell)\n')
|
|
||||||
f.write('cmake_minimum_required(VERSION 3.11)\n')
|
|
||||||
f.write('set(SRC_DIR "{}")\n'.format(lib_src).replace('\\', '/'))
|
|
||||||
f.write('\n')
|
|
||||||
f.write('add_library(hunspell SHARED {})\n'.format(' '.join(sources)))
|
|
||||||
f.write('\n')
|
|
||||||
f.write('add_compile_definitions(HAVE_CONFIG_H BUILDING_LIBHUNSPELL)\n')
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
f.write('add_compile_definitions(_WIN32)\n')
|
|
||||||
f.write('\n')
|
|
||||||
f.write('install(FILES {} \
|
|
||||||
DESTINATION include/hunspell)\n'.format(' '.join(headers)))
|
|
||||||
f.write('\n')
|
|
||||||
f.write('install(TARGETS hunspell \
|
|
||||||
RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)\n')
|
|
||||||
f.write('\n')
|
|
||||||
f.write('set(prefix "${CMAKE_INSTALL_PREFIX}")\n')
|
|
||||||
f.write('set(VERSION "{}")\n'.format(required_version))
|
|
||||||
f.write('configure_file({}/hunspell.pc.in \
|
|
||||||
${{CMAKE_CURRENT_BINARY_DIR}}/hunspell.pc @ONLY)\n'.format(src_dir.replace('\\', '/')))
|
|
||||||
f.write('install(FILES ${CMAKE_CURRENT_BINARY_DIR}/hunspell.pc \
|
|
||||||
DESTINATION lib/pkgconfig)\n')
|
|
||||||
|
|
||||||
cmake_args = '"{}" -DCMAKE_INSTALL_PREFIX="{}" {}'.format(
|
|
||||||
build_dir, install_dir, c.get_cmake_arch_args(bitness=bitness))
|
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version)
|
|
||||||
c.apply_cmd_env(env_cmd)
|
|
||||||
|
|
||||||
c.set_make_threaded()
|
|
||||||
c.run('cmake {}'.format(cmake_args))
|
|
||||||
build_type_flag = 'Debug' if build_type == 'debug' else 'Release'
|
|
||||||
c.run('cmake --build . --config {}'.format(build_type_flag))
|
|
||||||
c.run('cmake --build . --target install --config {}'.format(build_type_flag))
|
|
||||||
|
|
||||||
with open(cache_file, 'w') as f:
|
|
||||||
f.write(cache_file_data)
|
|
||||||
|
|
||||||
if not check_existing(): # create links
|
|
||||||
c.print('>> Build failed')
|
|
||||||
exit(1)
|
|
@ -1,100 +0,0 @@
|
|||||||
import common as c
|
|
||||||
from config import bitness, msvc_version, build_dir, dependencies_dir, build_type
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
|
|
||||||
c.print('>> Installing leptonica')
|
|
||||||
|
|
||||||
install_dir = dependencies_dir
|
|
||||||
url = 'https://github.com/DanBloomberg/leptonica/releases/download/1.82.0/leptonica-1.82.0.tar.gz'
|
|
||||||
required_version = '1.82.0'
|
|
||||||
|
|
||||||
|
|
||||||
build_type_flag = 'Debug' if build_type == 'debug' else 'Release'
|
|
||||||
|
|
||||||
cache_file = install_dir + '/leptonica.cache'
|
|
||||||
cache_file_data = required_version + build_type_flag
|
|
||||||
|
|
||||||
|
|
||||||
def check_existing():
|
|
||||||
if not os.path.exists(cache_file):
|
|
||||||
return False
|
|
||||||
with open(cache_file, 'r') as f:
|
|
||||||
cached = f.read()
|
|
||||||
if cached != cache_file_data:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
dll = install_dir + '/bin/leptonica-1.82.0.dll'
|
|
||||||
lib = install_dir + '/lib/leptonica-1.82.0.lib'
|
|
||||||
if not os.path.exists(dll) or not os.path.exists(lib):
|
|
||||||
return False
|
|
||||||
c.symlink(dll, install_dir + '/bin/leptonica.dll')
|
|
||||||
c.symlink(lib, install_dir + '/lib/leptonica.lib')
|
|
||||||
elif platform.system() == "Darwin":
|
|
||||||
lib = install_dir + '/lib/libleptonica.1.82.0.dylib'
|
|
||||||
if not os.path.exists(lib):
|
|
||||||
return False
|
|
||||||
c.symlink(lib, install_dir + '/lib/libleptonica.dylib')
|
|
||||||
else:
|
|
||||||
if not os.path.exists(install_dir + '/lib/libleptonica.so'):
|
|
||||||
return False
|
|
||||||
|
|
||||||
includes_path = install_dir + '/include/leptonica'
|
|
||||||
if len(c.get_folder_files(includes_path)) == 0:
|
|
||||||
return False
|
|
||||||
|
|
||||||
version_file = install_dir + '/lib/cmake/leptonica/LeptonicaConfig-version.cmake'
|
|
||||||
if not os.path.exists(version_file):
|
|
||||||
return False
|
|
||||||
|
|
||||||
with open(version_file, 'rt') as f:
|
|
||||||
existing_version = f.readline()[22:28] # set(Leptonica_VERSION 1.82.0)
|
|
||||||
if existing_version != required_version:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
if check_existing():
|
|
||||||
c.print('>> Using cached')
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
archive = os.path.basename(url)
|
|
||||||
c.download(url, archive)
|
|
||||||
|
|
||||||
src_dir = os.path.abspath('leptonica_src')
|
|
||||||
c.extract(archive, '.')
|
|
||||||
c.symlink(c.get_archive_top_dir(archive), src_dir)
|
|
||||||
|
|
||||||
with open('{}/CMakeLists.txt'.format(src_dir), 'r+') as f:
|
|
||||||
data = f.read()
|
|
||||||
data = data.replace('pkg_check_modules(WEBP', '#pkg_check_modules(WEBP')
|
|
||||||
data = data.replace('if(NOT WEBP', 'if(FALSE')
|
|
||||||
f.seek(0, os.SEEK_SET)
|
|
||||||
f.write(data)
|
|
||||||
|
|
||||||
c.ensure_got_path(install_dir)
|
|
||||||
|
|
||||||
c.recreate_dir(build_dir)
|
|
||||||
os.chdir(build_dir)
|
|
||||||
|
|
||||||
cmake_args = '"{}" -DCMAKE_INSTALL_PREFIX="{}" -DBUILD_SHARED_LIBS=ON \
|
|
||||||
-DSW_BUILD=OFF'.format(src_dir, install_dir,)
|
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version)
|
|
||||||
c.apply_cmd_env(env_cmd)
|
|
||||||
cmake_args += ' ' + c.get_cmake_arch_args(bitness=bitness)
|
|
||||||
|
|
||||||
c.set_make_threaded()
|
|
||||||
c.run('cmake {}'.format(cmake_args))
|
|
||||||
build_type_flag = 'Debug' if build_type == 'debug' else 'Release'
|
|
||||||
c.run('cmake --build . --config {}'.format(build_type_flag))
|
|
||||||
c.run('cmake --build . --target install --config {}'.format(build_type_flag))
|
|
||||||
|
|
||||||
with open(cache_file, 'w') as f:
|
|
||||||
f.write(cache_file_data)
|
|
||||||
|
|
||||||
if not check_existing(): # create links
|
|
||||||
c.print('>> Build failed')
|
|
||||||
exit(1)
|
|
@ -1,82 +0,0 @@
|
|||||||
import common as c
|
|
||||||
from config import qt_modules, qt_version, qt_dir, os_name
|
|
||||||
import sys
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
|
|
||||||
c.print('>> Downloading Qt {} ({}) for {}'.format(
|
|
||||||
qt_version, qt_modules, os_name))
|
|
||||||
|
|
||||||
if os_name == 'linux':
|
|
||||||
os_url = 'linux_x64'
|
|
||||||
kit_arch = 'gcc_64'
|
|
||||||
qt_dir_prefix = '{}/gcc_64'.format(qt_version)
|
|
||||||
elif os_name == 'win32':
|
|
||||||
os_url = 'windows_x86'
|
|
||||||
kit_arch = 'win32_msvc2019'
|
|
||||||
qt_dir_prefix = '{}/msvc2019'.format(qt_version)
|
|
||||||
elif os_name == 'win64':
|
|
||||||
os_url = 'windows_x86'
|
|
||||||
kit_arch = 'win64_msvc2019_64'
|
|
||||||
qt_dir_prefix = '{}/msvc2019_64'.format(qt_version)
|
|
||||||
elif os_name == 'macos':
|
|
||||||
os_url = 'mac_x64'
|
|
||||||
kit_arch = 'clang_64'
|
|
||||||
qt_dir_prefix = '{}/clang_64'.format(qt_version)
|
|
||||||
|
|
||||||
qt_version_dotless = qt_version.replace('.', '')
|
|
||||||
base_url = 'https://download.qt.io/online/qtsdkrepository/{}/desktop/qt5_{}' \
|
|
||||||
.format(os_url, qt_version_dotless)
|
|
||||||
updates_file = 'Updates-{}-{}.xml'.format(qt_version, os_name)
|
|
||||||
c.download(base_url + '/Updates.xml', updates_file)
|
|
||||||
|
|
||||||
updates = ET.parse(updates_file)
|
|
||||||
updates_root = updates.getroot()
|
|
||||||
all_modules = {}
|
|
||||||
for i in updates_root.iter('PackageUpdate'):
|
|
||||||
name = i.find('Name').text
|
|
||||||
if 'debug' in name or not kit_arch in name:
|
|
||||||
continue
|
|
||||||
|
|
||||||
archives = i.find('DownloadableArchives')
|
|
||||||
if archives.text is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
archives_parts = archives.text.split(',')
|
|
||||||
version = i.find('Version').text
|
|
||||||
for archive in archives_parts:
|
|
||||||
archive = archive.strip()
|
|
||||||
parts = archive.split('-')
|
|
||||||
module_name = parts[0]
|
|
||||||
all_modules[module_name] = {'package': name, 'file': version + archive}
|
|
||||||
|
|
||||||
if len(sys.argv) > 1: # handle subcommand
|
|
||||||
if sys.argv[1] == 'list':
|
|
||||||
c.print('Available modules:')
|
|
||||||
for k in iter(sorted(all_modules.keys())):
|
|
||||||
c.print(k, '---', all_modules[k]['file'])
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
for module in qt_modules:
|
|
||||||
if module not in all_modules:
|
|
||||||
c.print('>> Required module {} not available'.format(module))
|
|
||||||
continue
|
|
||||||
file_name = all_modules[module]['file']
|
|
||||||
package = all_modules[module]['package']
|
|
||||||
c.download(base_url + '/' + package + '/' + file_name, file_name)
|
|
||||||
c.extract(file_name, '.')
|
|
||||||
|
|
||||||
c.symlink(qt_dir_prefix, qt_dir)
|
|
||||||
|
|
||||||
c.print('>> Updating license')
|
|
||||||
config_name = qt_dir + '/mkspecs/qconfig.pri'
|
|
||||||
config = ''
|
|
||||||
with open(config_name, 'r') as f:
|
|
||||||
config = f.read()
|
|
||||||
|
|
||||||
config = config.replace('Enterprise', 'OpenSource')
|
|
||||||
config = config.replace('licheck.exe', '')
|
|
||||||
config = config.replace('licheck64', '')
|
|
||||||
config = config.replace('licheck_mac', '')
|
|
||||||
|
|
||||||
with open(config_name, 'w') as f:
|
|
||||||
f.write(config)
|
|
@ -1,52 +0,0 @@
|
|||||||
import common as c
|
|
||||||
from config import ssl_dir, os_name
|
|
||||||
import sys
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
|
|
||||||
c.print('>> Downloading ssl for Qt for {}'.format(os_name))
|
|
||||||
|
|
||||||
if os_name == 'linux':
|
|
||||||
os_url = 'linux_x64'
|
|
||||||
tool_name = 'tools_openssl_x64'
|
|
||||||
root_path = 'Tools/OpenSSL/binary'
|
|
||||||
elif os_name == 'win32':
|
|
||||||
os_url = 'windows_x86'
|
|
||||||
tool_name = 'tools_openssl_x86'
|
|
||||||
root_path = 'Tools/OpenSSL/Win_x86'
|
|
||||||
elif os_name == 'win64':
|
|
||||||
os_url = 'windows_x86'
|
|
||||||
tool_name = 'tools_openssl_x64'
|
|
||||||
root_path = 'Tools/OpenSSL/Win_x64'
|
|
||||||
elif os_name == 'macos':
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
base_url = 'https://download.qt.io/online/qtsdkrepository/{}/desktop/{}' \
|
|
||||||
.format(os_url, tool_name)
|
|
||||||
updates_file = 'Updates-{}-{}.xml'.format(tool_name, os_name)
|
|
||||||
c.download(base_url + '/Updates.xml', updates_file)
|
|
||||||
|
|
||||||
updates = ET.parse(updates_file)
|
|
||||||
updates_root = updates.getroot()
|
|
||||||
url = ''
|
|
||||||
file_name = ''
|
|
||||||
for i in updates_root.iter('PackageUpdate'):
|
|
||||||
name = i.find('Name').text
|
|
||||||
if not 'qt.tools.openssl' in name:
|
|
||||||
continue
|
|
||||||
|
|
||||||
archives = i.find('DownloadableArchives')
|
|
||||||
if archives.text is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
version = i.find('Version').text
|
|
||||||
url = base_url + '/' + name + '/' + version + archives.text
|
|
||||||
file_name = archives.text
|
|
||||||
|
|
||||||
if len(url) == 0:
|
|
||||||
c.print('>> No ssl url found')
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
c.download(url, file_name)
|
|
||||||
c.extract(file_name, '.')
|
|
||||||
|
|
||||||
c.symlink(root_path, ssl_dir)
|
|
@ -1,98 +0,0 @@
|
|||||||
import common as c
|
|
||||||
from config import bitness, msvc_version, build_dir, dependencies_dir, build_type
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
|
|
||||||
c.print('>> Installing tesseract')
|
|
||||||
|
|
||||||
install_dir = dependencies_dir
|
|
||||||
required_version = '5.2.0'
|
|
||||||
url = 'https://github.com/tesseract-ocr/tesseract/archive/{}.tar.gz'.format(required_version)
|
|
||||||
|
|
||||||
build_type_flag = 'Debug' if build_type == 'debug' else 'Release'
|
|
||||||
|
|
||||||
cache_file = install_dir + '/tesseract.cache'
|
|
||||||
cache_file_data = required_version + build_type_flag
|
|
||||||
|
|
||||||
def check_existing():
|
|
||||||
if not os.path.exists(cache_file):
|
|
||||||
return False
|
|
||||||
with open(cache_file, 'r') as f:
|
|
||||||
cached = f.read()
|
|
||||||
if cached != cache_file_data:
|
|
||||||
return False
|
|
||||||
|
|
||||||
includes_path = install_dir + '/include/tesseract'
|
|
||||||
if len(c.get_folder_files(includes_path)) == 0:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
file_name_ver = required_version[0] + required_version[2]
|
|
||||||
dll = install_dir + '/bin/tesseract{}.dll'.format(file_name_ver)
|
|
||||||
lib = install_dir + '/lib/tesseract{}.lib'.format(file_name_ver)
|
|
||||||
if not os.path.exists(dll) or not os.path.exists(lib):
|
|
||||||
return False
|
|
||||||
c.symlink(dll, install_dir + '/bin/tesseract.dll')
|
|
||||||
c.symlink(lib, install_dir + '/lib/tesseract.lib')
|
|
||||||
elif platform.system() == "Darwin":
|
|
||||||
lib = install_dir + '/lib/libtesseract.{}.dylib'.format(required_version)
|
|
||||||
if not os.path.exists(lib):
|
|
||||||
return False
|
|
||||||
c.symlink(lib, install_dir + '/lib/libtesseract.dylib')
|
|
||||||
else:
|
|
||||||
lib = install_dir + '/lib/libtesseract.so.{}'.format(required_version)
|
|
||||||
if not os.path.exists(lib):
|
|
||||||
return False
|
|
||||||
c.symlink(lib, install_dir + '/lib/libtesseract.so')
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
if check_existing() and not 'FORCE' in os.environ:
|
|
||||||
c.print('>> Using cached')
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
archive = 'tesseract-' + os.path.basename(url)
|
|
||||||
c.download(url, archive)
|
|
||||||
|
|
||||||
src_dir = os.path.abspath('tesseract_src')
|
|
||||||
c.extract(archive, '.')
|
|
||||||
c.symlink(c.get_archive_top_dir(archive), src_dir)
|
|
||||||
|
|
||||||
c.ensure_got_path(install_dir)
|
|
||||||
|
|
||||||
c.recreate_dir(build_dir)
|
|
||||||
os.chdir(build_dir)
|
|
||||||
|
|
||||||
cmake_args = '"{0}" \
|
|
||||||
-DCMAKE_INSTALL_PREFIX="{1}" \
|
|
||||||
-DLeptonica_DIR="{1}/cmake" \
|
|
||||||
-DSW_BUILD=OFF \
|
|
||||||
-DBUILD_TRAINING_TOOLS=OFF \
|
|
||||||
-DBUILD_TESTS=OFF \
|
|
||||||
-DBUILD_SHARED_LIBS=ON \
|
|
||||||
-DDISABLE_CURL=ON \
|
|
||||||
-DDISABLE_ARCHIVE=ON \
|
|
||||||
-DUSE_SYSTEM_ICU=ON \
|
|
||||||
-DENABLE_LTO=ON \
|
|
||||||
-DGRAPHICS_DISABLED=ON \
|
|
||||||
-DDISABLED_LEGACY_ENGINE=ON \
|
|
||||||
'.format(src_dir, install_dir)
|
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version)
|
|
||||||
c.apply_cmd_env(env_cmd)
|
|
||||||
cmake_args += ' ' + c.get_cmake_arch_args(bitness=bitness)
|
|
||||||
|
|
||||||
c.set_make_threaded()
|
|
||||||
c.run('cmake {}'.format(cmake_args))
|
|
||||||
|
|
||||||
c.run('cmake --build . --config {}'.format(build_type_flag))
|
|
||||||
c.run('cmake --build . --target install --config {}'.format(build_type_flag))
|
|
||||||
|
|
||||||
with open(cache_file, 'w') as f:
|
|
||||||
f.write(cache_file_data)
|
|
||||||
|
|
||||||
if not check_existing(): # add suffix
|
|
||||||
c.print('>> Build failed')
|
|
||||||
exit(1)
|
|
@ -1,19 +0,0 @@
|
|||||||
import common as c
|
|
||||||
from config import *
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
tag = os.environ.get('TAG', '')
|
|
||||||
artifact_name = '{}-{}{}.dmg'.format(app_name, app_version, tag)
|
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == 'artifact_name': # subcommand
|
|
||||||
c.print(artifact_name)
|
|
||||||
exit(0)
|
|
||||||
artifact_path = os.path.abspath(artifact_name)
|
|
||||||
|
|
||||||
c.print('>> Making mac deploy')
|
|
||||||
|
|
||||||
os.chdir(build_dir)
|
|
||||||
build_target = build_dir + '/' + target_name + '.app'
|
|
||||||
built_dmg = build_dir + '/' + target_name + '.dmg'
|
|
||||||
c.run('{}/bin/macdeployqt "{}" -dmg'.format(qt_dir, build_target))
|
|
||||||
os.rename(built_dmg, artifact_path)
|
|
@ -1,43 +0,0 @@
|
|||||||
import os
|
|
||||||
import platform
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
here = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
|
|
||||||
def r_out(script, args):
|
|
||||||
return subprocess.run([sys.executable, os.path.join(here, script)] + args, check=True, stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
|
|
||||||
|
|
||||||
|
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == 'artifact_name':
|
|
||||||
artifact_name = ''
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
artifact_name = r_out('appimage.py', ['artifact_name'])
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
artifact_name = r_out('windeploy.py', ['artifact_name'])
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
artifact_name = r_out('macdeploy.py', ['artifact_name'])
|
|
||||||
print(artifact_name)
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
def r(script):
|
|
||||||
return subprocess.run([sys.executable, os.path.join(here, script)], check=True)
|
|
||||||
|
|
||||||
|
|
||||||
r('get_qt.py')
|
|
||||||
r('get_qt_ssl.py')
|
|
||||||
r('get_leptonica.py')
|
|
||||||
r('get_tesseract.py')
|
|
||||||
|
|
||||||
r('get_hunspell.py')
|
|
||||||
r('test.py')
|
|
||||||
r('build.py')
|
|
||||||
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
r('appimage.py')
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
r('windeploy.py')
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
r('macdeploy.py')
|
|
@ -1,84 +0,0 @@
|
|||||||
import common as c
|
|
||||||
from config import app_version
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import io
|
|
||||||
import urllib
|
|
||||||
import platform
|
|
||||||
from paramiko import SSHClient, WarningPolicy, RSAKey, SSHException
|
|
||||||
|
|
||||||
files = sys.argv[1:]
|
|
||||||
c.print('>> Uploading artifacts to sourceforge {}'.format(files))
|
|
||||||
|
|
||||||
for f in files:
|
|
||||||
if not os.path.exists(f):
|
|
||||||
c.print('>> File "{}" not exists. Exiting'.format(f))
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
pkey_name = 'SF_PKEY'
|
|
||||||
if not pkey_name in os.environ:
|
|
||||||
c.print('>> No sf pkey set. Exiting')
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
api_name = 'SF_API'
|
|
||||||
if not api_name in os.environ:
|
|
||||||
c.print('>> No sf api set. Exiting')
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
pkey_data = io.StringIO(os.environ[pkey_name])
|
|
||||||
pkey = None
|
|
||||||
try:
|
|
||||||
pkey = RSAKey.from_private_key(pkey_data)
|
|
||||||
except SSHException as e:
|
|
||||||
c.print('>> Sf pkey error "{}". Exiting'.format(e))
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
ssh = SSHClient()
|
|
||||||
ssh.set_missing_host_key_policy(WarningPolicy())
|
|
||||||
ssh.connect('frs.sourceforge.net', username='onemoregres', pkey=pkey)
|
|
||||||
sftp = ssh.open_sftp()
|
|
||||||
target_path = 'bin/v' + app_version
|
|
||||||
try:
|
|
||||||
remote_path = '/home/frs/project/screen-translator/'
|
|
||||||
for part in target_path.split('/'):
|
|
||||||
existing = sftp.listdir(remote_path)
|
|
||||||
remote_path = remote_path + part + '/'
|
|
||||||
if not part in existing:
|
|
||||||
sftp.mkdir(remote_path)
|
|
||||||
existing = sftp.listdir(remote_path)
|
|
||||||
for f in files:
|
|
||||||
file_name = os.path.basename(f)
|
|
||||||
if file_name in existing:
|
|
||||||
c.print('>> File "{}" already exists. Removing'.format(file_name))
|
|
||||||
sftp.remove(remote_path + file_name)
|
|
||||||
sftp.put(f, remote_path + file_name)
|
|
||||||
except IOError as err:
|
|
||||||
c.print('>> SFTP error "{}". Exiting'.format(err))
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
sftp.close()
|
|
||||||
ssh.close()
|
|
||||||
|
|
||||||
api_key = os.environ[api_name]
|
|
||||||
base_url = 'https://sourceforge.net/projects/screen-translator/files/' + target_path
|
|
||||||
for f in files:
|
|
||||||
file_name = os.path.basename(f)
|
|
||||||
url = base_url + '/' + file_name
|
|
||||||
|
|
||||||
data = {'api_key': api_key}
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
data['default'] = 'windows'
|
|
||||||
elif platform.system() == "Darwin":
|
|
||||||
data['default'] = 'mac'
|
|
||||||
else:
|
|
||||||
data['default'] = 'linux'
|
|
||||||
|
|
||||||
raw_data = urllib.parse.urlencode(data).encode('utf-8')
|
|
||||||
try:
|
|
||||||
request = urllib.request.Request(
|
|
||||||
url, method='PUT', headers={"Accept": "application/json"}, data=raw_data)
|
|
||||||
with urllib.request.urlopen(request) as r:
|
|
||||||
pass
|
|
||||||
c.print('>> Updated info for "{}"'.format(url), r.status, r.reason)
|
|
||||||
except Exception as e:
|
|
||||||
c.print('>> Update info for "{}" failed {}'.format(url, e))
|
|
@ -1,25 +0,0 @@
|
|||||||
import common as c
|
|
||||||
from config import *
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
import glob
|
|
||||||
|
|
||||||
c.print('>> Testing {} on {}'.format(app_name, os_name))
|
|
||||||
|
|
||||||
c.add_to_path(os.path.abspath(qt_dir + '/bin'))
|
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version)
|
|
||||||
c.apply_cmd_env(env_cmd)
|
|
||||||
|
|
||||||
c.recreate_dir(build_dir)
|
|
||||||
os.chdir(build_dir)
|
|
||||||
|
|
||||||
c.set_make_threaded()
|
|
||||||
c.run('qmake {} "{}"'.format(os.environ.get('QMAKE_FLAGS', ''), test_pro_file))
|
|
||||||
make_cmd = c.get_make_cmd()
|
|
||||||
c.run(make_cmd)
|
|
||||||
|
|
||||||
for file in glob.glob('./**/tests*', recursive=True):
|
|
||||||
print(file)
|
|
||||||
c.run(file, silent=False)
|
|
@ -1,57 +0,0 @@
|
|||||||
import common as c
|
|
||||||
from config import *
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import shutil
|
|
||||||
from glob import glob
|
|
||||||
|
|
||||||
tag = os.environ.get('TAG', '')
|
|
||||||
artifact_name = '{}-{}{}-{}.zip'.format(app_name, app_version, tag, os_name)
|
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == 'artifact_name': # subcommand
|
|
||||||
c.print(artifact_name)
|
|
||||||
exit(0)
|
|
||||||
artifact_path = os.path.abspath(artifact_name)
|
|
||||||
|
|
||||||
c.print('>> Making win deploy')
|
|
||||||
|
|
||||||
if os_name.startswith('win'):
|
|
||||||
env_cmd = c.get_msvc_env_cmd(bitness=bitness, msvc_version=msvc_version)
|
|
||||||
c.apply_cmd_env(env_cmd)
|
|
||||||
|
|
||||||
pwd = os.getcwd()
|
|
||||||
os.chdir(build_dir)
|
|
||||||
|
|
||||||
install_dir = os.path.abspath(app_name)
|
|
||||||
c.recreate_dir(install_dir)
|
|
||||||
|
|
||||||
c.run('nmake INSTALL_ROOT="{0}" DESTDIR="{0}" install'.format(install_dir))
|
|
||||||
c.run('{}/bin/windeployqt.exe "{}"'.format(qt_dir, install_dir))
|
|
||||||
|
|
||||||
vcredist_for_ssl_url = ''
|
|
||||||
vcredist_for_ssl_file = ''
|
|
||||||
if bitness == '32':
|
|
||||||
vcredist_for_ssl_url = 'https://download.microsoft.com/download/C/6/D/C6D0FD4E-9E53-4897-9B91-836EBA2AACD3/vcredist_x86.exe'
|
|
||||||
vcredist_for_ssl_file = 'vc_redist.x86.2010.exe'
|
|
||||||
else:
|
|
||||||
vcredist_for_ssl_url = 'https://download.microsoft.com/download/A/8/0/A80747C3-41BD-45DF-B505-E9710D2744E0/vcredist_x64.exe'
|
|
||||||
vcredist_for_ssl_file = 'vc_redist.x64.2010.exe'
|
|
||||||
|
|
||||||
c.download(vcredist_for_ssl_url, os.path.join(install_dir, vcredist_for_ssl_file))
|
|
||||||
|
|
||||||
libs_dir = os.path.join(dependencies_dir, 'bin')
|
|
||||||
for file in os.scandir(libs_dir):
|
|
||||||
if file.is_file(follow_symlinks=False) and file.name.endswith('.dll'):
|
|
||||||
full_name = os.path.join(libs_dir, file.name)
|
|
||||||
c.print('>> Copying {} to {}'.format(full_name, install_dir))
|
|
||||||
shutil.copy(full_name, install_dir)
|
|
||||||
|
|
||||||
for f in glob(ssl_dir + '/bin/*.dll'):
|
|
||||||
c.print('>> Copying {} to {}'.format(f, install_dir))
|
|
||||||
shutil.copy(f, install_dir)
|
|
||||||
|
|
||||||
open(os.path.join(install_dir, 'qt.conf'), 'a').close() # fix for non-latin paths
|
|
||||||
|
|
||||||
c.archive(c.get_folder_files(os.path.relpath(install_dir)), artifact_path)
|
|
||||||
|
|
||||||
bin_path = install_dir + '\\' + bin_name + '.exe'
|
|
||||||
c.print('>> Md5 {} {}'.format(bin_path, c.md5sum(bin_path)))
|
|
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 641 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 894 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 3.9 KiB |
@ -1,13 +0,0 @@
|
|||||||
FROM alpine:latest
|
|
||||||
|
|
||||||
ADD entrypoint.sh /entrypoint.sh
|
|
||||||
|
|
||||||
RUN \
|
|
||||||
addgroup -g 1200 -S app && \
|
|
||||||
adduser -G app -u 1200 -S app && \
|
|
||||||
apk add --upgrade --no-cache git zip && \
|
|
||||||
chmod +x /entrypoint.sh
|
|
||||||
|
|
||||||
USER app
|
|
||||||
VOLUME [ "/git", "/packed" ]
|
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
|
@ -1,15 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
services:
|
|
||||||
mirror:
|
|
||||||
build: .
|
|
||||||
image: gres/st_mirror
|
|
||||||
restart: always
|
|
||||||
container_name: st_mirror
|
|
||||||
logging:
|
|
||||||
driver: json-file
|
|
||||||
options:
|
|
||||||
max-size: "10m"
|
|
||||||
max-file: "5"
|
|
||||||
volumes:
|
|
||||||
- ./git:/git
|
|
||||||
- ./packed:/packed
|
|
@ -1,51 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
pack() {
|
|
||||||
mkdir -p "$2"
|
|
||||||
for f in $(ls $1); do
|
|
||||||
source="$1/$f"
|
|
||||||
target="$2/$f"
|
|
||||||
if [ -d "$source" ]; then
|
|
||||||
pack "$source" "$target"
|
|
||||||
elif [ -f "$source" ]; then
|
|
||||||
if [ "$target.zip" -nt "$source" ]; then
|
|
||||||
echo "$source is up to date"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
tmp=/tmp/archive.zip
|
|
||||||
echo "packing $source -> $tmp"
|
|
||||||
ls -l "$source"
|
|
||||||
zip -9 -j "$tmp" "$source"
|
|
||||||
|
|
||||||
echo "moving $tmp -> $target.zip"
|
|
||||||
mv "$tmp" "$target.zip"
|
|
||||||
chmod 444 "$target.zip"
|
|
||||||
ls -l "$target.zip"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
mirror() {
|
|
||||||
cur="$(pwd)"
|
|
||||||
url="$1"
|
|
||||||
dir="$2"
|
|
||||||
git_dir="/git/$dir"
|
|
||||||
pack_dir="/packed/$dir"
|
|
||||||
echo $url $git_dir $pack_dir
|
|
||||||
if [ -d $git_dir ]; then
|
|
||||||
echo "fetching"
|
|
||||||
cd $git_dir && git fetch --depth=1 origin master
|
|
||||||
else
|
|
||||||
echo "cloning"
|
|
||||||
git clone --depth=1 --single-branch "$url" $git_dir
|
|
||||||
fi
|
|
||||||
echo "packing"
|
|
||||||
pack "$git_dir" "$pack_dir"
|
|
||||||
}
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
mirror 'git://anongit.freedesktop.org/libreoffice/dictionaries' 'dictionaries'
|
|
||||||
mirror 'https://github.com/tesseract-ocr/tessdata_best.git' 'tessdata_best'
|
|
||||||
echo "sleeping"
|
|
||||||
sleep 6h
|
|
||||||
done
|
|
@ -1,9 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Comment=OCR and translation tool
|
|
||||||
Exec=screen-translator
|
|
||||||
GenericName=Screen Translator
|
|
||||||
Name=ScreenTranslator
|
|
||||||
Icon=screentranslator
|
|
||||||
Terminal=false
|
|
||||||
Type=Application
|
|
||||||
Categories=Utility;Core;Qt;
|
|
1578
share/uncrustify.cfg
@ -1,106 +0,0 @@
|
|||||||
import sys
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
def parse_language_names():
|
|
||||||
root = os.path.abspath(os.path.basename(__file__) + '/../../..')
|
|
||||||
lines = []
|
|
||||||
with open(root + '/src/languagecodes.cpp', 'r') as d:
|
|
||||||
lines = d.readlines()
|
|
||||||
result = {}
|
|
||||||
for line in lines:
|
|
||||||
if line.startswith('//'):
|
|
||||||
continue
|
|
||||||
all = re.findall(r'"(.*?)"', line)
|
|
||||||
if len(all) != 6:
|
|
||||||
continue
|
|
||||||
result[all[2]] = all[5]
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print("Usage:", sys.argv[0], "<dict_dir> [<download_url>]")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
dict_dir = sys.argv[1]
|
|
||||||
|
|
||||||
download_url = "https://cgit.freedesktop.org/libreoffice/dictionaries/plain"
|
|
||||||
if len(sys.argv) > 2:
|
|
||||||
download_url = sys.argv[2]
|
|
||||||
|
|
||||||
mirror_url = "https://translator.gres.biz/resources/dictionaries"
|
|
||||||
|
|
||||||
language_names = parse_language_names()
|
|
||||||
|
|
||||||
preferred = ['sr.aff', 'sv_FI.aff',
|
|
||||||
'en_US.aff', 'de_DE_frami.aff', 'nb_NO.aff']
|
|
||||||
|
|
||||||
files = {}
|
|
||||||
it = os.scandir(dict_dir)
|
|
||||||
for d in it:
|
|
||||||
if not d.is_dir():
|
|
||||||
continue
|
|
||||||
|
|
||||||
lang = d.name
|
|
||||||
if '_' in lang:
|
|
||||||
lang = lang[0:lang.index('_')]
|
|
||||||
|
|
||||||
affs = []
|
|
||||||
fit = os.scandir(os.path.join(dict_dir, d.name))
|
|
||||||
for f in fit:
|
|
||||||
if not f.is_file or not f.name.endswith('.aff'):
|
|
||||||
continue
|
|
||||||
affs.append(f.name)
|
|
||||||
|
|
||||||
aff = ''
|
|
||||||
if len(affs) == 0:
|
|
||||||
continue
|
|
||||||
if len(affs) == 1:
|
|
||||||
aff = affs[0]
|
|
||||||
else:
|
|
||||||
for p in preferred:
|
|
||||||
if p in affs:
|
|
||||||
aff = p
|
|
||||||
break
|
|
||||||
|
|
||||||
if len(aff) == 0:
|
|
||||||
print('no aff for', lang, affs)
|
|
||||||
continue
|
|
||||||
|
|
||||||
aff = os.path.join(d.name, aff)
|
|
||||||
dic = aff[:aff.rindex('.')] + '.dic'
|
|
||||||
if not os.path.exists(os.path.join(dict_dir, dic)):
|
|
||||||
print('no dic exists', dic)
|
|
||||||
|
|
||||||
files[lang] = [aff, dic]
|
|
||||||
|
|
||||||
|
|
||||||
print(',"correction": {')
|
|
||||||
comma = ''
|
|
||||||
unknown_names = []
|
|
||||||
for lang in sorted(files.keys()):
|
|
||||||
file_names = files[lang]
|
|
||||||
if not lang in language_names:
|
|
||||||
unknown_names.append(lang)
|
|
||||||
continue
|
|
||||||
lang_name = language_names[lang]
|
|
||||||
print(' {}"{}":{{"files":['.format(comma, lang_name))
|
|
||||||
comma = ', '
|
|
||||||
lang_comma = ''
|
|
||||||
for file_name in file_names:
|
|
||||||
git_cmd = ['git', 'log', '-1', '--pretty=format:%cI', file_name]
|
|
||||||
date = subprocess.run(git_cmd, cwd=dict_dir, universal_newlines=True,
|
|
||||||
stdout=subprocess.PIPE, check=True).stdout
|
|
||||||
size = os.path.getsize(os.path.join(dict_dir, file_name))
|
|
||||||
installed = lang + file_name[file_name.index('/'):]
|
|
||||||
mirror = ',"' + mirror_url + '/' + file_name + \
|
|
||||||
'.zip"' if len(mirror_url) > 0 else ''
|
|
||||||
print(' {}{{"url":["{}/{}"{}], "path":"$hunspell$/{}", "date":"{}", "size":{}}}'.format(
|
|
||||||
lang_comma, download_url, file_name, mirror, installed, date, size))
|
|
||||||
lang_comma = ','
|
|
||||||
print(' ]}')
|
|
||||||
print('}')
|
|
||||||
|
|
||||||
print('unknown names', unknown_names)
|
|
@ -1,68 +0,0 @@
|
|||||||
import sys
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
def parse_language_names():
|
|
||||||
root = os.path.abspath(os.path.basename(__file__) + '/../../..')
|
|
||||||
lines = []
|
|
||||||
with open(root + '/src/languagecodes.cpp', 'r') as f:
|
|
||||||
lines = f.readlines()
|
|
||||||
result = {}
|
|
||||||
for line in lines:
|
|
||||||
all = re.findall(r'"(.*?)"', line)
|
|
||||||
if len(all) != 6:
|
|
||||||
continue
|
|
||||||
result[all[3]] = all[5]
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print("Usage:", sys.argv[0], "<tessdata_dir> [<download_url>]")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
tessdata_dir = sys.argv[1]
|
|
||||||
|
|
||||||
download_url = "https://github.com/tesseract-ocr/tessdata_best/raw/master"
|
|
||||||
if len(sys.argv) > 2:
|
|
||||||
download_url = sys.argv[2]
|
|
||||||
|
|
||||||
mirror_url = "https://translator.gres.biz/resources/tessdata_best"
|
|
||||||
|
|
||||||
language_names = parse_language_names()
|
|
||||||
|
|
||||||
files = {}
|
|
||||||
it = os.scandir(tessdata_dir)
|
|
||||||
for f in it:
|
|
||||||
if not f.is_file() or f.name in ["LICENSE", "README.md"]:
|
|
||||||
continue
|
|
||||||
name = f.name[:f.name.index('.')]
|
|
||||||
if len(name) == 0:
|
|
||||||
continue
|
|
||||||
files.setdefault(name, []).append(f.name)
|
|
||||||
|
|
||||||
print(',"recognizers": {')
|
|
||||||
comma = ''
|
|
||||||
unknown_names = []
|
|
||||||
for name in sorted(files.keys()):
|
|
||||||
file_names = files[name]
|
|
||||||
if not name in language_names:
|
|
||||||
unknown_names.append(name)
|
|
||||||
else:
|
|
||||||
name = language_names[name]
|
|
||||||
print(' {}"{}":{{"files":['.format(comma, name))
|
|
||||||
comma = ', '
|
|
||||||
for file_name in file_names:
|
|
||||||
git_cmd = ['git', 'log', '-1', '--pretty=format:%cI', file_name]
|
|
||||||
date = subprocess.run(git_cmd, cwd=tessdata_dir, universal_newlines=True,
|
|
||||||
stdout=subprocess.PIPE, check=True).stdout
|
|
||||||
size = os.path.getsize(os.path.join(tessdata_dir, file_name))
|
|
||||||
mirror = ',"' + mirror_url + '/' + file_name + \
|
|
||||||
'.zip"' if len(mirror_url) > 0 else ''
|
|
||||||
print(' {{"url":["{}/{}"{}], "path":"$tessdata$/{}", "date":"{}", "size":{}}}'.format(
|
|
||||||
download_url, file_name, mirror, file_name, date, size))
|
|
||||||
print(' ]}')
|
|
||||||
print('}')
|
|
||||||
|
|
||||||
print('unknown names', unknown_names)
|
|
@ -1,38 +0,0 @@
|
|||||||
import sys
|
|
||||||
import os
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
download_url = "https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master"
|
|
||||||
|
|
||||||
if len(sys.argv) > 1:
|
|
||||||
download_url = sys.argv[1]
|
|
||||||
|
|
||||||
subdir = 'translators'
|
|
||||||
root = os.path.abspath(os.path.basename(__file__) + '/../../..')
|
|
||||||
translators_dir = root + '/' + subdir
|
|
||||||
|
|
||||||
files = {}
|
|
||||||
it = os.scandir(translators_dir)
|
|
||||||
for f in it:
|
|
||||||
if not f.is_file() or not f.name.endswith('.js'):
|
|
||||||
continue
|
|
||||||
name = f.name[:f.name.index('.')]
|
|
||||||
files[name] = f.name
|
|
||||||
|
|
||||||
print(',"translators":{')
|
|
||||||
comma = ''
|
|
||||||
for name in sorted(files.keys()):
|
|
||||||
file_name = files[name]
|
|
||||||
print(' {}"{}": {{"files":['.format(comma, name))
|
|
||||||
comma = ','
|
|
||||||
md5 = hashlib.md5()
|
|
||||||
size = 0
|
|
||||||
with open(os.path.join(translators_dir, file_name), 'rb') as f:
|
|
||||||
data = f.read()
|
|
||||||
size = len(data)
|
|
||||||
md5.update(data)
|
|
||||||
print(' {{"url":"{}/{}", "path":"$translators$/{}", "md5":"{}", "size":{}}}'.format(
|
|
||||||
download_url, subdir + '/' + file_name, file_name,
|
|
||||||
md5.hexdigest(), size))
|
|
||||||
print(' ]}')
|
|
||||||
print('}')
|
|
@ -1,71 +0,0 @@
|
|||||||
#include "capturearea.h"
|
|
||||||
#include "settings.h"
|
|
||||||
#include "task.h"
|
|
||||||
|
|
||||||
CaptureArea::CaptureArea(const QRect &rect, const Settings &settings)
|
|
||||||
: rect_(rect)
|
|
||||||
, doTranslation_(settings.doTranslation)
|
|
||||||
, useHunspell_(settings.useHunspell)
|
|
||||||
, sourceLanguage_(settings.sourceLanguage)
|
|
||||||
, targetLanguage_(settings.targetLanguage)
|
|
||||||
, translators_(settings.translators)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskPtr CaptureArea::task(const QPixmap &pixmap,
|
|
||||||
const QPoint &pixmapOffset) const
|
|
||||||
{
|
|
||||||
if (pixmap.isNull() || !isValid())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
auto task = std::make_shared<Task>();
|
|
||||||
task->generation = generation_;
|
|
||||||
task->useHunspell = useHunspell_;
|
|
||||||
task->captured = pixmap.copy(rect_);
|
|
||||||
task->capturePoint = pixmapOffset + rect_.topLeft();
|
|
||||||
task->sourceLanguage = sourceLanguage_;
|
|
||||||
if (task->sourceLanguage.isEmpty())
|
|
||||||
task->error += QObject::tr("No source language set");
|
|
||||||
|
|
||||||
if (doTranslation_ && !translators_.isEmpty()) {
|
|
||||||
task->targetLanguage = targetLanguage_;
|
|
||||||
task->translators = translators_;
|
|
||||||
if (task->targetLanguage.isEmpty()) {
|
|
||||||
task->error += (task->error.isEmpty() ? "" : ", ") +
|
|
||||||
QObject::tr("No target language set");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureArea::setGeneration(uint generation)
|
|
||||||
{
|
|
||||||
generation_ = generation;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CaptureArea::isValid() const
|
|
||||||
{
|
|
||||||
return !(rect_.width() < 3 || rect_.height() < 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QRect &CaptureArea::rect() const
|
|
||||||
{
|
|
||||||
return rect_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureArea::setRect(const QRect &rect)
|
|
||||||
{
|
|
||||||
rect_ = rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString CaptureArea::toolTip() const
|
|
||||||
{
|
|
||||||
return doTranslation_ ? sourceLanguage_ + "->" + targetLanguage_
|
|
||||||
: sourceLanguage_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CaptureArea::isLocked() const
|
|
||||||
{
|
|
||||||
return isLocked_;
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "stfwd.h"
|
|
||||||
|
|
||||||
#include <QRect>
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
class QPixmap;
|
|
||||||
|
|
||||||
class CaptureArea
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CaptureArea(const QRect& rect, const Settings& settings);
|
|
||||||
TaskPtr task(const QPixmap& pixmap, const QPoint& pixmapOffset) const;
|
|
||||||
|
|
||||||
void setGeneration(uint generation);
|
|
||||||
bool isValid() const;
|
|
||||||
bool isLocked() const;
|
|
||||||
const QRect& rect() const;
|
|
||||||
void setRect(const QRect& rect);
|
|
||||||
|
|
||||||
QString toolTip() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class CaptureAreaEditor;
|
|
||||||
|
|
||||||
Generation generation_{};
|
|
||||||
QRect rect_;
|
|
||||||
bool doTranslation_;
|
|
||||||
bool isLocked_{false};
|
|
||||||
bool useHunspell_{false};
|
|
||||||
LanguageId sourceLanguage_;
|
|
||||||
LanguageId targetLanguage_;
|
|
||||||
QStringList translators_;
|
|
||||||
};
|
|
@ -1,86 +0,0 @@
|
|||||||
#include "captureareaeditor.h"
|
|
||||||
#include "capturearea.h"
|
|
||||||
#include "captureareaselector.h"
|
|
||||||
#include "commonmodels.h"
|
|
||||||
#include "languagecodes.h"
|
|
||||||
|
|
||||||
#include <QCheckBox>
|
|
||||||
#include <QComboBox>
|
|
||||||
#include <QGridLayout>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QPushButton>
|
|
||||||
|
|
||||||
CaptureAreaEditor::CaptureAreaEditor(const CommonModels &models,
|
|
||||||
QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
, doTranslation_(new QCheckBox(tr("Translate:"), this))
|
|
||||||
, isLocked_(new QCheckBox(tr("Save (can capture via hotkey)"), this))
|
|
||||||
, useHunspell_(new QCheckBox(tr("Use auto corrections"), this))
|
|
||||||
, sourceLanguage_(new QComboBox(this))
|
|
||||||
, targetLanguage_(new QComboBox(this))
|
|
||||||
{
|
|
||||||
setCursor(Qt::CursorShape::ArrowCursor);
|
|
||||||
|
|
||||||
auto layout = new QGridLayout(this);
|
|
||||||
auto row = 0;
|
|
||||||
layout->addWidget(new QLabel(tr("Recognize:")), row, 0);
|
|
||||||
layout->addWidget(sourceLanguage_, row, 1);
|
|
||||||
auto swapLanguages = new QPushButton(tr("⇵"));
|
|
||||||
layout->addWidget(swapLanguages, row, 2, 2, 1);
|
|
||||||
|
|
||||||
++row;
|
|
||||||
layout->addWidget(doTranslation_, row, 0);
|
|
||||||
layout->addWidget(targetLanguage_, row, 1);
|
|
||||||
|
|
||||||
++row;
|
|
||||||
layout->addWidget(useHunspell_, row, 0, 1, 2);
|
|
||||||
|
|
||||||
++row;
|
|
||||||
layout->addWidget(isLocked_, row, 0, 1, 2);
|
|
||||||
|
|
||||||
sourceLanguage_->setModel(models.sourceLanguageModel());
|
|
||||||
targetLanguage_->setModel(models.targetLanguageModel());
|
|
||||||
targetLanguage_->setEnabled(doTranslation_->isChecked());
|
|
||||||
|
|
||||||
swapLanguages->setFlat(true);
|
|
||||||
{
|
|
||||||
auto font = swapLanguages->font();
|
|
||||||
font.setPointSize(std::max(font.pointSize() * 2, 16));
|
|
||||||
swapLanguages->setFont(font);
|
|
||||||
}
|
|
||||||
swapLanguages->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
|
||||||
|
|
||||||
connect(doTranslation_, &QCheckBox::toggled, //
|
|
||||||
targetLanguage_, &QComboBox::setEnabled);
|
|
||||||
connect(swapLanguages, &QPushButton::clicked, //
|
|
||||||
this, &CaptureAreaEditor::swapLanguages);
|
|
||||||
}
|
|
||||||
|
|
||||||
CaptureAreaEditor::~CaptureAreaEditor() = default;
|
|
||||||
|
|
||||||
void CaptureAreaEditor::swapLanguages()
|
|
||||||
{
|
|
||||||
const auto target = targetLanguage_->currentText();
|
|
||||||
targetLanguage_->setCurrentText(sourceLanguage_->currentText());
|
|
||||||
sourceLanguage_->setCurrentText(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaEditor::set(const CaptureArea &area)
|
|
||||||
{
|
|
||||||
isLocked_->setChecked(area.isLocked());
|
|
||||||
useHunspell_->setChecked(area.useHunspell_);
|
|
||||||
doTranslation_->setChecked(area.doTranslation_);
|
|
||||||
sourceLanguage_->setCurrentText(LanguageCodes::name(area.sourceLanguage_));
|
|
||||||
targetLanguage_->setCurrentText(LanguageCodes::name(area.targetLanguage_));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaEditor::apply(CaptureArea &area) const
|
|
||||||
{
|
|
||||||
area.isLocked_ = isLocked_->isChecked();
|
|
||||||
area.useHunspell_ = useHunspell_->isChecked();
|
|
||||||
area.doTranslation_ = doTranslation_->isChecked();
|
|
||||||
area.sourceLanguage_ =
|
|
||||||
LanguageCodes::idForName(sourceLanguage_->currentText());
|
|
||||||
area.targetLanguage_ =
|
|
||||||
LanguageCodes::idForName(targetLanguage_->currentText());
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "stfwd.h"
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
class QCheckBox;
|
|
||||||
class QComboBox;
|
|
||||||
|
|
||||||
class CaptureAreaEditor : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit CaptureAreaEditor(const CommonModels& models,
|
|
||||||
QWidget* parent = nullptr);
|
|
||||||
~CaptureAreaEditor();
|
|
||||||
|
|
||||||
void set(const CaptureArea& area);
|
|
||||||
void apply(CaptureArea& area) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void swapLanguages();
|
|
||||||
|
|
||||||
QCheckBox* doTranslation_;
|
|
||||||
QCheckBox* isLocked_;
|
|
||||||
QCheckBox* useHunspell_;
|
|
||||||
QComboBox* sourceLanguage_;
|
|
||||||
QComboBox* targetLanguage_;
|
|
||||||
};
|
|
@ -1,355 +0,0 @@
|
|||||||
#include "captureareaselector.h"
|
|
||||||
#include "capturearea.h"
|
|
||||||
#include "captureareaeditor.h"
|
|
||||||
#include "capturer.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "geometryutils.h"
|
|
||||||
#include "settings.h"
|
|
||||||
|
|
||||||
#include <QMenu>
|
|
||||||
#include <QMouseEvent>
|
|
||||||
#include <QPainter>
|
|
||||||
|
|
||||||
static bool locked(const std::shared_ptr<CaptureArea> &area)
|
|
||||||
{
|
|
||||||
return area->isLocked();
|
|
||||||
}
|
|
||||||
static bool notLocked(const std::shared_ptr<CaptureArea> &area)
|
|
||||||
{
|
|
||||||
return !area->isLocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
CaptureAreaSelector::CaptureAreaSelector(Capturer &capturer,
|
|
||||||
const Settings &settings,
|
|
||||||
const CommonModels &models,
|
|
||||||
const QPixmap &pixmap,
|
|
||||||
const QPoint &pixmapOffset)
|
|
||||||
: capturer_(capturer)
|
|
||||||
, settings_(settings)
|
|
||||||
, pixmap_(pixmap)
|
|
||||||
, pixmapOffset_(pixmapOffset)
|
|
||||||
, editor_(std::make_unique<CaptureAreaEditor>(models, this))
|
|
||||||
, contextMenu_(new QMenu(this))
|
|
||||||
{
|
|
||||||
setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint |
|
|
||||||
Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
|
|
||||||
setMouseTracking(true);
|
|
||||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
|
||||||
|
|
||||||
help_ = tr(R"(Right click on selection - customize
|
|
||||||
Left click on selection - process
|
|
||||||
Enter - process all selections
|
|
||||||
Esc - cancel
|
|
||||||
Ctrl - keep selecting)");
|
|
||||||
|
|
||||||
{
|
|
||||||
auto action = contextMenu_->addAction(tr("Capture all"));
|
|
||||||
connect(action, &QAction::triggered, //
|
|
||||||
this, &CaptureAreaSelector::captureAll);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto action = contextMenu_->addAction(tr("Cancel"));
|
|
||||||
connect(action, &QAction::triggered, //
|
|
||||||
this, &CaptureAreaSelector::cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CaptureAreaSelector::~CaptureAreaSelector() = default;
|
|
||||||
|
|
||||||
void CaptureAreaSelector::activate()
|
|
||||||
{
|
|
||||||
setGeometry(QRect(pixmapOffset_, pixmap_.size()));
|
|
||||||
show();
|
|
||||||
activateWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CaptureAreaSelector::hasLocked() const
|
|
||||||
{
|
|
||||||
const auto it = std::find_if(areas_.cbegin(), areas_.cend(), locked);
|
|
||||||
return it != areas_.cend();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::captureLocked()
|
|
||||||
{
|
|
||||||
SOFT_ASSERT(hasLocked(), return );
|
|
||||||
++generation_;
|
|
||||||
for (auto &area : areas_) {
|
|
||||||
if (area->isLocked())
|
|
||||||
capture(*area, generation_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::capture(CaptureArea &area, uint generation)
|
|
||||||
{
|
|
||||||
area.setGeneration(generation);
|
|
||||||
capturer_.selected(area);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::captureAll()
|
|
||||||
{
|
|
||||||
SOFT_ASSERT(!areas_.empty(), return );
|
|
||||||
++generation_;
|
|
||||||
for (auto &area : areas_) capture(*area, generation_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::cancel()
|
|
||||||
{
|
|
||||||
capturer_.canceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::updateCursorShape(const QPoint &pos)
|
|
||||||
{
|
|
||||||
const auto set = [this](Qt::CursorShape shape) {
|
|
||||||
const auto current = cursor().shape();
|
|
||||||
if (current != shape)
|
|
||||||
setCursor(shape);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (areas_.empty()) {
|
|
||||||
set(Qt::CrossCursor);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &area : areas_) {
|
|
||||||
if (area->rect().contains(pos)) {
|
|
||||||
set(Qt::CursorShape::PointingHandCursor);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set(Qt::CrossCursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::setScreenRects(const std::vector<QRect> &screens)
|
|
||||||
{
|
|
||||||
auto helpRect = fontMetrics().boundingRect({}, 0, help_);
|
|
||||||
helpRect.setSize(helpRect.size() * 1.4);
|
|
||||||
|
|
||||||
helpRects_.clear();
|
|
||||||
helpRects_.reserve(screens.size());
|
|
||||||
for (const auto &screen : screens) {
|
|
||||||
auto possible = std::vector<QRect>(2, helpRect);
|
|
||||||
possible[0].moveTopLeft(screen.topLeft());
|
|
||||||
possible[1].moveTopRight(screen.topRight());
|
|
||||||
helpRects_.push_back({possible[0], possible});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::updateSettings()
|
|
||||||
{
|
|
||||||
areas_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::paintEvent(QPaintEvent * /*event*/)
|
|
||||||
{
|
|
||||||
QPainter painter(this);
|
|
||||||
painter.drawPixmap(rect(), pixmap_);
|
|
||||||
|
|
||||||
for (const auto &rect : helpRects_) drawHelpRects(painter, rect);
|
|
||||||
|
|
||||||
if (!areas_.empty()) {
|
|
||||||
for (const auto &area : areas_) drawCaptureArea(painter, *area);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (editor_->isVisible()) {
|
|
||||||
painter.setBrush(QBrush(QColor(200, 200, 200, 200)));
|
|
||||||
painter.setPen(Qt::NoPen);
|
|
||||||
painter.drawRect(editor_->geometry());
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto area = CaptureArea(
|
|
||||||
QRect(startSelectPos_, currentSelectPos_).normalized(), settings_);
|
|
||||||
if (!area.isValid())
|
|
||||||
return;
|
|
||||||
drawCaptureArea(painter, area);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CaptureAreaSelector::updateCurrentHelpRects()
|
|
||||||
{
|
|
||||||
const auto cursor = mapFromGlobal(QCursor::pos());
|
|
||||||
auto changed = false;
|
|
||||||
|
|
||||||
for (auto &screenHelp : helpRects_) {
|
|
||||||
if (!screenHelp.current.contains(cursor))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (const auto &screenPossible : screenHelp.possible) {
|
|
||||||
if (screenPossible.contains(cursor))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
screenHelp.current = screenPossible;
|
|
||||||
changed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::drawHelpRects(QPainter &painter,
|
|
||||||
const HelpRect &rect) const
|
|
||||||
{
|
|
||||||
painter.setBrush(QBrush(QColor(200, 200, 200, 200)));
|
|
||||||
painter.setPen(Qt::NoPen);
|
|
||||||
painter.drawRect(rect.current);
|
|
||||||
|
|
||||||
painter.setBrush({});
|
|
||||||
painter.setPen(Qt::black);
|
|
||||||
painter.drawText(rect.current, Qt::AlignCenter, help_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::drawCaptureArea(QPainter &painter,
|
|
||||||
const CaptureArea &area) const
|
|
||||||
{
|
|
||||||
const auto areaRect = area.rect();
|
|
||||||
const auto toolTip = area.toolTip();
|
|
||||||
auto toolTipRect = painter.boundingRect(QRect(), 0, toolTip);
|
|
||||||
toolTipRect.moveTopLeft(areaRect.topLeft() - QPoint(0, toolTipRect.height()));
|
|
||||||
|
|
||||||
painter.setBrush(QBrush(QColor(200, 200, 200, 50)));
|
|
||||||
painter.setPen(Qt::NoPen);
|
|
||||||
painter.drawRect(areaRect);
|
|
||||||
|
|
||||||
painter.setBrush(QBrush(QColor(200, 200, 200, 200)));
|
|
||||||
painter.drawRect(toolTipRect);
|
|
||||||
|
|
||||||
painter.setBrush({});
|
|
||||||
painter.setPen(Qt::red);
|
|
||||||
painter.drawRect(areaRect);
|
|
||||||
|
|
||||||
painter.setPen(Qt::black);
|
|
||||||
painter.drawText(toolTipRect, 0, toolTip);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::showEvent(QShowEvent * /*event*/)
|
|
||||||
{
|
|
||||||
editor_->hide();
|
|
||||||
startSelectPos_ = currentSelectPos_ = QPoint();
|
|
||||||
areas_.erase(std::remove_if(areas_.begin(), areas_.end(), notLocked),
|
|
||||||
areas_.end());
|
|
||||||
updateCursorShape(QCursor::pos());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::hideEvent(QHideEvent * /*event*/)
|
|
||||||
{
|
|
||||||
editor_->hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::keyPressEvent(QKeyEvent *event)
|
|
||||||
{
|
|
||||||
if (event->key() == Qt::Key_Escape) {
|
|
||||||
if (editor_ && editor_->isVisible())
|
|
||||||
applyEditor();
|
|
||||||
cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->key() == Qt::Key_Return) {
|
|
||||||
if (editor_ && editor_->isVisible())
|
|
||||||
applyEditor();
|
|
||||||
|
|
||||||
if (!areas_.empty()) {
|
|
||||||
captureAll();
|
|
||||||
} else {
|
|
||||||
cancel();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
SOFT_ASSERT(editor_, return );
|
|
||||||
if (editor_->isVisible()) {
|
|
||||||
if (editor_->geometry().contains(event->pos()))
|
|
||||||
return;
|
|
||||||
applyEditor();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!areas_.empty()) {
|
|
||||||
for (auto &area : areas_) {
|
|
||||||
if (!area->rect().contains(event->pos()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (event->button() == Qt::LeftButton) {
|
|
||||||
capture(*area, ++generation_);
|
|
||||||
} else if (event->button() == Qt::RightButton) {
|
|
||||||
customize(area);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startSelectPos_.isNull())
|
|
||||||
startSelectPos_ = currentSelectPos_ = event->pos();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::mouseMoveEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
updateCursorShape(QCursor::pos());
|
|
||||||
|
|
||||||
if (startSelectPos_.isNull()) {
|
|
||||||
if (updateCurrentHelpRects())
|
|
||||||
update();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentSelectPos_ = event->pos();
|
|
||||||
updateCurrentHelpRects();
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::mouseReleaseEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
if (startSelectPos_.isNull())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto endPos = event->pos();
|
|
||||||
const auto selection = QRect(startSelectPos_, endPos).normalized();
|
|
||||||
|
|
||||||
startSelectPos_ = currentSelectPos_ = {};
|
|
||||||
|
|
||||||
auto area = CaptureArea(selection, settings_);
|
|
||||||
if (!area.isValid()) { // just a click
|
|
||||||
if (areas_.empty()) {
|
|
||||||
cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event->button() == Qt::RightButton) {
|
|
||||||
contextMenu_->popup(QCursor::pos());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
areas_.emplace_back(std::make_unique<CaptureArea>(area));
|
|
||||||
if (event->button() == Qt::RightButton) {
|
|
||||||
customize(areas_.back());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(event->modifiers() & Qt::ControlModifier))
|
|
||||||
captureAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::customize(const std::shared_ptr<CaptureArea> &area)
|
|
||||||
{
|
|
||||||
SOFT_ASSERT(editor_, return );
|
|
||||||
SOFT_ASSERT(area, return );
|
|
||||||
editor_->set(*area);
|
|
||||||
edited_ = area;
|
|
||||||
editor_->show();
|
|
||||||
const auto topLeft = service::geometry::cornerAtPoint(
|
|
||||||
area->rect().center(), editor_->size(), QRect({}, size()));
|
|
||||||
editor_->move(topLeft);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CaptureAreaSelector::applyEditor()
|
|
||||||
{
|
|
||||||
SOFT_ASSERT(editor_, return );
|
|
||||||
if (!editor_->isVisible() || edited_.expired())
|
|
||||||
return;
|
|
||||||
editor_->apply(*edited_.lock());
|
|
||||||
editor_->hide();
|
|
||||||
update();
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "stfwd.h"
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
class QMenu;
|
|
||||||
|
|
||||||
class CaptureAreaSelector : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
CaptureAreaSelector(Capturer &capturer, const Settings &settings,
|
|
||||||
const CommonModels &models, const QPixmap &pixmap,
|
|
||||||
const QPoint &pixmapOffset);
|
|
||||||
~CaptureAreaSelector();
|
|
||||||
|
|
||||||
void activate();
|
|
||||||
bool hasLocked() const;
|
|
||||||
void captureLocked();
|
|
||||||
void setScreenRects(const std::vector<QRect> &screens);
|
|
||||||
void updateSettings();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void showEvent(QShowEvent *event) override;
|
|
||||||
void hideEvent(QHideEvent *event) override;
|
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
|
||||||
void mouseMoveEvent(QMouseEvent *event) override;
|
|
||||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct HelpRect {
|
|
||||||
QRect current;
|
|
||||||
std::vector<QRect> possible;
|
|
||||||
};
|
|
||||||
void capture(CaptureArea &area, uint generation);
|
|
||||||
void captureAll();
|
|
||||||
void cancel();
|
|
||||||
void updateCursorShape(const QPoint &pos);
|
|
||||||
|
|
||||||
bool updateCurrentHelpRects();
|
|
||||||
void drawHelpRects(QPainter &painter, const HelpRect &rect) const;
|
|
||||||
|
|
||||||
void customize(const std::shared_ptr<CaptureArea> &area);
|
|
||||||
void applyEditor();
|
|
||||||
void drawCaptureArea(QPainter &painter, const CaptureArea &area) const;
|
|
||||||
|
|
||||||
Capturer &capturer_;
|
|
||||||
const Settings &settings_;
|
|
||||||
const QPixmap &pixmap_;
|
|
||||||
const QPoint &pixmapOffset_;
|
|
||||||
Generation generation_{};
|
|
||||||
QPoint startSelectPos_;
|
|
||||||
QPoint currentSelectPos_;
|
|
||||||
QString help_;
|
|
||||||
std::vector<HelpRect> helpRects_;
|
|
||||||
std::vector<std::shared_ptr<CaptureArea>> areas_;
|
|
||||||
std::weak_ptr<CaptureArea> edited_;
|
|
||||||
std::unique_ptr<CaptureAreaEditor> editor_;
|
|
||||||
QMenu *contextMenu_;
|
|
||||||
};
|
|
@ -1,106 +0,0 @@
|
|||||||
#include "capturer.h"
|
|
||||||
#include "capturearea.h"
|
|
||||||
#include "captureareaselector.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "manager.h"
|
|
||||||
#include "settings.h"
|
|
||||||
#include "task.h"
|
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QScreen>
|
|
||||||
|
|
||||||
Capturer::Capturer(Manager &manager, const Settings &settings,
|
|
||||||
const CommonModels &models)
|
|
||||||
: manager_(manager)
|
|
||||||
, settings_(settings)
|
|
||||||
, selector_(std::make_unique<CaptureAreaSelector>(*this, settings_, models,
|
|
||||||
pixmap_, pixmapOffset_))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Capturer::~Capturer() = default;
|
|
||||||
|
|
||||||
void Capturer::capture()
|
|
||||||
{
|
|
||||||
updatePixmap();
|
|
||||||
SOFT_ASSERT(selector_, return );
|
|
||||||
selector_->activate();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Capturer::canCaptureLocked()
|
|
||||||
{
|
|
||||||
SOFT_ASSERT(selector_, return false);
|
|
||||||
return selector_->hasLocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Capturer::captureLocked()
|
|
||||||
{
|
|
||||||
updatePixmap();
|
|
||||||
SOFT_ASSERT(selector_, return );
|
|
||||||
selector_->captureLocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Capturer::updatePixmap()
|
|
||||||
{
|
|
||||||
const auto screens = QApplication::screens();
|
|
||||||
std::vector<QRect> screenRects;
|
|
||||||
screenRects.reserve(screens.size());
|
|
||||||
QRect rect;
|
|
||||||
|
|
||||||
for (const QScreen *screen : screens) {
|
|
||||||
const auto geometry = screen->geometry();
|
|
||||||
screenRects.push_back(geometry);
|
|
||||||
rect |= geometry;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPixmap combined(rect.size());
|
|
||||||
QPainter p(&combined);
|
|
||||||
p.translate(-rect.topLeft());
|
|
||||||
|
|
||||||
for (const auto screen : screens) {
|
|
||||||
const auto geometry = screen->geometry();
|
|
||||||
const auto pixmap =
|
|
||||||
screen->grabWindow(0, 0, 0, geometry.width(), geometry.height());
|
|
||||||
p.drawPixmap(geometry, pixmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
SOFT_ASSERT(selector_, return );
|
|
||||||
pixmap_ = combined;
|
|
||||||
pixmapOffset_ = rect.topLeft();
|
|
||||||
|
|
||||||
for (auto &r : screenRects) r.translate(-rect.topLeft());
|
|
||||||
selector_->setScreenRects(screenRects);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Capturer::repeatCapture()
|
|
||||||
{
|
|
||||||
SOFT_ASSERT(selector_, return );
|
|
||||||
selector_->activate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Capturer::updateSettings()
|
|
||||||
{
|
|
||||||
SOFT_ASSERT(selector_, return );
|
|
||||||
selector_->updateSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Capturer::selected(const CaptureArea &area)
|
|
||||||
{
|
|
||||||
SOFT_ASSERT(selector_, return manager_.captureCanceled())
|
|
||||||
selector_->hide();
|
|
||||||
|
|
||||||
SOFT_ASSERT(!pixmap_.isNull(), return manager_.captureCanceled())
|
|
||||||
auto task = area.task(pixmap_, pixmapOffset_);
|
|
||||||
if (task)
|
|
||||||
manager_.captured(task);
|
|
||||||
else
|
|
||||||
manager_.captureCanceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Capturer::canceled()
|
|
||||||
{
|
|
||||||
SOFT_ASSERT(selector_, return );
|
|
||||||
selector_->hide();
|
|
||||||
manager_.captureCanceled();
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "stfwd.h"
|
|
||||||
|
|
||||||
#include <QPixmap>
|
|
||||||
|
|
||||||
class Capturer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Capturer(Manager &manager, const Settings &settings,
|
|
||||||
const CommonModels &models);
|
|
||||||
~Capturer();
|
|
||||||
|
|
||||||
void capture();
|
|
||||||
bool canCaptureLocked();
|
|
||||||
void captureLocked();
|
|
||||||
void repeatCapture();
|
|
||||||
void updateSettings();
|
|
||||||
|
|
||||||
void selected(const CaptureArea &area);
|
|
||||||
void canceled();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void updatePixmap();
|
|
||||||
|
|
||||||
Manager &manager_;
|
|
||||||
const Settings &settings_;
|
|
||||||
QPixmap pixmap_;
|
|
||||||
QPoint pixmapOffset_;
|
|
||||||
std::unique_ptr<CaptureAreaSelector> selector_;
|
|
||||||
};
|
|