Compare commits

...

471 Commits

Author SHA1 Message Date
dependabot[bot]
6efc473859
Bump actions/download-artifact from 1 to 4.1.7 in /.github/workflows (#191)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 1 to 4.1.7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v1...v4.1.7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-06 10:51:26 +03:00
Gres
e1ec86f298 Update deepl translation 2023-09-02 21:09:27 +03:00
Gres
29ee5dda90 Update deepl translation 2023-05-19 23:30:44 +03:00
Gres
b8cd2dff54 Update project state 2023-05-05 21:50:57 +03:00
Gres
fb3f32f050 Update deepl translation 2023-04-08 22:09:35 +03:00
Gres
41f1f56fe5 Update deepl translation 2023-02-12 14:17:17 +03:00
Gres
5be5def820 Update deepl translation 2023-02-06 23:40:07 +03:00
Gres
12997389ff Add Hebrew translation 2023-01-28 12:29:12 +03:00
Gres
ab15cc8c79 Update Yandex translator 2023-01-28 12:29:12 +03:00
Gres
6cb9199a6c Fix repeated 'check for updates warning' 2023-01-28 12:29:12 +03:00
Gres
57b1cf8865 Add note about the app translaion 2023-01-03 21:51:56 +03:00
Gres
d328026356 Update correction resource names 2022-10-24 13:07:58 +03:00
Gres
8d4bcb8605 Update translation script 2022-10-14 21:20:42 +03:00
Gres
5a2c52e4c5 Update deepl supported languages 2022-10-07 21:09:43 +03:00
Gres
8c918e14a4 Update readme 2022-08-14 12:29:45 +03:00
Gres
936aaa90ff Update version file 2022-08-14 12:07:35 +03:00
Gres
efa9fab49f Update version file 2022-07-30 13:27:46 +03:00
Gres
cad1e83d44 Bump version 2022-07-30 13:10:43 +03:00
Gres
7be070744b Use single tesseract library
Link with it during compilation.
Bump to 5.2.0.
2022-07-29 23:09:56 +03:00
Gres
cb203b912e Improve image preprocessing 2022-07-29 23:05:49 +03:00
Gres
5f53aaec23 Rescent gcc fix 2022-07-03 22:34:16 +03:00
Gres
b71c67dd1b Respect "use user substitutions" option 2022-07-03 22:34:02 +03:00
Gres
5df1f52a68 Update version file 2022-05-22 15:06:14 +03:00
Gres
59d7f1d4f5 Update changelog 2022-05-22 14:53:33 +03:00
Gres
bf5d4efc6b Bump version 2022-05-22 14:37:50 +03:00
Gres
6cb853d804 Yet another typo 2022-05-18 23:13:51 +03:00
Gres
ac24d909d1 Typo in gcc version 2022-05-18 23:00:58 +03:00
Gres
9b6657318b Bump gcc version 2022-05-18 22:44:53 +03:00
Gres
ccfbf843dc Add info about Linux crash workaround 2022-05-18 22:44:21 +03:00
Gres
00e3f90430 Add workaround for tesseract crash on Linux 2022-05-18 22:44:21 +03:00
Gres
60f115acd1 Set proper flags for tesseract builds 2022-05-18 22:43:20 +03:00
Gres
1f6fff0050 Improve result window placement on screen borders
See #74
2022-05-04 19:36:56 +03:00
Gres
030c99d0fe Typo 2022-04-07 23:18:38 +03:00
Gres
a4a3f44806 Fixed avx support detection 2022-04-07 22:56:22 +03:00
Gres
ce9c47c3ea Update tesseract version 2022-04-03 12:19:50 +03:00
Gres
07b520d10b Change msvc version 2022-03-27 22:04:59 +03:00
Gres
260a10bea3 Close result widgets with escape button
For #86
2022-03-27 11:37:32 +03:00
Gres
4f195f5629 Fix multi-monitor result and editor positions
Fixes #90
2022-03-26 16:19:33 +03:00
Gres
7d84bd2f7b Fixed translators order persistence
fixes #91
2022-03-24 20:39:39 +03:00
Gres
a4e09d88c6 Update version file 2022-02-05 22:18:19 +03:00
Gres
ad39d98858 Disable sourceforge upload 2022-02-05 22:14:56 +03:00
Gres
2369015824 Bump version 2022-02-05 21:49:32 +03:00
Gres
156cd7c926 Make cache dependant on script changes 2022-02-05 21:49:32 +03:00
Gres
ff041facd8 Add file to store cached tesseract version in ci 2022-02-05 21:49:32 +03:00
Gres
5581f385ae Added joint script to make a release 2022-02-05 21:49:32 +03:00
Gres
958b86044e Use rescent linuxdeployqt for appimage 2022-02-05 21:49:32 +03:00
Gres
032a14380f Remove outdated file 2022-02-05 21:49:32 +03:00
Gres
c03a3d2fc2 Update translations 2022-02-05 21:49:32 +03:00
Gres
d5eb9f1855 Add some info to readme.
Add link to readme to the app.
2022-02-05 21:49:32 +03:00
Gres
7a8e8e510c Use more rescent library versions
Tesseract 5.0.1 causes "free: invalid pointer" error
2022-02-05 21:48:23 +03:00
Gres
45a36f3222 Change CI ubuntu version 2022-01-30 13:34:48 +03:00
Gres
155c3577c3 Auto select all translators if translation is enabled and no one is selected 2022-01-30 13:34:48 +03:00
Gres
0e72af3770 Auto select tesseract versions depending on cpu info 2022-01-30 13:34:48 +03:00
Gres
fdfa43c093 Fix multi-monitor configurations with primary not right/bottom 2022-01-30 13:34:48 +03:00
Gres
af15301bf1 Fix some special key mappings for global shortcuts 2022-01-12 19:06:51 +03:00
Gres
26c034e75a Change shortcut editor
Remove global shortcuts when blocking actions to not trigger it when editing
2022-01-12 19:06:51 +03:00
Gres
75bfd798c8 Add ISO code for Filipino language. Required for translation 2022-01-12 19:00:28 +03:00
Gres
59a10af57e Change tesseract github url 2021-11-22 11:37:51 +03:00
Gres
2ce8e0edc3 Update version file 2021-04-19 23:21:29 +03:00
Gres
1fa62d8370 Bump version 2021-04-19 22:52:02 +03:00
Gres
70f75ae63d Fix incorrect win/linux button coloring 2021-04-19 22:52:02 +03:00
Gres
39d1d6e237 Fix saving packed file instead of unpacked
Fixes #51
2021-04-19 22:52:02 +03:00
Gres
3f71c10964 Update version file 2021-04-17 17:36:49 +03:00
Gres
e4db31de10 Bump version 2021-04-17 11:54:58 +03:00
Gres
d5dfa5786b Disable os dependant test 2021-04-15 22:06:23 +03:00
Gres
1a153ff5c3 Order user substitutions 2021-04-15 21:49:24 +03:00
Gres
e8f4f01d9c Preview result font color and size 2021-04-15 21:32:32 +03:00
Gres
718bb7314b Show settings page info and error 2021-04-15 21:08:16 +03:00
Gres
3f2dcbbeb0 Auto check for updates when opening updates page
If page is empty
2021-04-13 20:55:47 +03:00
Gres
413cae80c4 Updates refactoring
Install updates immediately after click
Separate loader and installer logic
2021-04-08 23:11:21 +03:00
Gres
9706ab1c12 Renamed "about" help tab 2021-04-04 16:11:20 +03:00
Gres
d9bff84acd Fixed appimage binary detection for autostart 2021-04-04 16:09:16 +03:00
Gres
c715ed136a Add proper names for vertical languages 2021-03-22 22:28:47 +03:00
Gres
5b591fa476 Explicitly set page segmentation mode to auto 2021-03-21 23:05:32 +03:00
Gres
efee1088f7 Update version file 2021-02-07 12:29:02 +03:00
Gres
3aa76f6074 Bump version 2021-02-07 12:12:32 +03:00
Gres
bf3c3ad893 Typo 2021-02-07 12:06:49 +03:00
Gres
30894d7689 Fix translated text after manual edit 2021-01-31 17:46:17 +03:00
Gres
65b61dec56 Update version file 2021-01-24 21:13:31 +03:00
Gres
8273b245ef Bump version 2021-01-24 20:53:08 +03:00
Gres
49983a772a Update lib versions
Also disable webp support (some issues with it at github actions)
2021-01-24 15:50:42 +03:00
Gres
a76ee0f25a Remove set-env from ci file 2021-01-24 15:49:13 +03:00
Gres
abdf15aaaf Explicit portable mode paths 2021-01-10 16:14:09 +03:00
Gres
75a04beffa Handle empty and same text translation 2021-01-05 17:03:11 +03:00
Gres
d46c72fafb Update google translation script
Related to #37
2020-12-06 22:40:03 +03:00
Gres
6936914976 Remove outdated info about versions 2020-07-27 20:44:02 +03:00
Gres
eba1b84184 Update version file 2020-07-18 15:07:35 +03:00
Gres
99fbdf7d90 Update changelog 2020-07-18 13:48:43 +03:00
Gres
53d9470ccd Bundle vc_redist 2010 installer
Need for ssl

Fix wrong file commited
2020-07-18 13:40:31 +03:00
Gres
9c383badc9 Bundle vc_redist 2010 installer
Need for ssl
2020-07-18 13:39:00 +03:00
Gres
64869f6128 Add license info 2020-07-18 13:13:25 +03:00
Gres
74e2be5b8d Remove redundant arch flag from hunspell build 2020-07-18 12:59:44 +03:00
Gres
aba0a8b316 Add changelog link to about page 2020-07-18 12:59:16 +03:00
Gres
ac1c598953 Build hunspell via self-generated CMake file on linux too 2020-07-18 12:28:43 +03:00
Gres
0920ed1f40 Bundle multiple tesseract versions
Load them via C api and allow user to select which one to use.
2020-07-18 12:26:59 +03:00
Gres
bd99d04416 Install missing packages on ubuntu 2020-07-16 20:59:23 +03:00
Gres
6c62b5506e Change qt version 2020-07-14 22:51:24 +03:00
Gres
d8db4684fa Add explicit locale header 2020-07-14 22:51:00 +03:00
Gres
b67935ef57 Add workaround for non ascii path to binary on Windows 2020-07-13 22:40:34 +03:00
Gres
63876a3215 Add some error messages about wrong parameters 2020-07-12 15:39:28 +03:00
Gres
698c9e4e70 Remove obsolete options 2020-07-12 15:39:02 +03:00
Gres
092c5e65ca Select first item of source languages if none is selected 2020-07-12 15:28:40 +03:00
Gres
19f814261c Change hunspell->correction in updates 2020-07-12 15:12:56 +03:00
Gres
94776f06b3 Update translation script 2020-05-24 20:49:19 +03:00
Gres
614e1e5a02 Disable stale lock file 2020-05-22 21:37:29 +03:00
Gres
bbc635e500 Change models initialization order 2020-05-22 21:35:55 +03:00
Gres
644278890b Add option description about gui reset 2020-05-19 20:16:12 +03:00
Gres
a84013cb5c Publish update info 2020-05-09 14:37:37 +03:00
Gres
82be88bdf7 Update version 2020-05-09 14:05:34 +03:00
Gres
30ac4433f3 Add faq link 2020-05-09 13:58:04 +03:00
Gres
d7953024ff Update in-app help message 2020-05-09 13:48:40 +03:00
Gres
0a8ec0086b Update translators error message 2020-05-09 13:30:06 +03:00
Gres
ed81d9c406 Reset fatal error state 2020-05-09 13:30:06 +03:00
Gres
56e502c3c9 Add error message for empty source language 2020-05-09 13:30:06 +03:00
Gres
79b3092411 Update shortcuts error message 2020-05-09 13:30:06 +03:00
Gres
36700db5d1 Do not show translation via tray if not requested 2020-05-09 13:30:06 +03:00
Gres
3a4ee296af Add build from source info 2020-05-09 13:30:06 +03:00
Gres
b3bf0b6b69 Added versions info 2020-05-09 13:30:06 +03:00
Gres
ddbb180a0f Add setup information 2020-05-09 13:30:06 +03:00
Gres
4fb3af6cbf Fix representer actions when show result as tooltip 2020-05-09 11:58:40 +03:00
Gres
c5b57d1d7a Update readme 2020-05-07 12:59:54 +03:00
Gres
5c7e0260a9 Update readme 2020-05-07 12:58:19 +03:00
Gres
1d185c9402 Apply editor settings when handling key presses 2020-05-04 21:22:11 +03:00
Gres
283624a564 Create release archives with compatible tesseract version 2020-05-03 11:51:09 +03:00
Gres
d5480e6f0a Bundle nss libs into appimage 2020-05-03 10:38:16 +03:00
Gres
f71e886ccc Add tesseract alternative builds 2020-05-02 19:35:13 +03:00
Gres
51356f2624 Add tesseract build dependancy 2020-05-02 19:23:13 +03:00
Gres
55c97694fd Rename artifacts 2020-05-02 19:20:45 +03:00
Gres
5ee59598d3 Add alternative tesseract builds 2020-05-02 18:53:11 +03:00
Gres
65d17394c5 Set more compatible tesseract build 2020-05-02 17:47:05 +03:00
Gres
aa801fecb4 Update help message 2020-05-02 11:21:05 +03:00
Gres
1497538a9a Add issue templates 2020-05-01 16:22:41 +03:00
Gres
b1655775bd Add some trace messages 2020-05-01 15:20:48 +03:00
Gres
63a008d067 Write log while still can 2020-05-01 15:20:48 +03:00
Gres
1ce2f86b3d Create new task in result editor
To prevent concurrent access
2020-05-01 14:10:54 +03:00
Gres
409ad6d3c2 Clear processing queue after settings update 2020-05-01 14:05:23 +03:00
Gres
5afd8e7e7f Add short help text 2020-05-01 13:14:48 +03:00
Gres
e1ec939862 Improve dependency cache validation 2020-05-01 12:51:22 +03:00
Gres
96c678bf33 Add compatibility flags to tesseract build 2020-05-01 12:45:21 +03:00
Gres
16904f2531 Add mutex in log handler 2020-04-28 21:49:47 +03:00
Gres
05b1c57d7d Add title for translator window 2020-04-28 21:43:36 +03:00
Gres
94afaf9497 Print md5 of binary in ci 2020-04-26 16:30:59 +03:00
Gres
16ffc3307b Fix incorrect trace option state 2020-04-26 14:06:01 +03:00
Gres
b73cbde790 Redirect stderr to log file
To log side libraries output
2020-04-25 19:46:49 +03:00
Gres
5556d7aae2 Add more trace messages 2020-04-24 19:22:38 +03:00
Gres
3e1ab494d9 Add warning for incorrect captured data 2020-04-23 22:14:45 +03:00
Gres
6c70b0f21a Add trace messages 2020-04-23 22:14:01 +03:00
Gres
3d50c17b81 Add app version hashes 2020-04-20 21:25:12 +03:00
Gres
74d4a6eca4 Set default updates sorting 2020-04-20 20:46:29 +03:00
Gres
1764cf80e4 Add resources mirror links 2020-04-20 20:46:29 +03:00
Gres
38b9086cac Add tesseract language names 2020-04-20 20:46:29 +03:00
Gres
9de8db88a9 Fix group 2020-04-20 20:46:29 +03:00
Gres
5d4ad56b1e Add updates file to project 2020-04-20 20:46:29 +03:00
Gres
a1c4d8bbd9 Add updater tests 2020-04-20 20:46:29 +03:00
Gres
c26d9a0924 Place downloadable resources in one dir 2020-04-20 20:46:29 +03:00
Gres
7b1d7b9eeb Add resources mirror creation tool 2020-04-20 20:46:29 +03:00
Gres
9b2009ed6c Change trace file name 2020-04-20 20:46:29 +03:00
Gres
351847a3ef Change sourceforge deploy path 2020-04-20 20:46:29 +03:00
Gres
68efe4c8cb Deploy to sourceforge 2020-04-20 20:46:29 +03:00
Gres
a27f97f2cb Add ability to build debug version 2020-04-20 20:46:29 +03:00
Gres
1a44e17739 Use absolute paths in portable mode 2020-04-20 20:46:29 +03:00
Gres
351ac398b4 Add updater application dir variable 2020-04-20 20:46:29 +03:00
Gres
f51521f8e2 Update versions file 2020-04-20 20:46:29 +03:00
Gres
461f1f433d Do not set downloaded file time
Because actually downloaded file can be newer than stated in updates.json
2020-04-20 20:46:29 +03:00
Gres
055ba92667 Add ci hunspell build 2020-04-20 20:46:29 +03:00
Gres
250feb9bc5 Sort update components 2020-04-20 20:46:29 +03:00
Gres
0b0b0cb09c Add note about visible translation window instead of silently showing and hiding it
Toggling visibility looks terrible on Windows
2020-04-20 20:46:29 +03:00
Gres
962234418b Update translators 2020-04-20 20:46:29 +03:00
Gres
c63abf4809 Fix error message 2020-04-20 20:46:29 +03:00
Gres
04de7388b1 Bundle ssl lib 2020-04-20 20:46:29 +03:00
Gres
2f796d33c9 Allow serialport dependency 2020-04-20 20:46:29 +03:00
Gres
858649e57b Remove old CI file 2020-04-20 20:46:29 +03:00
Gres
633b9c6561 Remove outdates translations deployment 2020-04-20 20:46:29 +03:00
Gres
dce3c798a2 Add ability to set substitution for any language 2020-04-20 20:46:29 +03:00
Gres
da51f79708 Allow special characters in substitutions 2020-04-20 20:46:29 +03:00
Gres
bcf83acf4e Add redirect support 2020-04-20 20:46:29 +03:00
Gres
48a01a3992 Add auto correction with hunspell 2020-04-20 20:46:29 +03:00
Gres
23cdc3e57c Fix update download error show 2020-04-20 20:46:29 +03:00
Gres
3c39faaaad Silence warnings 2020-04-20 20:46:29 +03:00
Gres
8957f2407d Add translation 2020-04-20 20:46:29 +03:00
Gres
7227fc9844 Change messages text 2020-04-20 20:46:29 +03:00
Gres
7c87371fe5 Add task debug print 2020-04-20 20:46:29 +03:00
Gres
8bd89db060 Collect updater errors and signal only on final failure 2020-04-20 20:46:29 +03:00
Gres
a865381b65 Enable trace if env variable is set 2020-04-20 20:46:29 +03:00
Gres
0c3893ddf8 Add support of zip archived components 2020-04-20 20:46:29 +03:00
Gres
7bc5030ed5 Cleanup 2020-04-20 20:46:29 +03:00
Gres
5be05c46d8 Change action order in context menu 2020-04-20 20:46:29 +03:00
Gres
eff7552d07 Replace one-use methods with lambdas 2020-04-20 20:46:29 +03:00
Gres
1ba4f6be4d Check for supported actions in setter 2020-04-20 20:46:29 +03:00
Gres
b2e0694dfa Disable edition updates via delegate 2020-04-20 20:46:29 +03:00
Gres
69819dee7f Add ability to select all updates and reset action from menu 2020-04-20 20:46:29 +03:00
Gres
576d9d5662 Add support of multiple update urls 2020-04-20 20:46:29 +03:00
Gres
d931015d6b Remove unused tesseract engines after some time 2020-04-20 20:46:29 +03:00
Gres
5397521c79 Hide result before capture 2020-04-20 20:46:29 +03:00
Gres
b9ff1eaca3 Add items to run new capture to result menu 2020-04-20 20:46:29 +03:00
Gres
ae8801d837 Change widgets that show paths 2020-04-20 20:46:29 +03:00
Gres
071eadc088 Fix last update check date handling 2020-04-20 20:46:29 +03:00
Gres
56bacbfc7e Warn if version might be outdated 2020-04-20 20:46:29 +03:00
Gres
ee899c1b23 Add ability to select updates via context menu 2020-04-20 20:46:29 +03:00
Gres
0ef82187d6 Add size to update info 2020-04-20 20:46:29 +03:00
Gres
7e73804348 Shuffle update urls 2020-04-20 20:46:29 +03:00
Gres
b2f5cc93f2 Show used translator name 2020-04-20 20:46:29 +03:00
Gres
77a1f4a00b Change cursor on selected area 2020-04-20 20:46:29 +03:00
Gres
8d2e726715 Add ability to capture multiple areas 2020-04-20 20:46:29 +03:00
Gres
5f4ef955e1 Add support of multiple component urls 2020-04-20 20:46:29 +03:00
Gres
35cbdb17e8 Move update download handling to separate method 2020-04-20 20:46:29 +03:00
Gres
0df0f2ef20 Added baidu translator 2020-04-20 20:46:29 +03:00
Gres
bbe7d0a729 Add ability to move result widget with middle button 2020-04-20 20:46:29 +03:00
Gres
013370e51e Write trace to file for debugging 2020-04-20 20:46:29 +03:00
Gres
58f6283fe3 Move about info to settings 2020-04-20 20:46:29 +03:00
Gres
ca99f570f7 Change url escaping 2020-04-20 20:46:29 +03:00
Gres
45e12265c1 Show update download progress 2020-04-20 20:46:29 +03:00
Gres
8aa10754e2 Add methods to convert between component/index 2020-04-20 20:46:29 +03:00
Gres
9144ea262d Update translator 2020-04-20 20:46:29 +03:00
Gres
040d0ff540 Change user agent string 2020-04-20 20:46:29 +03:00
Gres
a85dafa18d Ensure page is visible during translation
Because some translator sites require it
2020-04-20 20:46:29 +03:00
Gres
6f7270f229 Preserve translators order in settings 2020-04-20 20:46:29 +03:00
Gres
2f1779ee9b Add ability to show debug page from translator window
Also always enable chromium debug mode
2020-04-20 20:46:29 +03:00
Gres
4dd2d2ddb6 Add ability to show translator from menu and close it 2020-04-20 20:46:29 +03:00
Gres
9ea8974108 Hide previous results before capturing new 2020-04-20 20:46:29 +03:00
Gres
ff19f87b44 Improve support of empty input data 2020-04-20 20:46:29 +03:00
Gres
e36fbbf3b0 Change about message 2020-04-20 20:46:29 +03:00
Gres
15d4bcb36e Change help and area tooltip drawing 2020-04-20 20:46:29 +03:00
Gres
0738a88eb7 Add ability to lock capture areas and capture them by hotkey 2020-04-20 20:46:29 +03:00
Gres
aac286df9d Fix corrector work with empty corrections 2020-04-20 20:46:29 +03:00
Gres
43597f05f2 Update readme 2020-04-20 20:46:29 +03:00
Gres
e70f5c21a5 Add ability to change result font and background colors 2020-04-20 20:46:29 +03:00
Gres
ac1267c88d Show current languages for capture area 2020-04-20 20:46:29 +03:00
Gres
1ff3fb1f1b Add ability to edit results 2020-04-20 20:46:29 +03:00
Gres
c9b2677bec Remove unneeded variable 2020-04-20 20:46:29 +03:00
Gres
9e72e67ee7 Check if translated before copy to clipboard 2020-04-20 20:46:28 +03:00
Gres
258073c785 Move common model to separate class 2020-04-20 20:46:28 +03:00
Gres
87987f4a71 Move updater setup to separate function 2020-04-20 20:46:28 +03:00
Gres
c2a1dab1c7 Show results with errors 2020-04-20 20:46:28 +03:00
Gres
4ca17dca30 Update supported translation languages 2020-04-20 20:46:28 +03:00
Gres
2da5363448 Move interaction with last result to representer 2020-04-20 20:46:28 +03:00
Gres
d55ccd82b9 Compact capture editor 2020-04-20 20:46:28 +03:00
Gres
a92a5a1cc6 Test before build 2020-04-20 20:46:28 +03:00
Gres
9d9f082b57 Add test 2020-04-20 20:46:28 +03:00
Gres
7ed6742f51 Add ability to swap languages in customization menu 2020-04-20 20:46:28 +03:00
Gres
897db5b6c4 Compile translation into binary 2020-04-20 20:46:28 +03:00
Gres
d760ea3f13 Remove translators from distribution bundle 2020-04-20 20:46:28 +03:00
Gres
872b047d70 Show translated/recognized text on top if there is no space below 2020-04-20 20:46:28 +03:00
Gres
4b5fb132f7 Place editor respecting available space 2020-04-20 20:46:28 +03:00
Gres
e1c2defd71 Add ability to edit selected rect 2020-04-20 20:46:28 +03:00
Gres
cc815f4245 Changed language codes to singleton 2020-04-20 20:46:28 +03:00
Gres
aee380fcd5 Move task creation to separate class 2020-04-20 20:46:28 +03:00
Gres
6336d3545a Change overlay drawing 2020-04-20 20:46:28 +03:00
Gres
295353332f Move available translations getter to translator class 2020-04-20 20:46:28 +03:00
Gres
10f5e5e387 Add default translation state to help 2020-04-20 20:46:28 +03:00
Gres
12b8d332c8 Cleanup 2020-04-20 20:46:28 +03:00
Gres
ce83cfff54 Change default result font family 2020-04-20 20:46:28 +03:00
Gres
4edb231c4e Move service classes to separate namespace 2020-04-20 20:46:28 +03:00
Gres
2874177bc4 Use one capture widget for all screens instead of one per screen 2020-04-20 20:46:28 +03:00
Gres
178c954124 Prevent capture stuck 2020-04-20 20:46:28 +03:00
Gres
668c8f1183 Add capture help message 2020-04-20 20:46:28 +03:00
Gres
71b74bb286 Rename capture overlay 2020-04-20 20:46:28 +03:00
Gres
d7cb1e3f56 Add translation search path in resources 2020-04-20 20:46:28 +03:00
Gres
1757b2b89f Recreate page on webengine render error/crash 2020-04-20 20:46:28 +03:00
Gres
705bae636d Use only one settings instance 2020-04-20 20:46:28 +03:00
Gres
0da289e16f Update translation scripts 2020-04-20 20:46:28 +03:00
Gres
2bf5515b0b Convert both translation languages ids to codes 2020-04-20 20:45:26 +03:00
Gres
04abe4b0d4 Change cache key to separate win32/64 builds 2020-04-20 20:45:26 +03:00
Gres
0fcdff9be2 Added ability to toggle autorun 2020-04-20 20:45:26 +03:00
Gres
1f478bc48a Added ability to show/hide captured image and recognized text 2020-04-20 20:45:26 +03:00
Gres
69f7d10c3a Added ability to change result font 2020-04-20 20:45:26 +03:00
Gres
124d32857f Keep only available languages in correction selector 2020-04-20 20:45:26 +03:00
Gres
8b84fef929 Update only last check date when needed 2020-04-20 20:45:26 +03:00
Gres
b81bdc69eb Use langauge names for updates 2020-04-20 20:45:26 +03:00
Gres
cc00f0a54d Use localized language names 2020-04-20 20:45:26 +03:00
Gres
fa75537487 Print localized update name 2020-04-20 20:45:26 +03:00
Gres
1582bdf294 Added auto update checker 2020-04-20 20:45:26 +03:00
Gres
71bb96fe2f Refactoring 2020-04-20 20:45:26 +03:00
Gres
148bf249f8 Change updated file time if available 2020-04-20 20:45:26 +03:00
Gres
ced4fdd834 Load substitutions in legacy format 2020-04-20 20:45:26 +03:00
Gres
afe2cc1a2c Added ability to only check for updates without installing
For built in components
2020-04-20 20:45:26 +03:00
Gres
3847c8d61b Inform about updates 2020-04-20 20:45:26 +03:00
Gres
493262876f Added portable mode 2020-04-20 20:45:26 +03:00
Gres
83232bfc76 Add ability to apply settings without closing dialog 2020-04-20 20:45:26 +03:00
Gres
032895830c Removed outdated tooltips 2020-04-20 20:45:26 +03:00
Gres
d7b671a73d Tessdata and translators path and constant and point to default app writable location 2020-04-20 20:45:26 +03:00
Gres
bff2598285 Prevent setting names edition 2020-04-20 20:45:26 +03:00
Gres
40824e60de Add resource downloader/updater 2020-04-20 20:45:26 +03:00
Gres
c5889e1374 typo 2020-04-20 20:44:37 +03:00
Gres
bfe84af681 Check for empty path 2020-04-20 20:44:37 +03:00
Gres
9da8c516b6 Show translator script errors 2020-04-20 20:44:37 +03:00
Gres
835714d76d Change preferred ocr resolution 2020-04-20 20:44:37 +03:00
Gres
e7fe13c5f0 Change leptonica/qt conversion functions 2020-04-20 20:44:37 +03:00
Gres
cc4b0573d8 Show corrected text in result with uncorrected in tooltip 2020-04-20 20:44:37 +03:00
Gres
deb1bee2c7 Refactor scaling 2020-04-20 20:44:37 +03:00
Gres
4005f94766 Use version from .pro file 2020-04-20 20:44:37 +03:00
Gres
d90fd735e6 Cleanup settings 2020-04-20 20:44:37 +03:00
Gres
4316d1607a Add dependencies build cache 2020-04-20 20:44:37 +03:00
Gres
a4df962ca8 Ignore empty tasks 2020-04-20 20:44:37 +03:00
Gres
d945081dab Write corrected text to separate property 2020-04-20 20:44:37 +03:00
Gres
ab23b6ba6c Add message on program start 2020-04-20 20:44:37 +03:00
Gres
8a65cddc6f Allow edit user substitutions 2020-04-20 20:44:37 +03:00
Gres
a197edd62b Fix correction infinite loop 2020-04-20 20:44:37 +03:00
Gres
6d820ad229 Save selected source and target languages 2020-04-20 20:44:37 +03:00
Gres
7cba01ef20 Prevent excess update 2020-04-20 20:44:37 +03:00
Gres
cdce150db5 Remove property 2020-04-20 20:44:37 +03:00
Gres
546f4782d1 Add proxy handling 2020-04-20 20:44:37 +03:00
Gres
aee289eaef Clamp for concrete enum values 2020-04-20 20:44:37 +03:00
Gres
86d5ee3aa6 Show current page's url and load images option 2020-04-20 20:44:37 +03:00
Gres
fd45105dc7 Reformat readme 2020-04-20 20:44:37 +03:00
Gres
4d6f5857c0 Cleanup 2020-04-20 20:44:37 +03:00
Gres
ad107834eb Handle doTranslation option 2020-04-20 20:44:37 +03:00
Gres
fbbfdb9921 Disable attempt to install rescent gcc 2020-04-20 20:44:37 +03:00
Gres
d3b2b1f328 Install additional package 2020-04-20 20:44:37 +03:00
Gres
0b6511434a Disable osx build 2020-04-20 20:44:37 +03:00
Gres
8804be6436 Force rescent compiler on linux 2020-04-20 20:44:37 +03:00
Gres
cc93067587 Install rescent gcc on linux 2020-04-20 20:44:37 +03:00
Gres
8b4f6742c6 Disable pkg-config for leptonica search 2020-04-20 20:44:37 +03:00
Gres
630a4e8ec9 Change pkgconfig search path 2020-04-20 20:44:37 +03:00
Gres
863fe2fe92 Set leptonica path via cmake parameter 2020-04-20 20:44:37 +03:00
Gres
a371b5bed4 Merge env via python 2020-04-20 20:44:37 +03:00
Gres
40f37f648d Misprint 2020-04-20 20:44:37 +03:00
Gres
a2a24f6648 Convert paths 2020-04-20 20:44:37 +03:00
Gres
8387662e83 Adjust ci 2020-04-20 20:44:37 +03:00
Gres
f2b7ac1821 Add build and ci scripts 2020-04-20 20:44:37 +03:00
Gres
81f370f5c7 Remove outdated files 2020-04-20 20:44:37 +03:00
Gres
4c526f65df Refactor. WIP 2020-04-20 20:44:37 +03:00
Gres
623a4b6086 Update .gitignore 2020-04-20 20:44:37 +03:00
Gres
5a980b4365 foreach => for 2020-04-20 20:44:37 +03:00
Gres
9356f4cb1f Update build paths and flags 2020-04-20 20:44:37 +03:00
Gres
7a2d512a81 Move style file 2020-04-20 20:44:37 +03:00
Gres
d198c88625 Rename sources and put them to src dir 2020-04-20 20:44:37 +03:00
Gres
12cb3bf4ab Adjust to rescent tesseract version 2020-04-20 20:44:37 +03:00
Gres
f15fb29ac1 Typo 2020-04-20 20:44:37 +03:00
Gres
2ae8b4e4a7 Update bing script 2020-03-23 20:50:23 +03:00
Gres
eb796471cf Update readme 2020-02-20 20:50:53 +03:00
Landgraf132
393cec503d Incorrect screen capture on ubuntu 19.10 (#20)
Added support Qt version 5.12.4 (relevant for ubuntu 19.10)
2020-01-07 17:27:46 +03:00
Gres
fa89460466 Added papago translation script 2019-09-17 23:07:49 +03:00
Gres
d44c8ef060 Updated bing translator 2019-08-11 21:37:00 +03:00
Gres
0b3396e1f4 Added deepl translation script 2019-02-10 16:43:08 +03:00
Gres
1e3411bd58 Added google api translation script 2018-12-24 20:26:47 +03:00
Gres
49c5659334 Updated google translation 2018-11-29 19:22:51 +03:00
Gres
a69213c956 Updated bing translation 2018-10-17 19:33:35 +03:00
Gres
4109968fed Fixed possible wrong index usage 2018-10-17 19:32:55 +03:00
Gres
bab4d633db Translation fix 2018-10-17 19:29:19 +03:00
Gres
6a315c75c5 Updated readme. Closes #3. 2018-04-13 21:41:26 +03:00
Gres
778fe16481 Embed vcredist into offline installer. 2018-02-24 15:58:36 +03:00
Gres
1db0e85e2d Embed vcredist installer. 2018-02-24 15:38:58 +03:00
Gres
013f697461 Deploy english readme last on sourceforge. 2018-02-24 15:27:48 +03:00
Gres
98c73947d4 Embed ssl into installer. 2018-02-24 14:45:31 +03:00
Gres
dcbcb070a8 Add changelog to sourceforge. 2018-02-24 14:45:10 +03:00
Gres
f641197552 Fixed translation for offline installer. 2018-02-24 14:43:13 +03:00
Gres
1841539543 Changed version number. 2018-02-24 13:33:15 +03:00
Gres
b21cd123bb Updated installer languages. 2018-02-24 13:07:34 +03:00
Gres
670fc64d9a Several changes. 2018-02-23 14:01:47 +03:00
Gres
4499e94c44 Added force rotate translators option. 2018-02-23 12:46:47 +03:00
Gres
fe16cbe669 Debug print translated message.
Sometimes prevent duplicate translations.
2017-08-30 19:28:17 +03:00
Gres
ce8567afca Increased git clone depth. 2017-08-10 16:50:22 +03:00
Gres
b022dd18f4 Added windows ci. 2017-08-07 11:31:14 +03:00
Gres
1d609339f6 Deploy script back to normal. 2017-08-07 11:30:51 +03:00
Gres
399824afb6 Check only online installer with test version. 2017-08-07 11:20:57 +03:00
Gres
9a5738c4fd Check deployment. 2017-08-07 11:13:08 +03:00
Gres
1bc25c174e Merged environment sections. 2017-08-07 11:09:06 +03:00
Gres
9ecf4c27fb Added upload to sourceforge. 2017-08-07 11:04:01 +03:00
Gres
5800c23989 Added dependencies caching. 2017-08-06 11:50:05 +03:00
Gres
280175e9c2 Added appveyour status shield. 2017-08-05 16:18:59 +03:00
Gres
e6a3a5d158 First version. 2017-08-05 16:10:15 +03:00
Gres
407ed8a8a2 Added support of iss definitions file. 2017-08-05 15:54:32 +03:00
Gres
a6be52ea0d Added User32 lib for windows. 2017-08-05 15:53:54 +03:00
Gres
a489912731 Updated license year. 2017-08-05 15:53:38 +03:00
Gres
c6dc304627 Updated translator script. 2017-07-21 20:24:53 +03:00
Gres
b11f3e9368 Changed installer names in scripts. 2017-06-25 12:16:38 +03:00
Gres
e5a25b1bc3 Fixed markdown. 2017-06-25 12:15:24 +03:00
Gres
f3732c96e9 Changed installer's tessdata handling. 2017-06-25 11:59:27 +03:00
Gres
0bab1d6f60 Removed linux compatible version. 2017-06-02 15:22:40 +03:00
Gres
7a424953cc Removed linux compatible version. 2017-06-02 15:21:56 +03:00
Gres
c382cdf8cd Updated yandex translator. 2017-05-31 20:34:41 +03:00
Gres
180dc2c35e Fixed yandex translator. 2017-05-31 20:32:18 +03:00
Gres
34081855cd Merge branch 'develop' 2017-05-27 13:11:44 +03:00
Gres
8dbc32a07f Added option to ignore ssl errors. 2017-05-27 13:03:17 +03:00
Gres
a84e2fa72a Updated translators. 2017-05-27 13:00:13 +03:00
Gres
d1f930ff57 Use of correct lrelease tool 2015-12-26 14:36:16 +03:00
Gres
ab1af32fa8 Travis notification setting changed. 2015-11-07 21:06:11 +03:00
Gres
a4f82b3d75 Binary download link changed. 2015-11-06 23:31:12 +03:00
Gres
fd1a97d119 Binary download link changed. 2015-11-06 23:29:20 +03:00
Gres
382db2f810 Finish 2.0.0 2015-11-06 23:21:01 +03:00
Gres
41d028951f Finish 2.0.0 2015-11-06 23:21:01 +03:00
Gres
e0177de97e Do not cleanup work dir by default. 2015-11-06 22:58:16 +03:00
Gres
eca5d1ed39 Added travis build for release branch. 2015-11-06 22:53:56 +03:00
Gres
8c15289f8a Application updates disabled in linux (only notify). 2015-11-06 22:50:11 +03:00
Gres
777bb9ff25 Proper numeric fields extract from version.json. 2015-11-06 22:46:56 +03:00
Gres
aa26e6f9de Set tray icon to processing state on retranslating edited text. 2015-11-06 22:00:52 +03:00
Gres
8c6e6854e1 Increased translation stability. 2015-11-06 21:13:18 +03:00
Gres
02e4624514 Changed default tessdata setting value for linux. 2015-11-06 21:12:42 +03:00
Gres
a0a1e4afd4 Fix wrong window size on some linux systems. 2015-11-05 19:54:20 +03:00
Gres
6ce69c9db1 Build scripts updated. 2015-11-04 21:30:36 +03:00
Gres
9ce964945c app.rc removed. 2015-11-02 20:58:56 +03:00
Gres
83ed896b93 Fill more Win exe's properties. 2015-11-02 20:48:52 +03:00
Gres
4f8958b9c6 Travis integration. 2015-10-25 16:22:37 +03:00
Gres
4a14a0af7f Updated project. 2015-10-25 14:39:40 +03:00
Gres
810564f75f Update of build scripts. 2015-10-25 14:38:57 +03:00
Gres
f9b47872d7 Updated translations. 2015-10-25 14:05:19 +03:00
Gres
baa0717884 Updated readme. 2015-10-25 14:05:06 +03:00
Gres
e00b244050 Set numeric locale to C for proper tesseract-orc-* packages work on linux. 2015-10-25 14:04:48 +03:00
Gres
45d7f76890 Added new tesseract languages. 2015-10-25 14:03:44 +03:00
Gres
7db8cd2d19 Show message if failed to register global shortcut. 2015-10-25 13:58:29 +03:00
Gres
692ccc133b Relative paths now relative to app.exe instead of current dir. 2015-10-25 13:56:56 +03:00
Gres
b0c745b61e Build and distribution process has been automated. 2015-10-25 13:55:43 +03:00
Gres
af91756ca3 Added icon source. 2015-10-20 23:28:46 +03:00
Gres
ff7ed3de66 Recognizer substitutions now placed in user's home dir. 2015-10-20 23:26:59 +03:00
Gres
c53dcdae6d License year changed. 2015-10-20 23:26:59 +03:00
Gres
25d6ccd81a Added ability to auto-update some components. 2015-10-20 23:26:59 +03:00
Gres
01b5d1c919 Edit update settings. 2015-10-20 23:26:59 +03:00
Gres
9892a9f29b Added Updater class. 2015-10-20 23:26:59 +03:00
Gres
37f2293156 Image conversion optimization. 2015-10-11 19:51:37 +03:00
Gres
e18f9b2431 Fixed icon change to processing after last item show. 2015-10-11 19:51:07 +03:00
Gres
d90c104fd2 Version updated. 2015-10-11 18:34:55 +03:00
Gres
c79f40cf6d Translations updated. 2015-10-11 18:34:24 +03:00
Gres
e5e9950cfc Memory leaks and "leaks" fixes. 2015-10-11 18:33:55 +03:00
Gres
a4d3dd90fd Proxy fix. 2015-10-11 18:33:19 +03:00
Gres
1d211d1baf Added some usage tips in about window. 2015-10-10 21:05:28 +03:00
Gres
027c7b46a0 Added ability to swap translation/ocr languages. 2015-10-10 21:05:12 +03:00
Gres
a57e0b1af8 Manager now sets ocrLanguage instead of recognizer. Last one checks input item for validity. 2015-10-10 21:04:28 +03:00
Gres
c1dab33e7c Updated ocr<->translation codes functions. 2015-10-10 21:01:19 +03:00
Gres
1c922db961 Added qss to ResultDialog. 2015-10-10 19:39:38 +03:00
Gres
a6c813e07a Settings tab order updated. 2015-10-10 19:11:21 +03:00
Gres
45978ec013 Icons updated. Now use 4 icon types depending on internal state. 2015-10-10 19:07:25 +03:00
Gres
811b232c81 ProcessingItem finishes its way to Manager even if there are errors on the way. 2015-10-10 19:05:38 +03:00
Gres
b6461fa3a9 Added ability to set proxy. 2015-10-10 14:45:57 +03:00
Gres
0fd694787a Added ability to automatically fix defined recognition errors. 2015-10-10 13:33:18 +03:00
Gres
fd78dde837 Remove obsolete file. 2015-10-10 00:20:54 +03:00
Gres
a7977a1f0e Added ability to copy selected image to clipboard. 2015-10-10 00:20:14 +03:00
Gres
350fa4cd72 Auto retranslate item after user edition if it should be. 2015-10-10 00:06:10 +03:00
Gres
9083a7ca6c Pass ProcessingItem through Translator event if it should not be translated (allow user to choose it be key modifiers). 2015-10-10 00:05:50 +03:00
Gres
2a1ee13b46 Readme update. 2015-10-09 23:36:58 +03:00
Gres
468a6fd039 SettingsEditor updated. Added rescent translator settings. 2015-10-09 23:35:37 +03:00
Gres
90cce0a370 Added translator debug mode. 2015-10-09 23:35:37 +03:00
Gres
a7ffec8827 Added and updated translators. 2015-10-09 23:35:37 +03:00
Gres
3a564e85a8 Added support of several translators. 2015-10-09 23:35:37 +03:00
Gres
6bacb14c56 Removed script error message support. Now it should return empty result if fails. 2015-10-09 23:35:37 +03:00
Gres
67a4c8e0ea Added TranslatorHelper class. 2015-10-09 23:35:37 +03:00
Gres
186395f692 Enabled local storage (yandex requires it). 2015-10-08 20:08:23 +03:00
Gres
56a7469260 Mark item as translated even on error (to show at least recognized text). 2015-10-08 20:07:45 +03:00
Gres
e38449b928 Replaced Translator and GoogleWebTranslator with WebTranslator. 2015-10-08 18:43:27 +03:00
Gres
01d969968e Added WebTranslator class. 2015-10-08 18:43:27 +03:00
Gres
f3c4f1c5b5 Added ability to correct recognized text. 2015-10-08 18:43:27 +03:00
Gres
381df69650 More correct result positioning on screen. 2015-10-08 18:43:27 +03:00
Gres
77757ff3c6 Removed obsolete code. 2015-10-08 18:43:27 +03:00
Gres
3e499b86c5 Added ability to retranslate item from ResultDialog. 2015-10-08 18:43:23 +03:00
Gres
f33c0431b9 More correct submenu detection. 2015-09-30 21:24:39 +03:00
Gres
6358629269 Show "Copied to clipboard" message only when copying after tray icon click. 2015-09-30 21:05:02 +03:00
Gres
54f809707c Added ability to copy last result to clipboard from ResultDialog. 2015-09-30 21:04:25 +03:00
Gres
283b596ef1 Last showing item now stores only in ResultDialog. 2015-09-30 20:49:05 +03:00
Gres
1f5d6c73bd Added ability to repeat image recognition with another OCR language from ResultDialog. 2015-09-30 20:38:55 +03:00
Gres
5b166cba4c Update languages menu moved to LanguageHelper from SelectionDialog. 2015-09-30 20:20:33 +03:00
Gres
fb1d25f914 Added possibility to select image several times without closing SelectionDialog. 2015-09-30 19:49:10 +03:00
Gres
4c51e264b5 Ignore small selection (less 3x3). 2015-09-30 19:48:21 +03:00
Gres
3109444805 Added dependencies preparation scripts. 2015-09-29 19:21:38 +03:00
Gres
9e85af0eaf Added possibility to capture image from last used screenshot (SelectionDialog). 2015-09-29 18:14:56 +03:00
Gres
dd3d92edb6 Activate selection and result dialogs when showing. 2015-09-29 18:11:00 +03:00
Gres
c380d96815 Ui update (tooltips and label fixes). 2015-09-29 17:50:37 +03:00
Gres
6d57198471 Better support of only OCR mode (cosmetic). 2015-09-29 16:24:20 +03:00
Gres
bf6eea7f8e Added only OCR mode (without translation). 2015-09-29 15:34:01 +03:00
Gres
097405038b Code simplify. 2015-09-28 23:24:08 +03:00
Gres
0fa1a6a1fc Proper result show if taskbar is on top/left side of desktop. 2015-09-28 23:17:56 +03:00
Gres
4887441aeb Simplified applySettings method. 2015-09-28 22:57:51 +03:00
Gres
aa705b0ef5 More proper app termination. 2015-09-28 22:17:24 +03:00
Gres
bd6b417cd8 Check for available memory on linux too. 2015-09-28 22:12:38 +03:00
Gres
256e812401 Added base support of multi-screen systems. 2015-09-27 20:50:19 +03:00
Gres
d46eb9c5bd Disable actions when settings editor is active. 2015-09-27 18:58:50 +03:00
Gres
5c7220707e Trigger action only if it is enabled. 2015-09-27 18:58:06 +03:00
Gres
26ddd92584 Use QKeySequenceEdit in settings. 2015-09-27 18:57:50 +03:00
Gres
670fda2ac5 Added linux support. 2015-09-27 18:16:52 +03:00
Gres
ced4b1c5c8 Build configuration updated (now also includes linux builds). 2015-09-27 18:16:37 +03:00
Gres
bf2d19b6f0 Allow only 1 application instance. 2015-09-23 20:51:20 +03:00
Gres
9007379242 Code formatting with uncrustify. 2015-09-22 21:41:08 +03:00
176 changed files with 52144 additions and 4914 deletions

16
.clang-format Normal file
View File

@ -0,0 +1,16 @@
---
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 Normal file
View File

@ -0,0 +1,37 @@
---
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.

View File

@ -0,0 +1,20 @@
---
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 Normal file
View File

@ -0,0 +1,100 @@
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
View File

@ -6,3 +6,6 @@
*.exe *.exe
distr/content/ distr/content/
*.tar.gz
__pycache__

View File

@ -1,276 +0,0 @@
#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);
}

View File

@ -1,29 +0,0 @@
#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

View File

@ -1,93 +0,0 @@
#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);
}

