├── .gitignore
├── LICENSE.txt
├── MouseOverDictionary.sln
├── MouseOverDictionary
├── MouseOverDictionary.vcxproj
├── MouseOverDictionary.vcxproj.filters
├── Resources
│ ├── chat-4-line.png
│ ├── chat-off-line.png
│ ├── push-pin-2-line.png
│ ├── push-pin-line.png
│ ├── side-bar-line-hide.png
│ └── side-bar-line.png
├── dictionary.cpp
├── dictionary.h
├── image.cpp
├── image.h
├── inflector.h
├── main.cpp
├── mini_window.cpp
├── mini_window.h
├── mini_window.ui
├── mouse_over_dictionary.cpp
├── mouse_over_dictionary.h
├── mouse_over_dictionary.ico
├── mouse_over_dictionary.qrc
├── mouse_over_dictionary.rc
├── mouse_over_dictionary.ui
├── pause_window.cpp
├── pause_window.h
├── pause_window.ui
├── screen_ocr.cpp
├── screen_ocr.h
├── singularizer.h
├── thread.cpp
└── thread.h
├── README.md
└── licenses
├── LICENSE_Qt.txt
└── LICENSE_Tesseract.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 | *.ini
352 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 kengo700
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MouseOverDictionary.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28307.960
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MouseOverDictionary", "MouseOverDictionary\MouseOverDictionary.vcxproj", "{B12702AD-ABFB-343A-A199-8E24837244A3}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Release|x64 = Release|x64
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.ActiveCfg = Debug|x64
15 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.Build.0 = Debug|x64
16 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.ActiveCfg = Release|x64
17 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.Build.0 = Release|x64
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {56AEB598-3C18-4FE8-BCDC-787461461B69}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/MouseOverDictionary/MouseOverDictionary.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | x64
7 |
8 |
9 | Release
10 | x64
11 |
12 |
13 |
14 | {B12702AD-ABFB-343A-A199-8E24837244A3}
15 | QtVS_v301
16 | 10.0.17763.0
17 |
18 |
19 |
20 | Application
21 | v141
22 |
23 |
24 | Application
25 | v141
26 |
27 |
28 |
29 | $(MSBuildProjectDirectory)\QtMsBuild
30 |
31 |
32 | $(SolutionDir)$(Platform)\$(Configuration)\
33 |
34 |
35 | $(SolutionDir)$(Platform)\$(Configuration)\
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | msvc2017_64
54 | core;gui;widgets
55 |
56 |
57 | msvc2017_64
58 | core;gui;widgets
59 |
60 |
61 |
62 |
63 |
64 |
65 | true
66 | Disabled
67 | ProgramDatabase
68 | MultiThreadedDebugDLL
69 | true
70 | stdcpplatest
71 |
72 |
73 | Windows
74 | $(OutDir)\$(ProjectName).exe
75 | true
76 |
77 |
78 |
79 |
80 | true
81 |
82 | MultiThreadedDLL
83 | true
84 | stdcpplatest
85 | C:\tesseract\build\x64\include;.\;%(AdditionalIncludeDirectories)
86 |
87 |
88 | Windows
89 | $(OutDir)\$(ProjectName).exe
90 | false
91 | C:\tesseract\build\x64\lib;%(AdditionalLibraryDirectories)
92 | tesseract41.lib;leptonica-1.78.0.lib;UGlobalHotkey.lib;%(AdditionalDependencies)
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/MouseOverDictionary/MouseOverDictionary.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}
14 | qrc;*
15 | false
16 |
17 |
18 | {99349809-55BA-4b9d-BF79-8FDBB0286EB3}
19 | ui
20 |
21 |
22 | {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}
23 | qrc;*
24 | false
25 |
26 |
27 |
28 |
29 | Source Files
30 |
31 |
32 | Source Files
33 |
34 |
35 | Source Files
36 |
37 |
38 | Source Files
39 |
40 |
41 | Source Files
42 |
43 |
44 | Source Files
45 |
46 |
47 | Source Files
48 |
49 |
50 | Source Files
51 |
52 |
53 |
54 |
55 | Header Files
56 |
57 |
58 | Header Files
59 |
60 |
61 | Header Files
62 |
63 |
64 | Header Files
65 |
66 |
67 |
68 |
69 | Form Files
70 |
71 |
72 | Form Files
73 |
74 |
75 | Form Files
76 |
77 |
78 |
79 |
80 | Resource Files
81 |
82 |
83 |
84 |
85 | Header Files
86 |
87 |
88 | Header Files
89 |
90 |
91 | Header Files
92 |
93 |
94 | Header Files
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/MouseOverDictionary/Resources/chat-4-line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kengo700/mouse_over_dictionary/e1ba1b5fec67910e3273ccb41b55140814fb1c04/MouseOverDictionary/Resources/chat-4-line.png
--------------------------------------------------------------------------------
/MouseOverDictionary/Resources/chat-off-line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kengo700/mouse_over_dictionary/e1ba1b5fec67910e3273ccb41b55140814fb1c04/MouseOverDictionary/Resources/chat-off-line.png
--------------------------------------------------------------------------------
/MouseOverDictionary/Resources/push-pin-2-line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kengo700/mouse_over_dictionary/e1ba1b5fec67910e3273ccb41b55140814fb1c04/MouseOverDictionary/Resources/push-pin-2-line.png
--------------------------------------------------------------------------------
/MouseOverDictionary/Resources/push-pin-line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kengo700/mouse_over_dictionary/e1ba1b5fec67910e3273ccb41b55140814fb1c04/MouseOverDictionary/Resources/push-pin-line.png
--------------------------------------------------------------------------------
/MouseOverDictionary/Resources/side-bar-line-hide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kengo700/mouse_over_dictionary/e1ba1b5fec67910e3273ccb41b55140814fb1c04/MouseOverDictionary/Resources/side-bar-line-hide.png
--------------------------------------------------------------------------------
/MouseOverDictionary/Resources/side-bar-line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kengo700/mouse_over_dictionary/e1ba1b5fec67910e3273ccb41b55140814fb1c04/MouseOverDictionary/Resources/side-bar-line.png
--------------------------------------------------------------------------------
/MouseOverDictionary/dictionary.cpp:
--------------------------------------------------------------------------------
1 | #include "dictionary.h"
2 |
3 | Dictionary::Dictionary()
4 | {
5 |
6 | }
7 |
8 | bool Dictionary::Load(std::string foldername)
9 | {
10 | // 指定フォルダ内のテキストファイル名を取得
11 | QString path = QApplication::applicationDirPath() + "/" + QString::fromStdString(foldername) + "/";
12 |
13 | QStringList nameFilters;
14 | nameFilters.append("*.txt");
15 | nameFilters.append("*.TXT");
16 |
17 | QDir dir(path);
18 | QStringList files = dir.entryList(nameFilters, QDir::Files);
19 |
20 | // 各ファイルのフォーマットを確認
21 | bool read_success = false;
22 | for (QString file : files) {
23 | std::string filename = dir.filePath(file).toStdString();
24 |
25 | // データ読み込み
26 | switch (getFormat(filename))
27 | {
28 | case EIJIRO:
29 | if (LoadEIJIRO(filename)) {
30 | read_success = true;
31 | }
32 | break;
33 | case EJDIC:
34 | if (LoadEJDIC(filename)) {
35 | read_success = true;
36 | }
37 | break;
38 | case PDIC1:
39 | if (LoadPDIC1(filename)) {
40 | read_success = true;
41 | }
42 | break;
43 | default:
44 | break;
45 | }
46 | }
47 |
48 | return read_success;
49 | }
50 |
51 | // ejdic-hand形式の辞書データを読み込む
52 | // ejdic-hand形式について:https://github.com/kujirahand/EJDict
53 | bool Dictionary::LoadEJDIC(std::string filename)
54 | {
55 | QTextCodec *codec = QTextCodec::codecForName("UTF-8");
56 |
57 | // ファイルを開く
58 | // UTF-8のファイルはin.setCodec()で指定しないと文字化けする
59 | QFile file(filename.c_str());
60 | if (!file.open(QIODevice::ReadOnly)) {
61 | return false;
62 | }
63 | QTextStream in(&file);
64 | in.setCodec("UTF-8");
65 |
66 | QString line;
67 | while (!in.atEnd()) {
68 | line = in.readLine();
69 |
70 | QStringList items = line.split(codec->toUnicode("\t"));
71 |
72 | if (items.size() < 2) {
73 | continue;
74 | }
75 |
76 | // 意味が同じで綴りが少し異なるだけの単語はカンマで区切って列挙されているので、それぞれ登録する
77 | QStringList words = items[0].split(codec->toUnicode(","));
78 |
79 | std::string text = codec->fromUnicode(items[1]).toStdString();
80 |
81 | for (int i = 0; i < words.count(); i++) {
82 | std::string word = codec->fromUnicode(words[i]).toStdString();
83 | // 読み込み済みのワードは追記する
84 | if (data.find(word) != data.end()) {
85 | data[word] = data[word] + "\n" + text;
86 | }
87 | else {
88 | data[word] = text;
89 | }
90 | }
91 | }
92 |
93 | file.close();
94 |
95 | return true;
96 | }
97 |
98 | // 辞郎形式の辞書データを読み込む
99 | // 辞郎形式について:http://www.eijiro.jp/spec.htm
100 | bool Dictionary::LoadEIJIRO(std::string filename)
101 | {
102 | QTextCodec *codec = QTextCodec::codecForName("UTF-8");
103 |
104 | // ファイルを開く
105 | // in.setCodec()で指定しなくても、Shift-JISとUnicodeのファイルは正しく読めるっぽい
106 | QFile file(filename.c_str());
107 | if (!file.open(QIODevice::ReadOnly)) {
108 | return false;
109 | }
110 | QTextStream in(&file);
111 |
112 | // 一行ずつ処理
113 | // in.readAll()で一括で読み込んでsplit("\n")で分割してforeachで処理する方法も試してみたところ、倍以上遅かった
114 | QString line;
115 | while (!in.atEnd()) {
116 | line = in.readLine();
117 |
118 | // 用語と訳語の区切り「 : 」
119 | QStringList items = line.split(codec->toUnicode(" : "));
120 |
121 | if (items.size() < 2) {
122 | continue;
123 | }
124 |
125 | // 先頭の「■」を削除
126 | items[0].remove(codec->toUnicode(u8"■"));
127 |
128 | // 品詞ラベルなどを削除
129 | QStringList items2 = items[0].split(codec->toUnicode("{"));
130 | items[0] = items2[0];
131 |
132 | // 前後の半角スペースを削除
133 | items[0] = items[0].trimmed();
134 |
135 | // 品詞ラベルなどは訳語の先頭に付けておく
136 | if (items2.size() >= 2) {
137 | items[1] = codec->toUnicode("{") + items2[1] + items[1];
138 | }
139 |
140 | std::string word = codec->fromUnicode(items[0]).toStdString();
141 | std::string text = codec->fromUnicode(items[1]).toStdString();
142 |
143 | // 読み込み済みのワードは追記する
144 | if (data.find(word) != data.end()) {
145 | data[word] = data[word] + "\n" + text;
146 | }
147 | else {
148 | data[word] = text;
149 | }
150 | }
151 |
152 | file.close();
153 |
154 | return true;
155 | }
156 |
157 | // PDIC1行テキスト形式の辞書データを読み込む
158 | // PDIC1行テキスト形式について:http://pdic.la.coocan.jp/unicode/help/OneLineFormat.html
159 | bool Dictionary::LoadPDIC1(std::string filename)
160 | {
161 | QTextCodec *codec = QTextCodec::codecForName("UTF-8");
162 |
163 | // ファイルを開く
164 | // in.setCodec()で指定しなくても、Shift-JISとUnicodeのファイルは正しく読めるっぽい
165 | QFile file(filename.c_str());
166 | if (!file.open(QIODevice::ReadOnly)) {
167 | return false;
168 | }
169 | QTextStream in(&file);
170 |
171 | QString line;
172 | while (!in.atEnd()) {
173 | line = in.readLine();
174 |
175 | QStringList items = line.split(codec->toUnicode(" /// "));
176 |
177 | if (items.size() < 2) {
178 | continue;
179 | }
180 |
181 | // 改行指定「 \ 」を改行に置換
182 | items[1] = items[1].replace(codec->toUnicode(" \\ "), codec->toUnicode("\n"));
183 |
184 | // 訳語と用例の区切りは、辞郎フォーマットに合わせて「■・」に置換しておく(要検討)
185 | items[1] = items[1].replace(codec->toUnicode(" / "), codec->toUnicode(u8"■・"));
186 |
187 | std::string word = codec->fromUnicode(items[0]).toStdString();
188 | std::string text = codec->fromUnicode(items[1]).toStdString();
189 |
190 | // 読み込み済みのワードは追記する
191 | if (data.find(word) != data.end()) {
192 | data[word] = data[word] + "\n" + text;
193 | }
194 | else {
195 | data[word] = text;
196 | }
197 | }
198 |
199 | file.close();
200 |
201 | return true;
202 | }
203 |
204 | bool Dictionary::Find(std::string word, std::string& text)
205 | {
206 | if (word != "" && data.find(word) != data.end()) {
207 | text = data[word];
208 | return true;
209 | }
210 | return false;
211 | }
212 |
213 | enum DictionaryFormat Dictionary::getFormat(std::string filename)
214 | {
215 | QTextCodec *codec = QTextCodec::codecForName("UTF-8");
216 |
217 | // ファイルを開く
218 | // C++だとUnicodeのファイル読み込みが難しいので、Qtで読み込み
219 | QFile file(filename.c_str());
220 | if (!file.open(QIODevice::ReadOnly)) {
221 | return OTHER;
222 | }
223 | QTextStream in(&file);
224 |
225 | // 一行目を読み込み
226 | DictionaryFormat format;
227 | QString first_line = in.readLine();
228 |
229 | // 1行目に「■」を含む場合は辞郎フォーマットと判断する(要検討)
230 | if (first_line.contains(codec->toUnicode(u8"■"))) {
231 | format = EIJIRO;
232 | }
233 | // 1行目にタブを含む場合はejdicフォーマットと判断する(要検討)
234 | else if (first_line.contains(codec->toUnicode("\t"))) {
235 | format = EJDIC;
236 | }
237 | // 1行目に「///」を含む場合はPDIC1行テキストフォーマットと判断する(要検討)
238 | else if (first_line.contains(codec->toUnicode("///"))) {
239 | format = PDIC1;
240 | }
241 | else {
242 | format = OTHER;
243 | }
244 |
245 | file.close();
246 | return format;
247 | }
--------------------------------------------------------------------------------
/MouseOverDictionary/dictionary.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | enum DictionaryFormat
16 | {
17 | EJDIC,
18 | EIJIRO,
19 | PDIC1,
20 | OTHER,
21 | };
22 |
23 | class Dictionary
24 | {
25 | public:
26 | Dictionary();
27 |
28 | bool Load(std::string foldername);
29 | bool Find(std::string word, std::string& text);
30 |
31 | private:
32 | std::unordered_map data;
33 |
34 | bool LoadEJDIC(std::string filename);
35 | bool LoadEIJIRO(std::string filename);
36 | bool LoadPDIC1(std::string filename);
37 | enum DictionaryFormat getFormat(std::string filename);
38 |
39 | };
--------------------------------------------------------------------------------
/MouseOverDictionary/image.cpp:
--------------------------------------------------------------------------------
1 | #include "image.h"
2 |
3 | void Image::Flip(void* In, void* Out, int width, int height, unsigned int Bpp)
4 | {
5 | unsigned long Chunk = (Bpp > 24 ? width * 4 : width * 3 + width % 4);
6 | unsigned char* Destination = static_cast(Out);
7 | unsigned char* Source = static_cast(In) + Chunk * (height - 1);
8 |
9 | while (Source != In)
10 | {
11 | std::memcpy(Destination, Source, Chunk);
12 | Destination += Chunk;
13 | Source -= Chunk;
14 | }
15 | }
16 |
17 | Image::Image(HDC DC, int X, int Y, int Width, int Height) : Pixels(), width(Width), height(Height), BitsPerPixel(32)
18 | {
19 | BITMAP Bmp = { 0 };
20 | HBITMAP hBmp = reinterpret_cast(GetCurrentObject(DC, OBJ_BITMAP));
21 |
22 | if (GetObject(hBmp, sizeof(BITMAP), &Bmp) == 0)
23 | throw std::runtime_error("BITMAP DC NOT FOUND.");
24 |
25 | RECT area = { X, Y, X + Width, Y + Height };
26 | HWND Window = WindowFromDC(DC);
27 | GetClientRect(Window, &area);
28 |
29 | HDC MemDC = GetDC(nullptr);
30 | HDC SDC = CreateCompatibleDC(MemDC);
31 | HBITMAP hSBmp = CreateCompatibleBitmap(MemDC, width, height);
32 | DeleteObject(SelectObject(SDC, hSBmp));
33 |
34 | BitBlt(SDC, 0, 0, width, height, DC, X, Y, SRCCOPY);
35 | StretchBlt(SDC, 0, 0, width, height, DC, X, Y, Width, Height, SRCCOPY);
36 |
37 | unsigned int data_size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
38 | std::vector Data(data_size);
39 | this->Pixels.resize(data_size);
40 |
41 | // キャプチャした画像を画面左上に表示(デバッグ用)
42 | //BitBlt(DC, 0, 0, width, height, SDC, 0, 0, SRCCOPY);
43 |
44 | BITMAPINFO Info = { sizeof(BITMAPINFOHEADER), static_cast(width), static_cast(height), 1, BitsPerPixel, BI_RGB, data_size, 0, 0, 0, 0 };
45 | GetDIBits(SDC, hSBmp, 0, height, &Data[0], &Info, DIB_RGB_COLORS);
46 | this->Flip(&Data[0], &Pixels[0], width, height, BitsPerPixel);
47 |
48 | DeleteDC(SDC);
49 | DeleteObject(hSBmp);
50 | ReleaseDC(nullptr, MemDC);
51 | }
52 |
53 | // 文字認識精度を上げるため、拡大してキャプチャする
54 | Image::Image(HDC DC, int X, int Y, int Width, int Height, int scale) : Pixels(), width(Width), height(Height), BitsPerPixel(32)
55 | {
56 | width = (int)((width * scale) / 100);
57 | height = (int)((height * scale) / 100);
58 |
59 | BITMAP Bmp = { 0 };
60 | HBITMAP hBmp = reinterpret_cast(GetCurrentObject(DC, OBJ_BITMAP));
61 |
62 | if (GetObject(hBmp, sizeof(BITMAP), &Bmp) == 0)
63 | throw std::runtime_error("BITMAP DC NOT FOUND.");
64 |
65 | RECT area = { X, Y, X + Width, Y + Height };
66 | HWND Window = WindowFromDC(DC);
67 | GetClientRect(Window, &area);
68 |
69 | HDC MemDC = GetDC(nullptr);
70 | HDC SDC = CreateCompatibleDC(MemDC);
71 | HBITMAP hSBmp = CreateCompatibleBitmap(MemDC, width, height);
72 | DeleteObject(SelectObject(SDC, hSBmp));
73 |
74 | //SetStretchBltMode(SDC, BLACKONWHITE); // 伸縮モードBLACKONWHITE:低画質だが高速、デフォルト設定
75 | //SetStretchBltMode(SDC, COLORONCOLOR); // 伸縮モードCOLORONCOLOR:中画質で高速、デフォルト設定
76 | SetStretchBltMode(SDC, HALFTONE); // 伸縮モードHALFTONE:高画質だが低速
77 | SetBrushOrgEx(SDC, 0, 0, NULL);
78 | StretchBlt(SDC, 0, 0, width, height, DC, X, Y, Width, Height, SRCCOPY);
79 |
80 | unsigned int data_size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
81 | std::vector Data(data_size);
82 | this->Pixels.resize(data_size);
83 |
84 | // キャプチャした画像を画面左上に表示(デバッグ用)
85 | //BitBlt(DC, 0, 0, width, height, SDC, 0, 0, SRCCOPY);
86 |
87 | BITMAPINFO Info = { sizeof(BITMAPINFOHEADER), static_cast(width), static_cast(height), 1, BitsPerPixel, BI_RGB, data_size, 0, 0, 0, 0 };
88 | GetDIBits(SDC, hSBmp, 0, height, &Data[0], &Info, DIB_RGB_COLORS);
89 | this->Flip(&Data[0], &Pixels[0], width, height, BitsPerPixel);
90 |
91 | DeleteDC(SDC);
92 | DeleteObject(hSBmp);
93 | ReleaseDC(nullptr, MemDC);
94 | }
--------------------------------------------------------------------------------
/MouseOverDictionary/image.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | // Reference: https://stackoverflow.com/questions/22924209
4 | // Author: Brandon https://stackoverflow.com/users/1462718/brandon
5 | // License: Creative Commons Attribution-Share Alike
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | class Image
16 | {
17 | private:
18 | std::vector Pixels;
19 | std::uint32_t width, height;
20 | std::uint16_t BitsPerPixel;
21 |
22 | void Flip(void* In, void* Out, int width, int height, unsigned int Bpp);
23 |
24 | public:
25 | explicit Image(HDC DC, int X, int Y, int Width, int Height);
26 | explicit Image(HDC DC, int X, int Y, int Width, int Height, int scale);
27 |
28 | inline std::uint16_t GetBitsPerPixel() { return this->BitsPerPixel; }
29 | inline std::uint16_t GetBytesPerPixel() { return this->BitsPerPixel / 8; }
30 | inline std::uint16_t GetBytesPerScanLine() { return (this->BitsPerPixel / 8) * this->width; }
31 | inline int GetWidth() const { return this->width; }
32 | inline int GetHeight() const { return this->height; }
33 | inline const std::uint8_t* GetPixels() { return this->Pixels.data(); }
34 | };
--------------------------------------------------------------------------------
/MouseOverDictionary/inflector.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class Inflector
7 | {
8 | // まとめて原型の候補を生成
9 | public:
10 | bool getInfinitives(std::string word, std::vector& infinitives) {
11 | bool is_infinitive = false;
12 | is_infinitive = !processPlural(word, infinitives);
13 | is_infinitive = !processPastTense(word, infinitives);
14 | is_infinitive = !processParticiple(word, infinitives);
15 | is_infinitive = !processPronoun(word, infinitives);
16 | return is_infinitive;
17 | }
18 |
19 | // 複数形、三人称単数現在形
20 | public:
21 | bool processPlural(int rule_set_num, std::string plural, std::string& singular) {
22 | singular = plural;
23 | bool is_plural = false;
24 | if (rule_set_num > singular_rules_set.size()) {
25 | rule_set_num = singular_rules_set.size();
26 | }
27 | for (auto singular_rule : singular_rules_set[rule_set_num]) {
28 | if (std::regex_match(plural, std::regex(singular_rule[0]))) {
29 | singular = std::regex_replace(plural, std::regex(singular_rule[0]), singular_rule[1]);
30 | is_plural = true;
31 | return is_plural;
32 | }
33 | }
34 | return is_plural;
35 | }
36 |
37 | bool processPlural(std::string plural, std::vector& singular) {
38 | bool is_plural = false;
39 | for (auto singular_rules : singular_rules_set) {
40 | for (auto singular_rule : singular_rules) {
41 | if (std::regex_match(plural, std::regex(singular_rule[0]))) {
42 | singular.push_back(std::regex_replace(plural, std::regex(singular_rule[0]), singular_rule[1]));
43 | return is_plural;
44 | }
45 | }
46 | }
47 | return is_plural;
48 | }
49 |
50 | private:
51 | // Reference: https://gist.github.com/mrenouf/805745
52 | // Author: mrenouf https://gist.github.com/mrenouf
53 | std::vector>> singular_rules_set =
54 | {
55 | {
56 | {"(.*)people$", "$1person"},
57 | {"oxen$", "ox"},
58 | {"children$", "child"},
59 | {"feet$", "foot"},
60 | {"teeth$", "tooth"},
61 | {"geese$", "goose"},
62 | {"(.*)ives?$", "$1ife"},
63 | {"(.*)ves?$", "$1f"},
64 | {"(.*)men$", "$1man"},
65 | {"(.+[aeiou])ys$", "$1y"},
66 | {"(.+[^aeiou])ies$", "$1y"},
67 | {"(.+)zes$", "$1"},
68 | {"([m|l])ice$", "$1ouse"},
69 | {"matrices$", "matrix"},
70 | {"indices$", "index"},
71 | {"(.+[^aeiou])ices$", "$1ice"},
72 | {"(.*)ices$", "$1ex"},
73 | {"(octop|vir)i$", "$1us"},
74 | {"(.+(s|x|sh|ch))es$", "$1"},
75 | {"(.+)s$", "$1"},
76 | },{
77 | {"(.+)s$", "$1"}, // pulses → pulseなど
78 | },{
79 | {u8"(.+)'s$", "$1"}, // 所有格、記号の誤認識は多いので、それぞれ処理する
80 | {u8"(.+)'s$", "$1"},
81 | {u8"(.+)ʼs$", "$1"},
82 | {u8"(.+)’s$", "$1"},
83 | {u8"(.+)‘s$", "$1"},
84 | }
85 | };
86 |
87 | // 過去形(仮)
88 | public:
89 | bool processPastTense(int rule_set_num, std::string pasttense, std::string& presenttense) {
90 | presenttense = pasttense;
91 | bool is_pasttense = false;
92 | if (rule_set_num > pasttense_rules_set.size()) {
93 | rule_set_num = pasttense_rules_set.size();
94 | }
95 | for (auto pasttense_rule : pasttense_rules_set[rule_set_num]) {
96 | if (std::regex_match(pasttense, std::regex(pasttense_rule[0]))) {
97 | presenttense = std::regex_replace(pasttense, std::regex(pasttense_rule[0]), pasttense_rule[1]);
98 | is_pasttense = true;
99 | return is_pasttense;
100 | }
101 | }
102 | return is_pasttense;
103 | }
104 |
105 | bool processPastTense(std::string pasttense, std::vector& presenttense) {
106 | bool is_pasttense = false;
107 | for (auto pasttense_rules : pasttense_rules_set) {
108 | for (auto pasttense_rule : pasttense_rules) {
109 | if (std::regex_match(pasttense, std::regex(pasttense_rule[0]))) {
110 | presenttense.push_back(std::regex_replace(pasttense, std::regex(pasttense_rule[0]), pasttense_rule[1]));
111 | is_pasttense = true;
112 | }
113 | }
114 | }
115 | return is_pasttense;
116 | }
117 |
118 | private:
119 | std::vector>> pasttense_rules_set =
120 | {
121 | {
122 | {"abode", "abide"},
123 | {"addrest", "address"},
124 | {"arose$", "arise"},
125 | {"arisen$", "arise"},
126 | {"awoke$", "awake"},
127 | {"awoken$", "awake"},
128 | {"was$", "be"},
129 | {"were$", "be"},
130 | {"been$", "be"},
131 | {"bore$", "bear"},
132 | {"borne$", "bear"},
133 | {"born$", "bear"},
134 | {"beaten$", "beat"},
135 | {"became$", "become"},
136 | {"become$", "become"},
137 | {"began$", "begin"},
138 | {"begun$", "begin"},
139 | {"bent$", "bend"},
140 | {"blew$", "blow"},
141 | {"blown$", "blow"},
142 | {"broke$", "break"},
143 | {"broken$", "break"},
144 | {"brought$", "bring"},
145 | {"built$", "build"},
146 | {"bought$", "buy"},
147 | {"caught$", "catch"},
148 | {"chose$", "choose"},
149 | {"chosen$", "choose"},
150 | {"came$", "come"},
151 | {"dealt$", "deal"},
152 | {"dug$", "dig"},
153 | {"did$", "do"},
154 | {"done$", "do"},
155 | {"drew$", "draw"},
156 | {"drawn$", "draw"},
157 | {"drank$", "drink"},
158 | {"drunk$", "drink"},
159 | {"drove$", "drive"},
160 | {"driven$", "drive"},
161 | {"ate$", "eat"},
162 | {"eaten$", "eat"},
163 | {"fell$", "fall"},
164 | {"fallen$", "fall"},
165 | {"fed$", "feed"},
166 | {"felt$", "feel"},
167 | {"fought$", "fight"},
168 | {"found$", "find"},
169 | {"flew$", "fly"},
170 | {"flown$", "fly"},
171 | {"forgot$", "forget"},
172 | {"forgotten$", "forget"},
173 | {"got$", "get"},
174 | {"gotten$", "get"},
175 | {"gave$", "give"},
176 | {"given$", "give"},
177 | {"went$", "go"},
178 | {"gone$", "go"},
179 | {"grew$", "grow"},
180 | {"grown$", "grow"},
181 | {"hung$", "hang"},
182 | {"had$", "have"},
183 | {"heard$", "hear"},
184 | {"hid$", "hide"},
185 | {"hidden$", "hide"},
186 | {"held$", "hold"},
187 | {"kept$", "keep"},
188 | {"knew$", "know"},
189 | {"known$", "know"},
190 | {"laid$", "lay"},
191 | {"led$", "lead"},
192 | {"left$", "leave"},
193 | {"lent$", "lend"},
194 | {"lay$", "lie"},
195 | {"lain$", "lie"},
196 | {"lost$", "lose"},
197 | {"made$", "make"},
198 | {"met$", "meet"},
199 | {"mistaken", "mistake"},
200 | {"mistook", "mistake"},
201 | {"paid$", "pay"},
202 | {"rode$", "ride"},
203 | {"ridden$", "ride"},
204 | {"rang$", "ring"},
205 | {"rung$", "ring"},
206 | {"rose$", "rise"},
207 | {"risen$", "rise"},
208 | {"ran$", "run"},
209 | {"sawn$", "saw"},
210 | {"said$", "say"},
211 | {"saw$", "see"},
212 | {"seen$", "see"},
213 | {"sought$", "seek"},
214 | {"sold$", "sell"},
215 | {"sent$", "send"},
216 | {"sewed$", "sew"},
217 | {"sewn$", "sew"},
218 | {"shook$", "shake"},
219 | {"shaken$", "shake"},
220 | {"shot$", "shoot"},
221 | {"shown$", "show"},
222 | {"shrank$", "shrink"},
223 | {"sang$", "sing"},
224 | {"sung$", "sing"},
225 | {"sank$", "sink"},
226 | {"sunk$", "sink"},
227 | {"slept$", "sleep"},
228 | {"spoke$", "speak"},
229 | {"spoken$", "speak"},
230 | {"spent$", "spend"},
231 | {"stood$", "stand"},
232 | {"stole$", "steal"},
233 | {"stolen$", "steal"},
234 | {"stuck$", "stick"},
235 | {"swore$", "swear"},
236 | {"sworn$", "swear"},
237 | {"swam$", "swim"},
238 | {"swum$", "swim"},
239 | {"took$", "take"},
240 | {"taken$", "take"},
241 | {"taught$", "teach"},
242 | {"tore$", "tear"},
243 | {"torn$", "tear"},
244 | {"told$", "tell"},
245 | {"thought$", "think"},
246 | {"threw$", "throw"},
247 | {"thrown$", "throw"},
248 | {"understood$", "understand"},
249 | {"undertook$", "undertake" },
250 | {"undertaken$", "undertake" },
251 | {"woke$", "wake"},
252 | {"woken$", "wake"},
253 | {"wore$", "wear"},
254 | {"worn$", "wear"},
255 | {"wove$", "weave"},
256 | {"woven$", "weave"},
257 | {"won$", "win"},
258 | {"withdrew$", "withdraw"},
259 | {"withdrawn$", "withdraw"},
260 | {"withheld$", "withhold"},
261 | {"withstood$", "withstand"},
262 | {"wrote$", "write"},
263 | {"written$", "write"},
264 | {"(.+)lit", "$1alight" },
265 | {"(.+)slid", "$1slide" },
266 | {"(.+)slidden", "$1slide" },
267 | {"(.+)bit$", "$1bite"},
268 | {"(.+)bitten$", "$1bite"},
269 | {"(.+)sat$", "$1sit"},
270 | {"(.+)cked$", "$1c"},
271 | {"(.+)nned$", "$1n"},
272 | {"(.+)tted$", "$1t"},
273 | {"(.+)dded$", "$1d"},
274 | {"(.+)gged$", "$1g"},
275 | {"(.+)pped$", "$1p"},
276 | {"(.+)mmed$", "$1m"},
277 | {"(.+)bbed$", "$1b"},
278 | {"(.+)rred$", "$1r"},
279 | {"(.+)zzed$", "$1z"},
280 | {"(.+)ied$", "$1y"},
281 | {"(.+)ed$", "$1"},
282 | },
283 | {
284 | {"(.+)ed$", "$1e"}, // agreed → agreeなど
285 | }
286 | };
287 |
288 |
289 | // 現在分詞(仮)
290 | public:
291 | bool processParticiple(int rule_set_num, std::string participle, std::string& infinitive) {
292 | infinitive = participle;
293 | bool is_participle = false;
294 | if (rule_set_num > participle_rules_set.size()) {
295 | rule_set_num = participle_rules_set.size();
296 | }
297 | for (auto participle_rule : participle_rules_set[rule_set_num]) {
298 | if (std::regex_match(participle, std::regex(participle_rule[0]))) {
299 | infinitive = std::regex_replace(participle, std::regex(participle_rule[0]), participle_rule[1]);
300 | is_participle = true;
301 | return is_participle;
302 | }
303 | }
304 | return is_participle;
305 | }
306 |
307 | bool processParticiple(std::string participle, std::vector& infinitives) {
308 | bool is_participle = false;
309 | for (auto participle_rules : participle_rules_set) {
310 | for (auto participle_rule : participle_rules) {
311 | if (std::regex_match(participle, std::regex(participle_rule[0]))) {
312 | infinitives.push_back(std::regex_replace(participle, std::regex(participle_rule[0]), participle_rule[1]));
313 | is_participle = true;
314 | }
315 | }
316 | }
317 | return is_participle;
318 | }
319 |
320 | private:
321 | std::vector>> participle_rules_set =
322 | {
323 | {
324 | {"(.+)cking$", "$1c"}, // picnicking → picnicなど
325 | {"(.+)nning$", "$1n"},
326 | {"(.+)tting$", "$1t"},
327 | {"(.+)dding$", "$1d"},
328 | {"(.+)pping$", "$1p"},
329 | {"(.+)mming$", "$1m"},
330 | {"(.+)bbing$", "$1b"},
331 | {"(.+)rring$", "$1r"},
332 | {"(.+)lling$", "$1l"},
333 | {"(.+)zzing$", "$1z"},
334 | {"(.+)ying$", "$1ie"}, // dying → dieなど
335 | {"(.+)ing$", "$1"},
336 | },
337 | {
338 | {"(.+)ing$", "$1e"}, // coming → comeなど
339 | },
340 | {
341 | {"(.+)ing$", "$1"}, // replaying → replayなど
342 | }
343 | };
344 |
345 | // 代名詞
346 | public:
347 | bool processPronoun(int rule_set_num, std::string pronoun, std::string& general) {
348 | general = pronoun;
349 | bool is_pronoun = false;
350 | if (rule_set_num > pronoun_rules_set.size()) {
351 | rule_set_num = pronoun_rules_set.size();
352 | }
353 | for (auto pronoun_rule : pronoun_rules_set[rule_set_num]) {
354 | if (std::regex_match(pronoun, std::regex(pronoun_rule[0]))) {
355 | general = std::regex_replace(pronoun, std::regex(pronoun_rule[0]), pronoun_rule[1]);
356 | is_pronoun = true;
357 | return is_pronoun;
358 | }
359 | }
360 | return is_pronoun;
361 | }
362 |
363 | bool processPronoun(std::string pronoun, std::vector& general) {
364 | bool is_pronoun = false;
365 | for (auto pronoun_rules : pronoun_rules_set) {
366 | for (auto pronoun_rule : pronoun_rules) {
367 | if (std::regex_match(pronoun, std::regex(pronoun_rule[0]))) {
368 | general.push_back(std::regex_replace(pronoun, std::regex(pronoun_rule[0]), pronoun_rule[1]));
369 | is_pronoun = true;
370 | }
371 | }
372 | }
373 | return is_pronoun;
374 | }
375 |
376 | private:
377 | // Reference: https://github.com/wtetsu/mouse-dictionary/tree/master/rule
378 | // Author: wtetsu https://github.com/wtetsu
379 | std::vector>> pronoun_rules_set =
380 | {
381 | {
382 | {"my", "one's"},
383 | {"your", "one's"},
384 | {"his", "one's"},
385 | {"her", "one's"},
386 | {"its", "one's"},
387 | {"our", "one's"},
388 | {"their", "one's"},
389 | {"'s", "one's"},
390 | {"one's", "one's"},
391 | {"someone's", "someone's"},
392 | {"myself", "oneself"},
393 | {"yourself", "oneself"},
394 | {"himself", "oneself"},
395 | {"herself", "oneself"},
396 | {"ourselves", "oneself"},
397 | {"themselves", "oneself"},
398 | {"him", "someone"},
399 | {"them", "someone"},
400 | {"us", "someone"}
401 | },
402 | {
403 | {"my", "someone's"},
404 | {"your", "someone's"},
405 | {"his", "someone's"},
406 | {"her", "someone's"},
407 | {"its", "someone's"},
408 | {"our", "someone's"},
409 | {"their", "someone's"},
410 | {"'s", "someone's"},
411 | {"one's", "one's"},
412 | {"someone's", "someone's"},
413 | {"myself", "oneself"},
414 | {"yourself", "oneself"},
415 | {"himself", "oneself"},
416 | {"herself", "oneself"},
417 | {"ourselves", "oneself"},
418 | {"themselves", "oneself"},
419 | {"him", "someone"},
420 | {"them", "someone"},
421 | {"us", "someone"}
422 | },
423 | {
424 | {"my", "someone's"},
425 | {"your", "someone's"},
426 | {"his", "someone's"},
427 | {"her", "someone"},
428 | {"its", "someone's"},
429 | {"our", "someone's"},
430 | {"their", "someone's"},
431 | {"'s", "someone's"},
432 | {"one's", "one's"},
433 | {"someone's", "someone's"},
434 | {"myself", "oneself"},
435 | {"yourself", "oneself"},
436 | {"himself", "oneself"},
437 | {"herself", "oneself"},
438 | {"ourselves", "oneself"},
439 | {"themselves", "oneself"},
440 | {"him", "someone"},
441 | {"them", "someone"},
442 | {"us", "someone"}
443 | }
444 | };
445 | };
446 |
447 |
--------------------------------------------------------------------------------
/MouseOverDictionary/main.cpp:
--------------------------------------------------------------------------------
1 | #include "mouse_over_dictionary.h"
2 | #include
3 |
4 | int main(int argc, char *argv[])
5 | {
6 | // 高DPI用の設定
7 | QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
8 |
9 | QApplication a(argc, argv);
10 | MouseOverDictionary w;
11 |
12 | w.show();
13 | return a.exec();
14 | }
--------------------------------------------------------------------------------
/MouseOverDictionary/mini_window.cpp:
--------------------------------------------------------------------------------
1 | #include "mini_window.h"
2 |
3 | MiniWindow::MiniWindow(QWidget *parent)
4 | : QWidget(parent)
5 | {
6 | ui.setupUi(this);
7 | }
8 |
9 | MiniWindow::~MiniWindow()
10 | {
11 | }
12 |
13 | void MiniWindow::setRelativePos(int x, int y)
14 | {
15 | relative_pos_x = x;
16 | relative_pos_y = y;
17 | }
18 |
19 | void MiniWindow::followCursor()
20 | {
21 | POINT po;
22 | GetCursorPos(&po);
23 | this->move(po.x + relative_pos_x, po.y + relative_pos_y);
24 | }
25 |
26 | void MiniWindow::setHtml(QString text)
27 | {
28 | ui.textBrowser->setHtml(text);
29 | }
30 |
31 | void MiniWindow::toggleShow(bool toggle)
32 | {
33 | if (toggle) {
34 | this->show();
35 | }
36 | else {
37 | this->hide();
38 | }
39 | }
--------------------------------------------------------------------------------
/MouseOverDictionary/mini_window.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "ui_mini_window.h"
7 |
8 | class MiniWindow : public QWidget
9 | {
10 | Q_OBJECT
11 |
12 | public:
13 | MiniWindow(QWidget *parent = Q_NULLPTR);
14 | ~MiniWindow();
15 | void setRelativePos(int x, int y);
16 |
17 | public slots:
18 | void followCursor();
19 | void setHtml(QString text);
20 | void toggleShow(bool toggle);
21 |
22 | private:
23 | int relative_pos_x;
24 | int relative_pos_y;
25 | Ui::MiniWindow ui;
26 | };
27 |
--------------------------------------------------------------------------------
/MouseOverDictionary/mini_window.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MiniWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 250
10 | 70
11 |
12 |
13 |
14 | Mini Window
15 |
16 |
17 | 0.800000000000000
18 |
19 |
20 |
21 | 0
22 |
23 |
24 | 0
25 |
26 |
27 | 0
28 |
29 |
30 | 0
31 |
32 | -
33 |
34 |
35 | QFrame::NoFrame
36 |
37 |
38 | Qt::ScrollBarAlwaysOff
39 |
40 |
41 | Qt::ScrollBarAlwaysOff
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/MouseOverDictionary/mouse_over_dictionary.cpp:
--------------------------------------------------------------------------------
1 | #include "mouse_over_dictionary.h"
2 |
3 | MouseOverDictionary::MouseOverDictionary(QWidget *parent)
4 | : QMainWindow(parent)
5 | {
6 | ui.setupUi(this);
7 |
8 | // スクリーンのサイズ取得
9 | QScreen *screen = QGuiApplication::primaryScreen();
10 | QRect screenGeometry = screen->geometry();
11 | screen_width = screenGeometry.width();
12 | screen_height = screenGeometry.height();
13 |
14 | // 設定ファイル読み込み
15 | ReadSettings();
16 |
17 | // 透明度設定
18 | this->setWindowOpacity(main_window_opacity);
19 |
20 | // 前回終了時のウィンドウ位置、サイズを復元
21 | this->move(main_window_pos_x, main_window_pos_y);
22 | this->resize(main_window_width, main_window_height);
23 |
24 | // 前回終了時の最前面設定を復元
25 | if (stay_top == true) {
26 | this->setWindowFlags(Qt::WindowStaysOnTopHint);
27 | ui.toolButton_2->setIcon(QIcon(":/MouseOverDictionary/Resources/push-pin-2-line.png"));
28 | }
29 | else {
30 | ui.toolButton_2->setIcon(QIcon(":/MouseOverDictionary/Resources/push-pin-line.png"));
31 | }
32 |
33 |
34 | // マウス追従ウィンドウ作成
35 | // 最小化や最前面設定を独立にしたいので、メインウィンドウの子(new MiniWindow(this);)にはしない
36 | // closeEventで削除する
37 | mini_window = new MiniWindow();
38 |
39 | // マウス追従ウィンドウを最前面、枠なしに指定
40 | mini_window->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
41 |
42 | // 透明度、サイズ、位置設定
43 | mini_window->setWindowOpacity(mini_window_opacity);
44 | mini_window->resize(mini_window_width, mini_window_height);
45 | mini_window->setRelativePos(mini_window_pos_x, mini_window_pos_y);
46 |
47 | // マウス追従ウィンドウをカーソルに追従
48 | auto timer = new QTimer(this);
49 | timer->setInterval(15);
50 | timer->setSingleShot(false);
51 | connect(timer, SIGNAL(timeout()), mini_window, SLOT(followCursor()));
52 | timer->start();
53 |
54 | // 前回終了時のマウス追従ウィンドウの表示状態を復元
55 | if (mini_show == true) {
56 | mini_window->show();
57 | ui.toolButton->setIcon(QIcon(":/MouseOverDictionary/Resources/chat-4-line.png"));
58 | }
59 | else {
60 | mini_window->hide();
61 | ui.toolButton->setIcon(QIcon(":/MouseOverDictionary/Resources/chat-off-line.png"));
62 | }
63 |
64 |
65 | // 画面停止ウィンドウ作成
66 | // 最小化や最前面設定を独立にしたいので、メインウィンドウの子(new PauseWindow(this);)にはしない
67 | // closeEventで削除する
68 | pause_window = new PauseWindow();
69 |
70 | // 画面停止ウィンドウ設定
71 | pause_window->setWindowOpacity(pause_window_opacity);
72 | pause_window->setStyleSheet(QString::fromStdString("QScrollArea {border: " + std::to_string(pause_window_border_width) + "px solid " + pause_window_border_color + ";}"));
73 |
74 |
75 | // 検索履歴をダブルクリックで編集できないように設定
76 | ui.listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
77 |
78 | // 編集履歴のサイズを小さく設定
79 | ui.splitter->setSizes(QList() << 100 << 300);
80 |
81 | // 検索履歴モデルを設定
82 | historyModel = new QStringListModel(this);
83 | QStringList historyList;
84 | historyModel->setStringList(historyList);
85 | ui.listView->setModel(historyModel);
86 |
87 | // 前回終了時の検索履歴表示状態を復元
88 | if (history_show == true) {
89 | ui.toolButton_3->setIcon(QIcon(":/MouseOverDictionary/Resources/side-bar-line.png"));
90 | ui.listView->show();
91 | }
92 | else {
93 | ui.toolButton_3->setIcon(QIcon(":/MouseOverDictionary/Resources/side-bar-line-hide.png"));
94 | ui.listView->hide();
95 | }
96 |
97 |
98 | // 準備完了時にワード欄を有効化
99 | connect(&thread, SIGNAL(ready(bool)), ui.lineEdit, SLOT(setEnabled(bool)));
100 | connect(&thread, SIGNAL(ready(bool)), this, SLOT(setReady(bool)));
101 |
102 | // 文字認識の結果をワード欄にセット
103 | connect(&thread, SIGNAL(wordChanged(QString)), ui.lineEdit, SLOT(setText(QString)));
104 |
105 | // ワード欄が変化した場合(文字認識 or 直接入力 or 履歴クリック)に、辞書を検索する
106 | connect(ui.lineEdit, SIGNAL(textChanged(QString)), &thread, SLOT(search(QString)));
107 |
108 | // 辞書検索の結果をテキスト欄にセット
109 | connect(&thread, SIGNAL(mainTextChanged(QString)), ui.textBrowser, SLOT(setHtml(QString)));
110 | connect(&thread, SIGNAL(miniTextChanged(QString)), mini_window, SLOT(setHtml(QString)));
111 |
112 | // 検索履歴を記録
113 | connect(&thread, SIGNAL(wordFound(QString)), this, SLOT(updateHistory(QString)));
114 |
115 | // 検索履歴をクリックしたときに、その項目をワード欄にセット
116 | connect(ui.listView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
117 | this, SLOT(searchByHistory(QItemSelection)));
118 |
119 | // クリップボードが変化したときに、その内容をワード欄にセット
120 | clipboard = QApplication::clipboard();
121 | connect(clipboard, SIGNAL(dataChanged()), this, SLOT(setFromClipboard()));
122 |
123 |
124 | // キーボードショートカット設定
125 | // new UGlobalHotkeys(this)として紐づけると、なぜかメインウィンドウの左上部分がクリックできなくなった
126 | // なので紐づけずにcloseEvent()で削除することに
127 | hm_show_hide_both = new UGlobalHotkeys();
128 | hm_show_hide_both->registerHotkey(hotkey_show_hide_both);
129 | connect(hm_show_hide_both, &UGlobalHotkeys::activated, [&](size_t id) {
130 | if (enable_shortcut) {
131 | showHide();
132 | }
133 | });
134 |
135 | hm_show_hide_mini = new UGlobalHotkeys();
136 | hm_show_hide_mini->registerHotkey(hotkey_show_hide_mini);
137 | connect(hm_show_hide_mini, &UGlobalHotkeys::activated, [&](size_t id) {
138 | if (enable_shortcut) {
139 | showHideMini();
140 | }
141 | });
142 |
143 | hm_show_hide_pause = new UGlobalHotkeys();
144 | hm_show_hide_pause->registerHotkey(hotkey_screen_pause);
145 | connect(hm_show_hide_pause, &UGlobalHotkeys::activated, [&](size_t id) {
146 | if (enable_shortcut) {
147 | showPauseWindow();
148 | }
149 | });
150 |
151 | // 文字認識スレッド開始
152 | thread.start();
153 |
154 | }
155 |
156 | MouseOverDictionary::~MouseOverDictionary()
157 | {
158 | // 設定ファイル書き込み
159 | WriteSettings();
160 | }
161 |
162 | void MouseOverDictionary::ReadSettings()
163 | {
164 | QSettings settings("settings.ini", QSettings::IniFormat);
165 | settings.setIniCodec(QTextCodec::codecForName("UTF-8"));
166 |
167 | // デザインの設定
168 | settings.beginGroup("Color");
169 | main_window_word_font_color = settings.value("MainWindowWord", "#000088").toString().toStdString();
170 | main_window_text_font_color = settings.value("MainWindowText", "#101010").toString().toStdString();
171 | main_window_mark_font_color = settings.value("MainWindowMark", "#008000").toString().toStdString();
172 | main_window_background_color = settings.value("MainWindowBackground", "#ffffff").toString().toStdString();
173 | mini_window_word_font_color = settings.value("MiniWindowWord", "#000088").toString().toStdString();
174 | mini_window_text_font_color = settings.value("MiniWindowText", "#101010").toString().toStdString();
175 | mini_window_background_color = settings.value("MiniWindowBackground", "#ffffff").toString().toStdString();
176 | settings.endGroup();
177 |
178 | settings.beginGroup("FontSize");
179 | main_window_word_font_size = settings.value("MainWindowWord", 10).toInt();
180 | main_window_text_font_size = settings.value("MainWindowText", 10).toInt();
181 | main_window_mark_font_size = settings.value("MainWindowMark", 10).toInt();
182 | mini_window_word_font_size = settings.value("MiniWindowWord", 10).toInt();
183 | mini_window_text_font_size = settings.value("MiniWindowText", 10).toInt();
184 | settings.endGroup();
185 |
186 |
187 | // キーボードショートカットの設定
188 | settings.beginGroup("Shortcut");
189 | enable_shortcut = settings.value("Enable", true).toBool();
190 | hotkey_show_hide_both = settings.value("ShowHideBothWindow", "Ctrl+Alt+Z").toString();
191 | hotkey_show_hide_mini = settings.value("ShowHideMiniWindow", "Ctrl+Alt+X").toString();
192 | hotkey_screen_pause = settings.value("ScreenPause", "Ctrl+Alt+A").toString();
193 | settings.endGroup();
194 |
195 |
196 | // 各ウィンドウの設定
197 | settings.beginGroup("Window");
198 | main_window_opacity = settings.value("MainWindowOpacity", 0.9).toDouble();
199 | mini_window_opacity = settings.value("MiniWindowOpacity", 0.9).toDouble();
200 | mini_window_width = settings.value("MiniWindowWidth", 250).toInt();
201 | mini_window_height = settings.value("MiniWindowHeight", 70).toInt();
202 | mini_window_pos_x = settings.value("MiniWindowPositionX", 0).toInt(); // マウス追従ウィンドウがカーソルに被るとクリックができなくなるので注意
203 | mini_window_pos_y = settings.value("MiniWindowPositionY", 25).toInt();
204 | settings.endGroup();
205 |
206 |
207 | // 画面停止機能の設定
208 | settings.beginGroup("ScreenPause");
209 | pause_window_border_width = settings.value("BorderThickness", 1).toInt();
210 | pause_window_border_color = settings.value("BorderColor", "#ff4500").toString().toStdString();
211 | pause_window_opacity = settings.value("Opacity", 0.9).toDouble();
212 | settings.endGroup();
213 |
214 |
215 | // 文字認識の設定
216 | settings.beginGroup("OCR");
217 | // 画面キャプチャの範囲、カーソルから左右上下のピクセル数、キャプチャ幅 = AreaLeft + AreaRight、キャプチャ高さ = AreaTop + AreaBottom、範囲:1~500
218 | ocr_area_left = settings.value("CaptureAreaLeft", 50).toInt();
219 | ocr_area_right = settings.value("CaptureAreaRight", 150).toInt();
220 | ocr_area_top = settings.value("CaptureAreaTop", 20).toInt();
221 | ocr_area_bottom = settings.value("CaptureAreaBottom", 20).toInt();
222 | // 文字認識の精度を上げるためにキャプチャ後の画像を拡大する倍率、100→等倍、150→1.5倍、範囲:100~200
223 | ocr_scale = settings.value("CaptureScale", 130).toInt();
224 | settings.endGroup();
225 |
226 |
227 | // 前回終了時のウィンドウの情報 // saveGeometry()、restoreGeometry()という機能もあるらしいけど、とりあえずこれで
228 | settings.beginGroup("PreviousState");
229 | main_window_width = settings.value("MainWindowWidth", 350).toInt();
230 | main_window_height = settings.value("MainWindowHeight", 300).toInt();
231 | main_window_pos_x = settings.value("MainWindowPositionX", (screen_width - main_window_width) / 2).toInt(); // デフォルトは画面の中央に
232 | main_window_pos_y = settings.value("MainWindowPositionY", (screen_height - main_window_height) / 2).toInt();
233 | mini_show = settings.value("ShowMiniWindow", false).toBool();
234 | history_show = settings.value("ShowHistory", false).toBool();
235 | stay_top = settings.value("AlwaysOnTop", true).toBool();
236 | // MainWindowHistoryWidth
237 |
238 |
239 | // 不正な値が指定されてしまわないように、色コードをチェック(要検討)
240 | std::regex re_color("^#([\\da-fA-F]{6}|[\\da-fA-F]{3})$");
241 | if (std::regex_match(main_window_word_font_color , re_color) != 1) main_window_word_font_color = "#000088";
242 | if (std::regex_match(main_window_text_font_color , re_color) != 1) main_window_text_font_color = "#101010";
243 | if (std::regex_match(main_window_mark_font_color , re_color) != 1) main_window_mark_font_color = "#008000";
244 | if (std::regex_match(main_window_background_color, re_color) != 1) main_window_background_color = "#ffffff";
245 | if (std::regex_match(mini_window_word_font_color , re_color) != 1) mini_window_word_font_color = "#000088";
246 | if (std::regex_match(mini_window_text_font_color , re_color) != 1) mini_window_text_font_color = "#101010";
247 | if (std::regex_match(mini_window_background_color, re_color) != 1) mini_window_background_color = "#ffffff";
248 | if (std::regex_match(pause_window_border_color , re_color) != 1) pause_window_border_color = "#ff4500";
249 |
250 | // 不正な値が指定されてしまわないように、各パラメータを範囲内に収める(要検討)
251 | main_window_word_font_size = std::clamp(main_window_word_font_size, 3, 32);
252 | main_window_text_font_size = std::clamp(main_window_text_font_size, 3, 32);
253 | main_window_mark_font_size = std::clamp(main_window_mark_font_size, 3, 32);
254 | mini_window_word_font_size = std::clamp(mini_window_word_font_size, 3, 32);
255 | mini_window_text_font_size = std::clamp(mini_window_text_font_size, 3, 32);
256 | main_window_opacity = std::clamp(main_window_opacity, 0.0, 1.0);
257 | mini_window_opacity = std::clamp(mini_window_opacity, 0.0, 1.0);
258 | pause_window_opacity = std::clamp(pause_window_opacity, 0.0, 1.0);
259 | ocr_scale = std::clamp(ocr_scale, 100, 200);
260 | ocr_area_left = std::clamp(ocr_area_left, 1, 500);
261 | ocr_area_right = std::clamp(ocr_area_right, 1, 500);
262 | ocr_area_top = std::clamp(ocr_area_top, 1, 500);
263 | ocr_area_bottom = std::clamp(ocr_area_bottom, 1, 500);
264 | if (main_window_width < 0) mini_window_width = 0;
265 | if (main_window_height < 0) mini_window_height = 0;
266 | if (mini_window_width < 0) mini_window_width = 0;
267 | if (mini_window_height < 0) mini_window_height = 0;
268 | if (pause_window_border_width < 0) pause_window_border_width = 0;
269 |
270 | // 各パラメータを文字認識スレッドにセット
271 | thread.setMainFontColor(main_window_word_font_color, main_window_text_font_color, main_window_mark_font_color, main_window_background_color);
272 | thread.setMiniFontColor(mini_window_word_font_color, mini_window_text_font_color, mini_window_background_color);
273 | thread.setMainFontSize(main_window_word_font_size, main_window_text_font_size, main_window_mark_font_size);
274 | thread.setMiniFontSize(mini_window_word_font_size, mini_window_text_font_size);
275 | thread.setOcrScale(ocr_scale);
276 | thread.setOcrRoi(ocr_area_left, ocr_area_right, ocr_area_top, ocr_area_bottom);
277 |
278 | }
279 |
280 | void MouseOverDictionary::WriteSettings()
281 | {
282 | QSettings settings("settings.ini", QSettings::IniFormat);
283 | settings.setIniCodec(QTextCodec::codecForName("UTF-8"));
284 |
285 | settings.beginGroup("Color");
286 | settings.setValue("MainWindowWord", QString::fromStdString(main_window_word_font_color));
287 | settings.setValue("MainWindowText", QString::fromStdString(main_window_text_font_color));
288 | settings.setValue("MainWindowMark", QString::fromStdString(main_window_mark_font_color));
289 | settings.setValue("MainWindowBackground", QString::fromStdString(main_window_background_color));
290 | settings.setValue("MiniWindowWord", QString::fromStdString(mini_window_word_font_color));
291 | settings.setValue("MiniWindowText", QString::fromStdString(mini_window_text_font_color));
292 | settings.setValue("MiniWindowBackground", QString::fromStdString(mini_window_background_color));
293 | settings.endGroup();
294 |
295 | settings.beginGroup("FontSize");
296 | settings.setValue("MainWindowWord", main_window_word_font_size);
297 | settings.setValue("MainWindowText", main_window_text_font_size);
298 | settings.setValue("MainWindowMark", main_window_mark_font_size);
299 | settings.setValue("MiniWindowWord", mini_window_word_font_size);
300 | settings.setValue("MiniWindowText", mini_window_text_font_size);
301 | settings.endGroup();
302 |
303 | settings.beginGroup("Shortcut");
304 | settings.setValue("Enable", enable_shortcut);
305 | settings.setValue("ShowHideBothWindow", hotkey_show_hide_both);
306 | settings.setValue("ShowHideMiniWindow", hotkey_show_hide_mini);
307 | settings.setValue("ScreenPause", hotkey_screen_pause);
308 | settings.endGroup();
309 |
310 | settings.beginGroup("Window");
311 | settings.setValue("MainWindowOpacity", main_window_opacity);
312 | settings.setValue("MiniWindowOpacity", mini_window_opacity);
313 | settings.setValue("MiniWindowWidth", mini_window_width);
314 | settings.setValue("MiniWindowHeight", mini_window_height);
315 | settings.setValue("MiniWindowPositionX", mini_window_pos_x);
316 | settings.setValue("MiniWindowPositionY", mini_window_pos_y);
317 | settings.endGroup();
318 |
319 | settings.beginGroup("ScreenPause");
320 | settings.setValue("BorderThickness", pause_window_border_width);
321 | settings.setValue("BorderColor", QString::fromStdString(pause_window_border_color));
322 | settings.setValue("Opacity", pause_window_opacity);
323 | settings.endGroup();
324 |
325 | settings.beginGroup("OCR");
326 | settings.setValue("CaptureAreaLeft", ocr_area_left);
327 | settings.setValue("CaptureAreaRight", ocr_area_right);
328 | settings.setValue("CaptureAreaTop", ocr_area_top);
329 | settings.setValue("CaptureAreaBottom", ocr_area_bottom);
330 | settings.setValue("CaptureScale", ocr_scale);
331 | settings.endGroup();
332 |
333 | // 前回終了時のウィンドウの情報
334 | settings.beginGroup("PreviousState");
335 | settings.setValue("MainWindowWidth", main_window_width);
336 | settings.setValue("MainWindowHeight", main_window_height);
337 | settings.setValue("MainWindowPositionX", main_window_pos_x);
338 | settings.setValue("MainWindowPositionY", main_window_pos_y);
339 | settings.setValue("ShowMiniWindow", mini_show);
340 | settings.setValue("ShowHistory", history_show);
341 | settings.setValue("AlwaysOnTop", stay_top);
342 | // MainWindowHistoryWidth
343 | settings.endGroup();
344 |
345 | }
346 |
347 | void MouseOverDictionary::on_toolButton_clicked()
348 | {
349 | if (mini_show == false) {
350 | mini_window->show();
351 | ui.toolButton->setIcon(QIcon(":/MouseOverDictionary/Resources/chat-4-line.png"));
352 | }
353 | else {
354 | mini_window->hide();
355 | ui.toolButton->setIcon(QIcon(":/MouseOverDictionary/Resources/chat-off-line.png"));
356 | }
357 |
358 | mini_show = !mini_show;
359 | }
360 |
361 | void MouseOverDictionary::on_toolButton_2_clicked()
362 | {
363 | // https://stackoverflow.com/questions/2855968/how-do-i-toggle-always-on-top-for-a-qmainwindow-in-qt-without-causing-a-flicke
364 | #ifdef Q_OS_WIN
365 | if (stay_top == false)
366 | {
367 | SetWindowPos((HWND)this->winId(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
368 | }
369 | else
370 | {
371 | SetWindowPos((HWND)this->winId(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
372 | }
373 | #else
374 | Qt::WindowFlags flags = this->windowFlags();
375 | if (stay_top == false)
376 | {
377 | this->setWindowFlags(flags | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint);
378 | this->show();
379 | }
380 | else
381 | {
382 | this->setWindowFlags(flags ^ (Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint));
383 | this->show();
384 | }
385 | #endif
386 |
387 | if (stay_top == false) {
388 | ui.toolButton_2->setIcon(QIcon(":/MouseOverDictionary/Resources/push-pin-2-line.png"));
389 | }
390 | else {
391 | ui.toolButton_2->setIcon(QIcon(":/MouseOverDictionary/Resources/push-pin-line.png"));
392 | }
393 |
394 | stay_top = !stay_top;
395 | }
396 |
397 | void MouseOverDictionary::on_toolButton_3_clicked()
398 | {
399 | if (history_show == false) {
400 | ui.listView->show();
401 | ui.toolButton_3->setIcon(QIcon(":/MouseOverDictionary/Resources/side-bar-line.png"));
402 | }
403 | else {
404 | ui.listView->hide();
405 | ui.toolButton_3->setIcon(QIcon(":/MouseOverDictionary/Resources/side-bar-line-hide.png"));
406 |
407 | }
408 |
409 | history_show = !history_show;
410 | }
411 |
412 | void MouseOverDictionary::setReady(bool ready)
413 | {
414 | thread_ready = ready;
415 | thread.setWindowSize(this->frameGeometry().width(), this->frameGeometry().height());
416 | thread.setWindowPos(this->pos().x(), this->pos().y());
417 | }
418 |
419 | void MouseOverDictionary::resizeEvent(QResizeEvent *event)
420 | {
421 | // ウィンドウ内のサイズ
422 | main_window_width = this->geometry().width();
423 | main_window_height = this->geometry().height();
424 |
425 | // メインウィンドウ内では文字認識を走らせないようにするために、現在サイズを指定
426 | if (thread_ready) {
427 | // ウィンドウの枠なども含めたサイズを指定 https://doc.qt.io/archives/4.3/geometry.html
428 | thread.setWindowSize(this->frameGeometry().width(), this->frameGeometry().height());
429 | }
430 | QWidget::resizeEvent(event);
431 | }
432 |
433 | void MouseOverDictionary::moveEvent(QMoveEvent *event)
434 | {
435 | main_window_pos_x = this->pos().x();
436 | main_window_pos_y = this->pos().y();
437 |
438 | // メインウィンドウ内では文字認識を走らせないようにするために、現在位置を指定
439 | if (thread_ready) {
440 | thread.setWindowPos(main_window_pos_x, main_window_pos_y);
441 | }
442 | QWidget::moveEvent(event);
443 | }
444 |
445 | void MouseOverDictionary::closeEvent(QCloseEvent *event)
446 | {
447 | // delete *** よりも ***->deleteLater() の方が安全らしい https://stackoverflow.com/questions/39407564/difference-between-hide-close-and-show-in-qt
448 | mini_window->deleteLater();
449 | pause_window->deleteLater();
450 | hm_show_hide_both->deleteLater();
451 | hm_show_hide_mini->deleteLater();
452 | hm_show_hide_pause->deleteLater();
453 | QWidget::closeEvent(event);
454 | }
455 |
456 | void MouseOverDictionary::changeEvent(QEvent * event)
457 | {
458 | QMainWindow::changeEvent(event);
459 | if (event->type() == QEvent::WindowStateChange)
460 | {
461 |
462 | // メインウィンドウ最小化時に文字読み取りをオフにする
463 | if (windowState() == Qt::WindowMinimized)
464 | {
465 | thread.disableOcr();
466 | if (mini_show == true) {
467 | mini_window->hide();
468 | }
469 |
470 | }
471 | else if (windowState() == Qt::WindowNoState)
472 | {
473 | thread.enableOcr();
474 | if (mini_show == true) {
475 | mini_window->show();
476 | }
477 | }
478 | }
479 | }
480 |
481 | //void MouseOverDictionary::hideEvent(QHideEvent * event)
482 | //{
483 | // thread.disableOcr();
484 | // if (mini_show == true) {
485 | // mini_window->hide();
486 | // }
487 | //
488 | // QWidget::hideEvent(event);
489 | //}
490 | //
491 | //void MouseOverDictionary::showEvent(QShowEvent * event)
492 | //{
493 | // thread.enableOcr();
494 | // if (mini_show == true) {
495 | // mini_window->show();
496 | // }
497 | //
498 | // QWidget::showEvent(event);
499 | //}
500 |
501 | void MouseOverDictionary::updateHistory(QString word)
502 | {
503 | // 履歴選択 → ワード欄更新 → 辞書検索 → 履歴更新により選択変化 → ... の無限ループを防ぐためのフラグ(要検討)
504 | history_search_enable = false;
505 |
506 | if (history_update_enable) {
507 |
508 | //新しい検索語が履歴に含まれている場合は、その項目を削除
509 | // →逆に使いにくいかもしれない。要検討
510 | QModelIndexList indexMatchList = historyModel->match(historyModel->index(0, 0), Qt::DisplayRole, QVariant::fromValue(word), 1);
511 | if (indexMatchList.size() > 0) {
512 | historyModel->removeRows(indexMatchList.first().row(), 1); ;
513 | }
514 |
515 | // リストの一番上に追加
516 | int row = 0;
517 | historyModel->insertRows(row, 1);
518 | QModelIndex index = historyModel->index(row);
519 | historyModel->setData(index, word);
520 |
521 |
522 | // 一番上の項目を選択状態に
523 | QModelIndex indexOfTheCellIWant = historyModel->index(0, 0);
524 | ui.listView->setCurrentIndex(indexOfTheCellIWant);
525 |
526 |
527 | // 最大数を超える履歴を下から削除
528 | int history_max = 100;
529 | if (historyModel->rowCount() > history_max) {
530 | int row = historyModel->rowCount() - 1;
531 | historyModel->removeRows(row, 1);
532 | }
533 |
534 | }
535 | else {
536 | history_update_enable = true;
537 | }
538 |
539 | history_search_enable = true;
540 | }
541 |
542 |
543 | void MouseOverDictionary::searchByHistory(const QItemSelection &selected)
544 | {
545 |
546 | //履歴選択 → ワード欄更新 → 辞書検索 → 履歴更新により選択変化 → ... の無限ループを防ぐためのフラグ(要検討)
547 | if (history_search_enable) {
548 | if (selected.indexes().isEmpty() == false) {
549 |
550 | // 履歴選択による検索の場合は、履歴をアップデートしないように
551 | history_update_enable = false;
552 |
553 | QString word = historyModel->data(selected.indexes().first()).toString();
554 | ui.lineEdit->setText(word);
555 | }
556 | }
557 |
558 | }
559 |
560 | void MouseOverDictionary::setFromClipboard()
561 | {
562 | if (thread_ready) {
563 | QString clippboard_text = clipboard->text();
564 | if (clippboard_text.isEmpty() == false) {
565 | ui.lineEdit->setText(clippboard_text);
566 | }
567 | }
568 | }
569 |
570 | void MouseOverDictionary::showHide()
571 | {
572 | if (windowState() == Qt::WindowMinimized)
573 | {
574 | this->setWindowState(Qt::WindowNoState);
575 | }
576 | else if (windowState() == Qt::WindowNoState)
577 | {
578 | this->setWindowState(Qt::WindowMinimized);
579 | }
580 |
581 | // メインウィンドウを最小化する動きが少しうっとおしいので、show()、hide()にする
582 | // ただしこの方法だと、ショートカット以外に再表示させる方法がないので、誤操作が心配かも(最小化ならタスクバーから戻せる)
583 | // → タスクマネージャーからも見えなくなる挙動が怖いので、やっぱり最小化に戻す。タスクトレイ常駐を実装したら戻すかも
584 | //if (this->isVisible()) {
585 | // this->hide();
586 | //}
587 | //else {
588 | // this->show();
589 | //}
590 | }
591 |
592 | void MouseOverDictionary::showHideMini()
593 | {
594 | // そのうち「アクション」などで整理
595 | if (mini_show == false) {
596 | mini_window->show();
597 | ui.toolButton->setIcon(QIcon(":/MouseOverDictionary/Resources/chat-4-line.png"));
598 | }
599 | else {
600 | mini_window->hide();
601 | ui.toolButton->setIcon(QIcon(":/MouseOverDictionary/Resources/chat-off-line.png"));
602 | }
603 |
604 | mini_show = !mini_show;
605 | }
606 |
607 | void MouseOverDictionary::showPauseWindow()
608 | {
609 | if (pause_window->isVisible()) {
610 | pause_window->hide();
611 | }
612 | else {
613 |
614 | // 画面停止ウィンドウ終了後に元のウィンドウをアクティブにするために、現在アクティブなウィンドウを記録
615 | // GetActiveWindow()だと別スレッドは取れないので、GetForegroundWindow()を使う
616 | HWND hWnd;
617 | hWnd = GetForegroundWindow();
618 | pause_window->logActiveWindow(hWnd);
619 |
620 | // スクショに映り込まないように一時的に非表示に
621 | if (windowState() != Qt::WindowMinimized) {
622 | if (mini_show) {
623 | mini_window->hide();
624 | }
625 | this->hide();
626 | }
627 |
628 | // カーソルがあるスクリーンを取得
629 | QScreen * screen = QGuiApplication::screenAt(QCursor::pos());
630 | if (screen != 0) {
631 |
632 | // 画面停止ウィンドウのScrollAreaの枠の太さだけ内側をスクショ
633 | QPixmap screenPixmap = QPixmap();
634 | screenPixmap = screen->grabWindow(0, pause_window_border_width, pause_window_border_width);
635 | pause_window->setScreenshot(screenPixmap);
636 |
637 | QRect rect = screen->geometry();
638 |
639 | pause_window->show();
640 |
641 | // マウスがある方のスクリーンに移動させる
642 | // なぜかshow()の後じゃないと移動できない https://stackoverflow.com/questions/3203095/display-window-full-screen-on-secondary-monitor-using-qt
643 | pause_window->move(QPoint(rect.x(), rect.y()));
644 | }
645 |
646 | // 再表示
647 | if (windowState() != Qt::WindowMinimized) {
648 | this->show();
649 | if (mini_show) {
650 | mini_window->show();
651 | }
652 | }
653 | }
654 |
655 | }
--------------------------------------------------------------------------------
/MouseOverDictionary/mouse_over_dictionary.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include "ui_mouse_over_dictionary.h"
13 | #include "mini_window.h"
14 | #include "pause_window.h"
15 | #include "thread.h"
16 | #include "UGlobalHotkey\uglobalhotkeys.h"
17 |
18 | class MouseOverDictionary : public QMainWindow
19 | {
20 | Q_OBJECT
21 |
22 | public:
23 | MouseOverDictionary(QWidget *parent = Q_NULLPTR);
24 | ~MouseOverDictionary();
25 |
26 | private:
27 | void ReadSettings();
28 | void WriteSettings();
29 |
30 | Ui::MouseOverDictionaryClass ui;
31 | MiniWindow *mini_window;
32 | PauseWindow *pause_window;
33 | Thread thread;
34 | QClipboard *clipboard;
35 |
36 | UGlobalHotkeys *hm_show_hide_both;
37 | UGlobalHotkeys *hm_show_hide_mini;
38 | UGlobalHotkeys *hm_show_hide_pause;
39 |
40 | bool thread_ready = false;
41 |
42 | QStringListModel *historyModel;
43 | bool history_search_enable = true;
44 | bool history_update_enable = true;
45 |
46 | bool history_show = false;
47 | bool mini_show = false;
48 | bool stay_top = true;
49 |
50 | bool enable_shortcut;
51 | QString hotkey_show_hide_both;
52 | QString hotkey_show_hide_mini;
53 | QString hotkey_screen_pause;
54 |
55 | int ocr_scale;
56 | int ocr_area_left;
57 | int ocr_area_right;
58 | int ocr_area_top;
59 | int ocr_area_bottom;
60 |
61 | int main_window_word_font_size;
62 | int main_window_text_font_size;
63 | int main_window_mark_font_size;
64 | std::string main_window_word_font_color;
65 | std::string main_window_text_font_color;
66 | std::string main_window_mark_font_color;
67 | std::string main_window_background_color;
68 |
69 | int mini_window_word_font_size;
70 | int mini_window_text_font_size;
71 | std::string mini_window_word_font_color;
72 | std::string mini_window_text_font_color;
73 | std::string mini_window_background_color;
74 |
75 | int main_window_width;
76 | int main_window_height;
77 | int main_window_pos_x;
78 | int main_window_pos_y;
79 | int main_window_history_width;
80 |
81 | int mini_window_width;
82 | int mini_window_height;
83 | int mini_window_pos_x;
84 | int mini_window_pos_y;
85 |
86 | double main_window_opacity;
87 | double mini_window_opacity;
88 |
89 | double pause_window_opacity;
90 | int pause_window_border_width;
91 | std::string pause_window_border_color;
92 |
93 | int screen_width;
94 | int screen_height;
95 |
96 | public slots:
97 | // ToolButtonのchekableのトグルボタンは見た目が微妙だったので、普通のボタンクリックで切り替え
98 | void on_toolButton_clicked();
99 | void on_toolButton_2_clicked();
100 | void on_toolButton_3_clicked();
101 |
102 | void setReady(bool ready);
103 | void updateHistory(QString word);
104 | void searchByHistory(const QItemSelection &selected);
105 | void setFromClipboard();
106 | void showHide();
107 | void showHideMini();
108 | void showPauseWindow();
109 |
110 | protected:
111 | void resizeEvent(QResizeEvent *event);
112 | void moveEvent(QMoveEvent *event);
113 | void closeEvent(QCloseEvent *event);
114 | void changeEvent(QEvent *event);
115 | //void hideEvent(QHideEvent *event);
116 | //void showEvent(QShowEvent *event);
117 | };
118 |
--------------------------------------------------------------------------------
/MouseOverDictionary/mouse_over_dictionary.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kengo700/mouse_over_dictionary/e1ba1b5fec67910e3273ccb41b55140814fb1c04/MouseOverDictionary/mouse_over_dictionary.ico
--------------------------------------------------------------------------------
/MouseOverDictionary/mouse_over_dictionary.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | Resources/chat-4-line.png
4 | Resources/chat-off-line.png
5 | Resources/push-pin-line.png
6 | Resources/push-pin-2-line.png
7 | Resources/side-bar-line.png
8 | Resources/side-bar-line-hide.png
9 |
10 |
11 |
--------------------------------------------------------------------------------
/MouseOverDictionary/mouse_over_dictionary.rc:
--------------------------------------------------------------------------------
1 | IDI_ICON1 ICON DISCARDABLE "mouse_over_dictionary.ico"
--------------------------------------------------------------------------------
/MouseOverDictionary/mouse_over_dictionary.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MouseOverDictionaryClass
4 |
5 |
6 | true
7 |
8 |
9 |
10 | 0
11 | 0
12 | 350
13 | 300
14 |
15 |
16 |
17 | Mouse Over Dictionary
18 |
19 |
20 | 0.800000000000000
21 |
22 |
23 |
24 |
25 | 0
26 |
27 |
28 | 0
29 |
30 |
31 | 0
32 |
33 |
34 | 0
35 |
36 |
37 | 0
38 |
39 | -
40 |
41 |
42 | 3
43 |
44 |
45 | 3
46 |
47 |
48 | 3
49 |
50 |
51 | 3
52 |
53 |
54 | 3
55 |
56 |
-
57 |
58 |
59 | 0
60 |
61 |
-
62 |
63 |
64 | 検索履歴を表示
65 |
66 |
67 | ...
68 |
69 |
70 |
71 | :/MouseOverDictionary/Resources/side-bar-line-hide.png:/MouseOverDictionary/Resources/side-bar-line-hide.png
72 |
73 |
74 | false
75 |
76 |
77 |
78 |
79 |
80 | -
81 |
82 |
83 | false
84 |
85 |
86 |
87 | -
88 |
89 |
90 | 2
91 |
92 |
-
93 |
94 |
95 | マウス追従ウィンドウを表示
96 |
97 |
98 | ...
99 |
100 |
101 |
102 | :/MouseOverDictionary/Resources/chat-off-line.png:/MouseOverDictionary/Resources/chat-off-line.png
103 |
104 |
105 | false
106 |
107 |
108 | false
109 |
110 |
111 |
112 | -
113 |
114 |
115 | メインウィンドウを常に最前面に表示
116 |
117 |
118 | ...
119 |
120 |
121 |
122 | :/MouseOverDictionary/Resources/push-pin-2-line.png:/MouseOverDictionary/Resources/push-pin-2-line.png
123 |
124 |
125 | false
126 |
127 |
128 | false
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 | -
137 |
138 |
139 | Qt::Horizontal
140 |
141 |
142 | 4
143 |
144 |
145 |
146 | QFrame::StyledPanel
147 |
148 |
149 | QFrame::Plain
150 |
151 |
152 | Qt::ScrollBarAlwaysOff
153 |
154 |
155 |
156 |
157 | true
158 |
159 |
160 |
161 | 0
162 | 0
163 |
164 |
165 |
166 | false
167 |
168 |
169 | QFrame::Plain
170 |
171 |
172 | Qt::ScrollBarAlwaysOff
173 |
174 |
175 | true
176 |
177 |
178 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
179 | <html><head><meta name="qrichtext" content="1" /><style type="text/css">
180 | p, li { white-space: pre-wrap; }
181 | </style></head><body style=" font-family:'MS UI Gothic'; font-size:9pt; font-weight:400; font-style:normal;">
182 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html>
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
--------------------------------------------------------------------------------
/MouseOverDictionary/pause_window.cpp:
--------------------------------------------------------------------------------
1 | #include "pause_window.h"
2 |
3 | PauseWindow::PauseWindow(QWidget *parent)
4 | : QWidget(parent)
5 | {
6 | ui.setupUi(this);
7 |
8 | // スクロールバー非表示
9 | ui.scrollArea->horizontalScrollBar()->setStyleSheet("QScrollBar {height:0px;}");
10 | ui.scrollArea->verticalScrollBar()->setStyleSheet("QScrollBar {width:0px;}");
11 |
12 | // スクロール不可
13 | ui.scrollArea->verticalScrollBar()->setEnabled(false);
14 | ui.scrollArea->horizontalScrollBar()->setEnabled(false);
15 |
16 | // ドラッグでスクロールできるように
17 | //QScroller::grabGesture(ui.scrollArea, QScroller::LeftMouseButtonGesture);
18 |
19 | // 枠無しウィンドウ
20 | this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
21 | this->setWindowState(windowState() ^ Qt::WindowMaximized);
22 |
23 | }
24 |
25 | PauseWindow::~PauseWindow()
26 | {
27 | }
28 |
29 | void PauseWindow::setScreenshot(QPixmap screenPixmap)
30 | {
31 | ui.label->setPixmap(screenPixmap);
32 | }
33 |
34 | void PauseWindow::logActiveWindow(HWND hWnd)
35 | {
36 | previous_active_window = hWnd;
37 | }
38 |
39 | void PauseWindow::mousePressEvent(QMouseEvent * event)
40 | {
41 |
42 | SetForegroundWindow(previous_active_window);
43 |
44 | this->hide();
45 | }
--------------------------------------------------------------------------------
/MouseOverDictionary/pause_window.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include "ui_pause_window.h"
7 |
8 | #include "windows.h"
9 |
10 | class PauseWindow : public QWidget
11 | {
12 | Q_OBJECT
13 |
14 | public:
15 | PauseWindow(QWidget *parent = Q_NULLPTR);
16 | ~PauseWindow();
17 | void setScreenshot(QPixmap screenPixmap);
18 | void logActiveWindow(HWND hWnd);
19 |
20 | private:
21 | Ui::PauseWindow ui;
22 |
23 | HWND previous_active_window;
24 | protected:
25 | void mousePressEvent(QMouseEvent *event);
26 | };
27 |
--------------------------------------------------------------------------------
/MouseOverDictionary/pause_window.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | PauseWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 400
10 | 300
11 |
12 |
13 |
14 | PauseWindow
15 |
16 |
17 |
18 | 0
19 |
20 |
21 | 0
22 |
23 |
24 | 0
25 |
26 |
27 | 0
28 |
29 |
30 | 0
31 |
32 | -
33 |
34 |
35 | true
36 |
37 |
38 |
39 |
40 | 0
41 | 0
42 | 398
43 | 298
44 |
45 |
46 |
47 |
48 | 0
49 |
50 |
51 | 0
52 |
53 |
54 | 0
55 |
56 |
57 | 0
58 |
59 |
60 | 0
61 |
62 |
-
63 |
64 |
65 | Screenshot Image
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/MouseOverDictionary/screen_ocr.cpp:
--------------------------------------------------------------------------------
1 | #include "screen_ocr.h"
2 |
3 | ScreenOCR::ScreenOCR()
4 | {
5 | tesseract_ptr = std::unique_ptr(new tesseract::TessBaseAPI());
6 |
7 | }
8 |
9 | bool ScreenOCR::Init()
10 | {
11 | // Tesseract初期化
12 | if (tesseract_ptr->Init(NULL, "eng")) {
13 | //fprintf(stderr, "Could not initialize tesseract.\n");
14 | return false;
15 | }
16 |
17 | // ホワイトリスト設定
18 | // 「-」を指定しているのに検出されない? 一旦設定せず
19 | //tesseract_ptr->SetVariable("tessedit_char_whitelist", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-+,.;:/<>()[]*'\"!? ");
20 |
21 | // 警告「Warning. Invalid resolution 0 dpi. Using 70 instead.」を消すため
22 | //tesseract_ptr->SetVariable("user_defined_dpi", "300");
23 | tesseract_ptr->SetVariable("user_defined_dpi", "96");
24 |
25 | // Page Segmentation ModeのSingle Text Lineを試してみる
26 | //tesseract_ptr->SetVariable("psm", "7");
27 |
28 | return true;
29 | }
30 |
31 |
32 | bool ScreenOCR::Recognize(int x, int y, int width, int height, int scale)
33 | {
34 |
35 | HWND SomeWindowHandle = GetDesktopWindow();
36 | HDC DC = GetDC(SomeWindowHandle);
37 |
38 | if (DC == NULL) {
39 | return false;
40 | }
41 |
42 | // マウス付近の画像をキャプチャ
43 | try {
44 | //Image Img = Image(DC, x, y, width, height);
45 | Image Img = Image(DC, x, y, width, height, scale);
46 | tesseract_ptr->SetImage(Img.GetPixels(), Img.GetWidth(), Img.GetHeight(), Img.GetBytesPerPixel(), Img.GetBytesPerScanLine()); //Fixed this line..
47 | }
48 | catch (...) {
49 | return false;
50 | }
51 |
52 | ReleaseDC(SomeWindowHandle, DC);
53 |
54 | // 文字認識
55 | tesseract_ptr->Recognize(0);
56 | tesseract::ResultIterator* ri = tesseract_ptr->GetIterator();
57 | tesseract::PageIteratorLevel level = tesseract::RIL_WORD;
58 |
59 | // 結果を保存
60 | ocr_results.clear();
61 | bool found = false;
62 | if (ri != 0) {
63 | do {
64 | ocr_result result;
65 | ri->BoundingBox(level, &result.x1, &result.y1, &result.x2, &result.y2);
66 |
67 | result.x1 = (int)(result.x1 * 100 / scale);
68 | result.y1 = (int)(result.y1 * 100 / scale);
69 | result.x2 = (int)(result.x2 * 100 / scale);
70 | result.y2 = (int)(result.y2 * 100 / scale);
71 |
72 | const char* word = ri->GetUTF8Text(level);
73 | if (word != NULL) {
74 | result.word = word;
75 | ocr_results.push_back(result);
76 | found = true;
77 | }
78 | delete[] word;
79 | } while (ri->Next(level));
80 | }
81 |
82 | return found;
83 | }
84 |
85 |
86 | void ScreenOCR::GetResults(std::vector& results)
87 | {
88 | results = this->ocr_results;
89 | }
90 |
--------------------------------------------------------------------------------
/MouseOverDictionary/screen_ocr.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "image.h"
7 |
8 | struct ocr_result
9 | {
10 | std::string word;
11 | float conf;
12 | int x1, x2, y1, y2;
13 | };
14 |
15 | class ScreenOCR
16 | {
17 | public:
18 | ScreenOCR();
19 |
20 | private:
21 | std::unique_ptr tesseract_ptr;
22 | std::vector ocr_results;
23 |
24 | public:
25 | bool Init();
26 | bool Recognize(int x, int y, int width, int height, int scale);
27 | void GetResults(std::vector& results);
28 | };
29 |
--------------------------------------------------------------------------------
/MouseOverDictionary/singularizer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | //#include