Repository navigation
fix(ocr): list the installed OCR languages and keep the chosen one - #195
Merged
Merged
Conversation
TabOCR._run calls _ensure_tesseract, which rebuilt the language combo from pytesseract.get_languages() before _run read the selection. The rebuild called clear() and then addItem, and the first addItem selects index 0, so every run used the first installed code (osd excluded), whatever the user had chosen. --list-langs lists the codes in alphabetical order: with eng and por installed, choosing Portuguese ran eng, and with deu, eng and por, it ran deu. The rebuild now restores the selection by Tesseract code. The code is kept in TabOCR._lang_pick, apart from the combo, and is set when the user picks a language or when the rule below picks one. A code that is not installed leaves nothing selected, and _run stops with a new tool.ocr.lang_unavailable warning, added to the 8 locales, instead of running in another language. The pick comes back when the code is listed again: after a run with an empty list, or after its pack is removed and reinstalled. The codes are stored as item data, which replaces the parallel _lang_codes list. The combo shows the installed languages. The first time the OCR page is shown, tesseract --list-langs runs in a QThreadPool job and the result reaches the page through a @slot on a queued connection. The pool is the scan's own, not the global one, which the editor caps at 2 threads: behind two 3 s jobs on the global pool the scan waited 3.02 s, on its own pool 0.04 s. While the scan runs, and if it fails, the fixed list stays. Until the user picks a language, the rule picks the UI language's Tesseract code if it is installed, else eng, else the first installed one. The mapping covers the 8 UI locales (zh to chi_sim, nl to nld). Before the scan's result, the fixed list now shows the rule's pick from that list instead of its first entry, Portuguese, in every UI. chi_sim and nld, the pick of the zh and nl UIs, were shown as raw codes; tool.ocr.lang.zh and tool.ocr.lang.nl label them in the 8 locales. --list-langs runs through list_installed_langs, with CREATE_NO_WINDOW on Windows and a 5 s timeout, and its output is parsed as pytesseract parses it. pytesseract's get_languages and get_tesseract_version start Tesseract without that flag: under pythonw, one OCR run opened two Windows Terminal windows, and with the helper none. _ensure_tesseract calls the helper in place of both. OSError, or an exit status other than 0 or 1, shows the not-found message with the install hint, for parity: pytesseract 0.3.13 raises TesseractNotFoundError for those exit statuses, and on 674bef3 get_tesseract_version reached the same message on any non-zero exit. A tesseract.exe copied without its DLLs exits 0xC0000135, one without libtesseract exits 127 on Linux, and Tesseract exits 0 even with an unusable tessdata. A timeout keeps the list and lets the OCR call go ahead. pytesseract's minimum version check goes too: on 674bef3, a version below 3.05, or one it could not parse, raised SystemExit, which ended the app. CREATE_NO_WINDOW is set in an if sys.platform == "win32" block. As a conditional expression, mypy --platform linux reported it missing from subprocess: 50 errors in app/ against 49 on 674bef3. As a block, 49 on linux, win32 and darwin, as on 674bef3. Closing the app while the scan runs waits for it. With a single-process stub that sleeps 20 s, the app exited 4.1 s after the window closed, the scan having started 1 s before. On Windows, a wrapper whose child keeps the pipe open holds it longer: with a tesseract.bat, 19.1 to 19.4 s, as subprocess.run reads the pipe to its end after the kill. On Linux, subprocess.run only waits for the killed process: with a sh wrapper whose child kept the pipe, the app exited 4.0 to 4.2 s after the window closed, and the child ran on. When the teardown has already deleted the signals object, the scan's emit raises RuntimeError; the job catches it, so pdfapps._excepthook does not log it as CRITICAL. A pool deleted with an earlier QApplication is replaced by a new one. The "eng" entry that stood in for an empty list is removed: it was offered after --list-langs had listed no language. An empty list now reaches the existing tool.ocr.no_langs warning. The selection rules and the helper live in app/tools/_ocr_langs.py, with no Qt imports. CONTRIBUTING.md names _locate_tesseract as the function that sets TESSDATA_PREFIX, and describes the new combo. tests/test_ocr_language_selection.py adds 110 tests. The page tests run with a fake subprocess.run that answers only --list-langs and fails any other call, a fake pytesseract and a subprocess.Popen that fails when asked to start Tesseract; the scan job runs on its thread pool and the tests wait for it to finish. The list_installed_langs tests replace subprocess.run with a recorder that answers any argv, or with one that raises. On 674bef3 with app/tools/_ocr_langs.py added, 82 of them fail or error: pytest reports 79 failed, 31 passed and 75 errors, all at teardown, three of them in tests that otherwise passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bug. The OCR tool ignored the language the user chose. With Portuguese selected, it ran
engwhenengandporwere installed, anddeuwhendeu,engandporwere installed.Cause.
TabOCR._runcalls_ensure_tesseract, which rebuilt the language combo frompytesseract.get_languages()before_runread the selection. The rebuild calledclear()and thenaddItem, and the firstaddItemselects index 0, so every run used the first installed code (osdexcluded), in the alphabetical order of--list-langs.Fix.
TabOCR._lang_pick, apart from the combo, and is set when the user picks a language or when the default rule picks one. The codes are stored as item data, replacing the parallel_lang_codeslist._runstops with a newtool.ocr.lang_unavailablewarning (added to the 8 locales) instead of running in another language. The pick comes back when the code is listed again.tesseract --list-langsruns in aQThreadPooljob and the result reaches the page through a@Sloton a queued connection. While the scan runs, and if it fails, the fixed list stays.eng, else the first installed one. The mapping covers the 8 UI locales (zhtochi_sim,nltonld).chi_simandnldwere shown as raw codes;tool.ocr.lang.zhandtool.ocr.lang.nlnow label them in the 8 locales.--list-langsruns through a new helper,list_installed_langs, withCREATE_NO_WINDOWon Windows and a 5 s timeout, and its output is parsed as pytesseract parses it. pytesseract'sget_languagesandget_tesseract_versionstart Tesseract without that flag: underpythonw, one OCR run opened two Windows Terminal windows, and with the helper none._ensure_tesseractcalls the helper in place of both.app/tools/_ocr_langs.py, with no Qt imports.CONTRIBUTING.mdnames_locate_tesseractas the function that setsTESSDATA_PREFIX, and describes the new combo.Behaviour changes
--list-langstimeout keeps the list and lets the OCR call go ahead.OSError, or an exit status other than 0 or 1, shows the not-found message with the install hint. This is for parity: pytesseract 0.3.13 raisesTesseractNotFoundErrorfor those exit statuses, and on 674bef3get_tesseract_versionreached the same message on any non-zero exit.SystemExit, which ended the app.engplaceholder. Theengentry that stood in for an empty list is removed; an empty list now reaches the existingtool.ocr.no_langswarning.tesseract.bat); on Linux, with ashwrapper, the app exited 4.0 to 4.2 s after the window closed.Verification
tests/test_ocr_language_selection.pyadds 110 tests. The page tests run with a fakesubprocess.runthat answers only--list-langsand fails any other call, a fake pytesseract, and asubprocess.Popenthat fails when asked to start Tesseract.app/tools/_ocr_langs.pyadded, 82 of them fail or error: pytest reports 79 failed, 31 passed and 75 errors, all at teardown.CREATE_NO_WINDOWis set in anif sys.platform == "win32"block:mypyreports 49 errors inapp/on linux, win32 and darwin, as on 674bef3 (the conditional-expression form gave 50 on linux).Known follow-ups
envfixture could still lose its teardown if itsyield fromwere replaced by a plain call.🤖 Generated with Claude Code