View File

@ -1,43 +0,0 @@
#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

View File

@ -1,152 +0,0 @@
#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);
}

View File

@ -1,18 +0,0 @@
#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

View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2014 Gres (gres@gres.biz) Copyright (c) 2017 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

View File

@ -1,244 +0,0 @@
#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");
}

View File

@ -1,39 +0,0 @@
#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

View File

@ -1,227 +0,0 @@
#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);
}

View File

@ -1,57 +0,0 @@
#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

View File

@ -1,11 +0,0 @@
#include "ProcessingItem.h"
bool ProcessingItem::isValid() const
{
bool valid = true;
valid &= (!screenPos.isNull ());
valid &= (!source.isNull ());
valid &= (!recognized.isEmpty ());
valid &= (!translated.isEmpty ());
return valid;
}

View File

@ -1,20 +0,0 @@
#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
View File

@ -1,38 +1,92 @@
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.
Usage ## Installation
-----
1. Press capture hotkey.
2. Select region on screen.
3. Get translation of recognized text.
Features **Windows**: download archive from [github releases](https://github.com/OneMoreGres/ScreenTranslator/releases) page, extract it and run `.exe` file.
--------
* 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.
Limitations **Linux**: download `.AppImage` file from [github releases](https://github.com/OneMoreGres/ScreenTranslator/releases), make executable (`chmod +x <file>`) and run it.
-----------
* Works only on primary screen
* Can not capture some dynamic web-pages
* Not very precise OCR (need better preprocessing steps)
Used software **OS X**: currently not supported.
-------------
* 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/)

View File

@ -1,100 +0,0 @@
#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 ("Текст не распознан."));
}
}

View File

@ -1,40 +0,0 @@
#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

View File

@ -1,7 +0,0 @@
<RCC>
<qresource prefix="/">
<file>translations/translation_en.qm</file>
<file>translations/translation_ru.qm</file>
<file>images/icon.png</file>
</qresource>
</RCC>

View File

@ -1,69 +0,0 @@
#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);
}
}

View File

@ -1,31 +0,0 @@
#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

View File

@ -1,98 +0,0 @@
<?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>

View File

@ -1,66 +0,0 @@
#-------------------------------------------------
#
# 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

View File

@ -1,166 +0,0 @@
#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 ();
}

View File

@ -1,41 +0,0 @@
#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

View File

@ -1,43 +0,0 @@
<?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>

View File

@ -1,50 +0,0 @@
#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

View File

@ -1,133 +0,0 @@
#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));
}

View File

@ -1,43 +0,0 @@
#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

View File

@ -1,271 +0,0 @@
<?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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Сочетание клавиш для перехода в режим захвата.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Захватить</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Сочетание клавиш для перехода в режим захвата.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Скопировать</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Сочетание клавиш для перехода в режим захвата.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Повторить</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Необходимо для распознавания.&lt;/p&gt;&lt;p&gt;Скачивается отсюда: &lt;a href=&quot;https://code.google.com/p/tesseract-ocr/downloads/list&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://code.google.com/p/tesseract-ocr/downloads/list&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Заполняется на основании содержания tessdata&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Язык распознавания</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Масштабирование изображения для улучшения распознания. Больше - лучше (до определенных пределов), но медленнее.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Увеличение масштаба</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Язык, на который осуществляется перевод.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Язык результата</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>

View File

@ -1,15 +0,0 @@
#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

View File

@ -1,121 +0,0 @@
#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);
}

View File

@ -1,36 +0,0 @@
#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

1
app.rc
View File

@ -1 +0,0 @@
IDI_ICON1 ICON DISCARDABLE "images/icon.ico"

Binary file not shown.

View File

@ -1,269 +0,0 @@
; 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

View File

@ -1,26 +0,0 @@
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.

View File

@ -1,26 +0,0 @@
Изменения.
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:
- Отображение результата в окошке, вместе с картинкой.
- Контекстное меню расширено. Добавлены кнопки отображения последнего результата и копирования его в буфер обмена.

View File

@ -1,78 +0,0 @@
[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=Âüåòíàìñêèé

View File

@ -1,26 +0,0 @@
#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 Normal file
View File

@ -0,0 +1,28 @@
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 Normal file

File diff suppressed because it is too large Load Diff

14813
external/gtest/gtest.h vendored Normal file

File diff suppressed because it is too large Load Diff

22
external/miniz/LICENSE vendored Normal file
View File

@ -0,0 +1,22 @@
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 Normal file

File diff suppressed because it is too large Load Diff

1338
external/miniz/miniz.h vendored Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -1,25 +0,0 @@
#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();
}

16
recources.qrc Normal file
View File

@ -0,0 +1,16 @@
<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>

142
screen-translator.pro Normal file
View File

@ -0,0 +1,142 @@
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
}

125
share/Changelog_en.md Normal file
View File

@ -0,0 +1,125 @@
# 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.

125
share/Changelog_ru.md Normal file
View File

@ -0,0 +1,125 @@
# Изменения
## 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
* Отображение результата в окошке, вместе с картинкой.
* Контекстное меню расширено. Добавлены кнопки отображения последнего результата и копирования его в буфер обмена.

65
share/ci/appimage.py Normal file
View File

@ -0,0 +1,65 @@
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)))

26
share/ci/build.py Normal file
View File

@ -0,0 +1,26 @@
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)

213
share/ci/common.py Normal file
View File

@ -0,0 +1,213 @@
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 ''

35
share/ci/config.py Normal file
View File

@ -0,0 +1,35 @@
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'

131
share/ci/get_hunspell.py Normal file
View File

@ -0,0 +1,131 @@
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)

100
share/ci/get_leptonica.py Normal file
View File

@ -0,0 +1,100 @@
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)

82
share/ci/get_qt.py Normal file
View File

@ -0,0 +1,82 @@
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)

52
share/ci/get_qt_ssl.py Normal file
View File

@ -0,0 +1,52 @@
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)

98
share/ci/get_tesseract.py Normal file
View File

@ -0,0 +1,98 @@
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)

19
share/ci/macdeploy.py Normal file
View File

@ -0,0 +1,19 @@
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)

43
share/ci/release.py Normal file
View File

@ -0,0 +1,43 @@
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')

84
share/ci/sourceforge.py Normal file
View File

@ -0,0 +1,84 @@
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))

25
share/ci/test.py Normal file
View File

@ -0,0 +1,25 @@
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)

57
share/ci/windeploy.py Normal file
View File

@ -0,0 +1,57 @@
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)))

BIN
share/images/STIcon.xcf Normal file

Binary file not shown.

BIN
share/images/STIconBlue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
share/images/STIconRed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
share/images/debug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

BIN
share/images/debug@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
share/images/icon.icns Normal file

Binary file not shown.

BIN
share/images/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
share/images/loadImages.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

13
share/mirror/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
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"]

View File

@ -0,0 +1,15 @@
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

51
share/mirror/entrypoint.sh Executable file
View File

@ -0,0 +1,51 @@
#!/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

View File

@ -0,0 +1,9 @@
[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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1578
share/uncrustify.cfg Normal file

File diff suppressed because it is too large Load Diff

106
share/updates/hunspell.py Normal file
View File

@ -0,0 +1,106 @@
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)

68
share/updates/tessdata.py Normal file
View File

@ -0,0 +1,68 @@
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)

View File

@ -0,0 +1,38 @@
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('}')

View File

@ -0,0 +1,71 @@
#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_;
}

35
src/capture/capturearea.h Normal file
View File

@ -0,0 +1,35 @@
#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_;
};

View File

@ -0,0 +1,86 @@
#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());
}

View File

@ -0,0 +1,29 @@
#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_;
};

View File

@ -0,0 +1,355 @@
#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();
}

View File

@ -0,0 +1,64 @@
#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_;
};

106
src/capture/capturer.cpp Normal file
View File

@ -0,0 +1,106 @@
#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();
}

31
src/capture/capturer.h Normal file
View File

@ -0,0 +1,31 @@
#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_;
};

Some files were not shown because too many files have changed in this diff Show More