From a5b807b44ede01de912303a1c66515f40b71a427 Mon Sep 17 00:00:00 2001 From: AuroraWright Date: Tue, 14 Oct 2025 05:15:43 +0200 Subject: [PATCH] More changes --- README.md | 2 +- owocr/config.py | 4 +- owocr/run.py | 229 +++++++++++++++++------------- owocr/screen_coordinate_picker.py | 10 +- owocr_config.ini | 6 +- 5 files changed, 146 insertions(+), 105 deletions(-) diff --git a/README.md b/README.md index 28f784a..b519b0e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Additionally: - You can switch between OCR providers pressing their corresponding keyboard key inside the terminal window (refer to the list of keys in the providers list below) - You can start the script paused with the `-p` option or with a specific provider with the `-e` option (refer to `owocr -h` for the list) - You can specify keyboard combos in the config file to pause/unpause and switch the OCR provider from anywhere (refer to the config file or `owocr -h`) -- You can auto pause the script after a successful text recognition with the `-a=seconds` option if you're not using screen capture. 0 (the default) disables it. +- You can auto pause the script after a successful text recognition with the `-a=seconds` option. 0 (the default) disables it. - You can enable notifications in the config file or with `-n` to show the text with a native OS notification if you're not using screen capture with automatic screenshots. **Important for macOS users:** if you use Python from brew, you need to enter this command in your terminal before the first notification: `codesign -f -s - $(brew --cellar python)/3.*/Frameworks/Python.framework` (works on Ventura/Sonoma). Older macOS versions might require Python to be installed from the [official website](https://www.python.org/downloads/). Nothing can be done about this unfortunately. - Optionally, you can speed up the online providers by installing fpng-py: `pip install owocr[faster-png]` (requires setting up a developer environment on most operating systems/Python versions) - A config file (which will be automatically created in `user directory/.config/owocr_config.ini`, on Windows `user directory` is the `C:\Users\yourusername` folder) can be used to configure the script, as an example to limit providers (to reduce clutter/memory usage) as well as specifying provider settings such as api keys etc. A sample config file is also provided [here](https://raw.githubusercontent.com/AuroraWright/owocr/master/owocr_config.ini) diff --git a/owocr/config.py b/owocr/config.py index 41fc690..9d82564 100644 --- a/owocr/config.py +++ b/owocr/config.py @@ -35,11 +35,11 @@ parser.add_argument('-d', '--delete_images', type=str2bool, nargs='?', const=Tru parser.add_argument('-n', '--notifications', type=str2bool, nargs='?', const=True, default=argparse.SUPPRESS, help='Show an operating system notification with the detected text. Will be ignored when reading with screen capture and periodic screenshots.') parser.add_argument('-a', '--auto_pause', type=float, default=argparse.SUPPRESS, - help='Automatically pause the program after the specified amount of seconds since the last successful text recognition. Will be ignored when reading with screen capture. 0 to disable.') + help='Automatically pause the program after the specified amount of seconds since the last successful text recognition. 0 to disable.') parser.add_argument('-cp', '--combo_pause', type=str, default=argparse.SUPPRESS, help='Combo to wait on for pausing the program. As an example: "++p". The list of keys can be found here: https://pynput.readthedocs.io/en/latest/keyboard.html#pynput.keyboard.Key') parser.add_argument('-cs', '--combo_engine_switch', type=str, default=argparse.SUPPRESS, - help='Combo to wait on for switching the OCR engine. As an example: "++a". To be used with combo_pause. The list of keys can be found here: https://pynput.readthedocs.io/en/latest/keyboard.html#pynput.keyboard.Key') + help='Combo to wait on for switching the OCR engine. As an example: "++a". The list of keys can be found here: https://pynput.readthedocs.io/en/latest/keyboard.html#pynput.keyboard.Key') parser.add_argument('-sa', '--screen_capture_area', type=str, default=argparse.SUPPRESS, help='Area to target when reading with screen capture. Can be either empty (automatic selector), a set of coordinates (x,y,width,height), "screen_N" (captures a whole screen, where N is the screen number starting from 1) or a window name (the first matching window title will be used).') parser.add_argument('-swa', '--screen_capture_window_area', type=str, default=argparse.SUPPRESS, diff --git a/owocr/run.py b/owocr/run.py index e039bb4..8453c11 100644 --- a/owocr/run.py +++ b/owocr/run.py @@ -27,7 +27,7 @@ from desktop_notifier import DesktopNotifierSync, Urgency from .ocr import * from .config import config -from .screen_coordinate_picker import get_screen_selection +from .screen_coordinate_picker import get_screen_selection, terminate_selector_if_running try: import win32gui @@ -100,7 +100,7 @@ class ClipboardThread(threading.Thread): def process_message(self, hwnd: int, msg: int, wparam: int, lparam: int): WM_CLIPBOARDUPDATE = 0x031D timestamp = time.time() - if msg == WM_CLIPBOARDUPDATE and timestamp - self.last_update > 1 and not paused: + if msg == WM_CLIPBOARDUPDATE and timestamp - self.last_update > 1 and not paused.is_set(): self.last_update = timestamp while True: try: @@ -144,8 +144,8 @@ class ClipboardThread(threading.Thread): process_clipboard = False img = None - while not terminated: - if paused: + while not terminated.is_set(): + if paused.is_set(): sleep_time = 0.5 process_clipboard = False else: @@ -173,7 +173,7 @@ class ClipboardThread(threading.Thread): process_clipboard = True - if not terminated: + if not terminated.is_set(): time.sleep(sleep_time) @@ -194,8 +194,8 @@ class DirectoryWatcher(threading.Thread): if path.suffix.lower() in self.allowed_extensions: old_paths.add(self.get_path_key(path)) - while not terminated: - if paused: + while not terminated.is_set(): + if paused.is_set(): sleep_time = 0.5 else: sleep_time = self.delay_secs @@ -205,10 +205,10 @@ class DirectoryWatcher(threading.Thread): if path_key not in old_paths: old_paths.add(path_key) - if not paused: + if not paused.is_set(): image_queue.put((path, False)) - if not terminated: + if not terminated.is_set(): time.sleep(sleep_time) @@ -233,7 +233,7 @@ class WebsocketServerThread(threading.Thread): self.clients.add(websocket) try: async for message in websocket: - if self.read and not paused: + if self.read and not paused.is_set(): image_queue.put((message, False)) try: await websocket.send('True') @@ -282,7 +282,7 @@ class RequestHandler(socketserver.BaseRequestHandler): except TimeoutError: pass - if not paused: + if not paused.is_set(): image_queue.put((img, False)) conn.sendall(b'True') else: @@ -789,7 +789,8 @@ class ScreenshotThread(threading.Thread): elif screen_capture_area.startswith('screen_'): parts = screen_capture_area.split('_') if len(parts) != 2 or not parts[1].isdigit(): - raise ValueError('Invalid screen_capture_area') + logger.error('Invalid screen_capture_area') + sys.exit(1) screen_capture_monitor = int(parts[1]) self.screencapture_mode = 1 elif len(screen_capture_area.split(',')) == 4: @@ -806,7 +807,8 @@ class ScreenshotThread(threading.Thread): if self.screencapture_mode == 1: mon = self.sct.monitors if len(mon) <= screen_capture_monitor: - raise ValueError('Invalid monitor number in screen_capture_area') + logger.error('Invalid monitor number in screen_capture_area') + sys.exit(1) coord_left = mon[screen_capture_monitor]['left'] coord_top = mon[screen_capture_monitor]['top'] coord_width = mon[screen_capture_monitor]['width'] @@ -850,7 +852,8 @@ class ScreenshotThread(threading.Thread): break if not window_index: - raise ValueError(area_invalid_error) + logger.error(area_invalid_error) + sys.exit(1) self.window_id = window_ids[window_index] window_title = window_titles[window_index] @@ -863,7 +866,8 @@ class ScreenshotThread(threading.Thread): self.window_handle, window_title = self.get_windows_window_handle(screen_capture_area) if not self.window_handle: - raise ValueError(area_invalid_error) + logger.error(area_invalid_error) + sys.exit(1) ctypes.windll.shcore.SetProcessDpiAwareness(1) @@ -871,7 +875,8 @@ class ScreenshotThread(threading.Thread): self.windows_window_tracker_instance.start() logger.info(f'Selected window: {window_title}') else: - raise ValueError('Window capture is only currently supported on Windows and macOS') + logger.error('Window capture is only currently supported on Windows and macOS') + sys.exit(1) screen_capture_window_area = config.get_general('screen_capture_window_area') if screen_capture_window_area != 'window': @@ -882,7 +887,8 @@ class ScreenshotThread(threading.Thread): elif screen_capture_window_area == '': self.launch_coordinate_picker(False, False) else: - raise ValueError('"screen_capture_window_area" must be empty, "window" for the whole window, or a valid set of coordinates') + logger.error('"screen_capture_window_area" must be empty, "window" for the whole window, or a valid set of coordinates') + sys.exit(1) def get_windows_window_handle(self, window_title): def callback(hwnd, window_title_part): @@ -906,7 +912,7 @@ class ScreenshotThread(threading.Thread): def windows_window_tracker(self): found = True - while not terminated: + while not terminated.is_set(): found = win32gui.IsWindow(self.window_handle) if not found: break @@ -914,7 +920,7 @@ class ScreenshotThread(threading.Thread): self.screencapture_window_active = self.window_handle == win32gui.GetForegroundWindow() else: self.screencapture_window_visible = not win32gui.IsIconic(self.window_handle) - time.sleep(0.2) + time.sleep(0.5) if not found: on_window_closed(False) @@ -965,7 +971,7 @@ class ScreenshotThread(threading.Thread): def macos_window_tracker(self): found = True - while found and not terminated: + while found and not terminated.is_set(): found = False is_active = False with objc.autorelease_pool(): @@ -985,7 +991,7 @@ class ScreenshotThread(threading.Thread): found = True if found: self.screencapture_window_active = is_active - time.sleep(0.2) + time.sleep(0.5) if not found: on_window_closed(False) @@ -1074,7 +1080,8 @@ class ScreenshotThread(threading.Thread): screen_selection = get_screen_selection(None, self.coordinate_selector_combo_enabled) if not screen_selection: if on_init: - raise ValueError('Picker window was closed or an error occurred') + logger.error('Picker window was closed or an error occurred') + sys.exit(1) else: logger.warning('Picker window was closed or an error occurred, leaving settings unchanged') return @@ -1111,8 +1118,8 @@ class ScreenshotThread(threading.Thread): def run(self): if self.screencapture_mode != 2: self.sct = mss.mss() - while not terminated: - if not screenshot_event.wait(timeout=0.1): + while not terminated.is_set(): + if not screenshot_event.wait(timeout=0.5): if coordinate_selector_event.is_set(): self.launch_coordinate_picker(False, False) coordinate_selector_event.clear() @@ -1133,34 +1140,50 @@ class ScreenshotThread(threading.Thread): class AutopauseTimer: - def __init__(self, timeout): - self.timeout = timeout - self.timer_thread = None - self.running = False - - def __del__(self): - self.stop() - - def start(self): - self.stop() - self.running = True + def __init__(self): + self.timeout = config.get_general('auto_pause') self.timer_thread = threading.Thread(target=self._countdown) + self.running = True + self.countdown_active = threading.Event() + self.allow_auto_pause = threading.Event() + self.seconds_remaining = 0 + self.lock = threading.Lock() self.timer_thread.start() + def start_timer(self): + with self.lock: + self.seconds_remaining = self.timeout + self.allow_auto_pause.set() + self.countdown_active.set() + + def stop_timer(self): + self.countdown_active.clear() + self.allow_auto_pause.set() + def stop(self): - if self.running and self.timer_thread and self.timer_thread.is_alive(): - self.running = False + self.running = False + self.allow_auto_pause.set() + self.countdown_active.set() + if self.timer_thread.is_alive(): self.timer_thread.join() def _countdown(self): - seconds = self.timeout - while seconds > 0 and self.running and not terminated: - time.sleep(1) - seconds -= 1 - if self.running: - self.running = False - if not (paused or terminated): - pause_handler(True) + while self.running: + self.countdown_active.wait() + if not self.running: + break + + while self.running and self.countdown_active.is_set() and self.seconds_remaining > 0: + time.sleep(1) + with self.lock: + self.seconds_remaining -= 1 + + self.allow_auto_pause.wait() + + if self.running and self.countdown_active.is_set() and self.seconds_remaining == 0: + self.countdown_active.clear() + if not (paused.is_set() or terminated.is_set()): + pause_handler(True) class SecondPassThread: @@ -1170,9 +1193,6 @@ class SecondPassThread: self.ocr_thread = None self.running = False - def __del__(self): - self.stop() - def start(self): if self.ocr_thread is None or not self.ocr_thread.is_alive(): self.running = True @@ -1189,9 +1209,9 @@ class SecondPassThread: self.output_queue.get() def _process_ocr(self): - while self.running and not terminated: + while self.running: try: - img, engine_instance, recovered_lines_count = self.input_queue.get(timeout=0.1) + img, engine_instance, recovered_lines_count = self.input_queue.get(timeout=0.5) start_time = time.time() res, result_data = engine_instance(img) @@ -1237,10 +1257,8 @@ class OutputResult: lines.append(self.filtering._get_line_text(l)) return lines - def __call__(self, img_or_path, filter_text, notify): - if auto_pause_handler and not filter_text: - auto_pause_handler.stop() - + def __call__(self, img_or_path, filter_text, auto_pause, notify): + engine_index_local = engine_index output_format = config.get_general('output_format') engine_color = config.get_general('engine_color') engine_instance = engine_instances[engine_index] @@ -1248,7 +1266,7 @@ class OutputResult: result_data = None if filter_text and self.screen_capture_periodic: - if engine_index_2 != -1 and engine_index_2 != engine_index and engine_instance.threading_support: + if engine_index_2 != -1 and engine_index_2 != engine_index_local and engine_instance.threading_support: two_pass_processing_active = True engine_instance_2 = engine_instances[engine_index_2] start_time = time.time() @@ -1278,6 +1296,9 @@ class OutputResult: else: self.second_pass_thread.stop() + if auto_pause_handler and auto_pause: + auto_pause_handler.allow_auto_pause.clear() + if not result_data: start_time = time.time() res, result_data = engine_instance(img_or_path) @@ -1287,6 +1308,8 @@ class OutputResult: recovered_lines_count = 0 if not res: + if auto_pause_handler and auto_pause: + auto_pause_handler.stop_timer() logger.opt(colors=True).warning(f'<{engine_color}>{engine_name} reported an error after {processing_time:0.03f}s: {result_data}') return @@ -1310,7 +1333,9 @@ class OutputResult: if result_data_text != None: if filter_text: text_to_process = self.filtering._find_changed_lines_text(result_data_text, result_data, two_pass_processing_active, recovered_lines_count) - if self.screen_capture_periodic and len(text_to_process) == 0: + if self.screen_capture_periodic and not text_to_process: + if auto_pause_handler and auto_pause: + auto_pause_handler.allow_auto_pause.set() return output_string = self._post_process(text_to_process, True) else: @@ -1341,8 +1366,11 @@ class OutputResult: with Path(write_to).open('a', encoding='utf-8') as f: f.write(output_string + '\n') - if auto_pause_handler and not paused and not filter_text: - auto_pause_handler.start() + if auto_pause_handler and auto_pause: + if not paused.is_set(): + auto_pause_handler.start_timer() + else: + auto_pause_handler.stop_timer() def get_notification_urgency(): @@ -1351,16 +1379,16 @@ def get_notification_urgency(): return Urgency.Normal -def pause_handler(is_combo=True): +def pause_handler(is_combo=True): global paused - message = 'Unpaused!' if paused else 'Paused!' + message = 'Unpaused!' if paused.is_set() else 'Paused!' if auto_pause_handler: - auto_pause_handler.stop() + auto_pause_handler.stop_timer() if is_combo: notifier.send(title='owocr', message=message, urgency=get_notification_urgency()) logger.info(message) - paused = not paused + paused.clear() if paused.is_set() else paused.set() def engine_change_handler(user_input='s', is_combo=True): @@ -1386,11 +1414,11 @@ def user_input_thread_run(): def _terminate_handler(): global terminated logger.info('Terminated!') - terminated = True + terminated.set() if sys.platform == 'win32': import msvcrt - while not terminated: + while not terminated.is_set(): if coordinate_selector_event.is_set(): while coordinate_selector_event.is_set(): time.sleep(0.1) @@ -1407,19 +1435,19 @@ def user_input_thread_run(): except UnicodeDecodeError: pass else: - time.sleep(0.1) + time.sleep(0.2) else: import tty, termios, select fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: tty.setcbreak(fd) - while not terminated: + while not terminated.is_set(): if coordinate_selector_event.is_set(): while coordinate_selector_event.is_set(): time.sleep(0.1) tty.setcbreak(fd) - rlist, _, _ = select.select([sys.stdin], [], [], 0.1) + rlist, _, _ = select.select([sys.stdin], [], [], 0.2) if rlist: user_input = sys.stdin.read(1) if user_input.lower() in 'tq': @@ -1435,14 +1463,14 @@ def user_input_thread_run(): def signal_handler(sig, frame): global terminated logger.info('Terminated!') - terminated = True + terminated.set() def on_window_closed(alive): global terminated if not (alive or terminated): logger.info('Window closed or error occurred, terminated!') - terminated = True + terminated.set() def on_screenshot_combo(): @@ -1518,8 +1546,10 @@ def run(): read_from_path = None read_from_readable = [] write_to = config.get_general('write_to') - terminated = False - paused = config.get_general('pause_at_startup') + terminated = threading.Event() + paused = threading.Event() + if config.get_general('pause_at_startup'): + paused.set() auto_pause = config.get_general('auto_pause') output_format = config.get_general('output_format') clipboard_thread = None @@ -1544,10 +1574,7 @@ def run(): if combo_pause != '': key_combos[combo_pause] = pause_handler if combo_engine_switch != '': - if combo_pause != '': - key_combos[combo_engine_switch] = engine_change_handler - else: - raise ValueError('combo_pause must also be specified') + key_combos[combo_engine_switch] = engine_change_handler if 'websocket' in (read_from, read_from_secondary) or write_to == 'websocket': websocket_server_thread = WebsocketServerThread('websocket' in (read_from, read_from_secondary)) @@ -1568,7 +1595,8 @@ def run(): periodic_screenshot_queue = queue.Queue() screen_capture_periodic = True if not (screen_capture_on_combo or screen_capture_periodic): - raise ValueError('screen_capture_delay_secs or screen_capture_combo need to be valid values') + logger.error('screen_capture_delay_secs or screen_capture_combo need to be valid values') + sys.exit(1) screenshot_event = threading.Event() screenshot_thread = ScreenshotThread() screenshot_thread.start() @@ -1577,7 +1605,8 @@ def run(): read_from_readable.append('websocket') if 'unixsocket' in (read_from, read_from_secondary): if sys.platform == 'win32': - raise ValueError('"unixsocket" is not currently supported on Windows') + logger.error('"unixsocket" is not currently supported on Windows') + sys.exit(1) socket_path = Path('/tmp/owocr.sock') if socket_path.exists(): socket_path.unlink() @@ -1591,11 +1620,13 @@ def run(): read_from_readable.append('clipboard') if any(i and i not in non_path_inputs for i in (read_from, read_from_secondary)): if all(i and i not in non_path_inputs for i in (read_from, read_from_secondary)): - raise ValueError("read_from and read_from_secondary can't both be directory paths") + logger.error("read_from and read_from_secondary can't both be directory paths") + sys.exit(1) delete_images = config.get_general('delete_images') read_from_path = Path(read_from) if read_from not in non_path_inputs else Path(read_from_secondary) if not read_from_path.is_dir(): - raise ValueError('read_from and read_from_secondary must be either "websocket", "unixsocket", "clipboard", "screencapture", or a path to a directory') + logger.error('read_from and read_from_secondary must be either "websocket", "unixsocket", "clipboard", "screencapture", or a path to a directory') + sys.exit(1) directory_watcher_thread = DirectoryWatcher(read_from_path) directory_watcher_thread.start() read_from_readable.append(f'directory {read_from_path}') @@ -1610,64 +1641,71 @@ def run(): write_to_readable = write_to else: if Path(write_to).suffix.lower() != '.txt': - raise ValueError('write_to must be either "websocket", "clipboard" or a path to a text file') + logger.error('write_to must be either "websocket", "clipboard" or a path to a text file') + sys.exit(1) write_to_readable = f'file {write_to}' process_queue = (any(i in ('clipboard', 'websocket', 'unixsocket') for i in (read_from, read_from_secondary)) or read_from_path or screen_capture_on_combo) signal.signal(signal.SIGINT, signal_handler) - if (not screen_capture_periodic) and auto_pause != 0: - auto_pause_handler = AutopauseTimer(auto_pause) + if auto_pause != 0: + auto_pause_handler = AutopauseTimer() user_input_thread = threading.Thread(target=user_input_thread_run, daemon=True) user_input_thread.start() - # if json is selected check if engine is compatible if output_format == 'json' and not engine_instances[engine_index].coordinate_support: supported_engines = (engine.name for engine in engine_instances if engine.coordinate_support) logger.error(f"The selected engine '{engine_instances[engine_index].name}' does not support coordinate output.") - logger.error(f"Please choose one of: {', '.join(supported_engines)}") + logger.error(f"Please choose one of: {', '.join(supported_engines)}.") sys.exit(1) - logger.opt(colors=True).info(f"Reading from {' and '.join(read_from_readable)}, writing to {write_to_readable} using <{engine_color}>{engine_instances[engine_index].readable_name}{' (paused)' if paused else ''}") + logger.opt(colors=True).info(f"Reading from {' and '.join(read_from_readable)}, writing to {write_to_readable} using <{engine_color}>{engine_instances[engine_index].readable_name}{' (paused)' if paused.is_set() else ''}") - while not terminated: + while not terminated.is_set(): start_time = time.time() img = None filter_text = False + auto_pause = True + notify = False if process_queue: try: - img, filter_text = image_queue.get(timeout=0.1) - if screen_capture_periodic: - filter_text = False + img, is_screen_capture = image_queue.get_nowait() + if not screen_capture_periodic and is_screen_capture: + filter_text = True + if is_screen_capture: + auto_pause = False notify = True except queue.Empty: pass if (not img) and screen_capture_periodic: - if (not paused) and screenshot_thread.screencapture_window_active and screenshot_thread.screencapture_window_visible and (time.time() - last_screenshot_time) > screen_capture_delay_secs: + if (not paused.is_set()) and screenshot_thread.screencapture_window_active and screenshot_thread.screencapture_window_visible and (time.time() - last_screenshot_time) > screen_capture_delay_secs: screenshot_event.set() try: - img = periodic_screenshot_queue.get(timeout=0.1) + img = periodic_screenshot_queue.get_nowait() filter_text = True - notify = False last_screenshot_time = time.time() except queue.Empty: pass if img == 0: on_window_closed(False) - terminated = True + terminated.set() break elif img: - output_result(img, filter_text, notify) + output_result(img, filter_text, auto_pause, notify) if isinstance(img, Path): if delete_images: Path.unlink(img) elapsed_time = time.time() - start_time - if (not terminated) and elapsed_time < 0.1: + if (not terminated.is_set()) and elapsed_time < 0.1: time.sleep(0.1 - elapsed_time) + user_input_thread.join() + auto_pause_handler.stop() + output_result.second_pass_thread.stop() + terminate_selector_if_running() if websocket_server_thread: websocket_server_thread.stop_server() websocket_server_thread.join() @@ -1684,4 +1722,3 @@ def run(): screenshot_thread.join() if key_combo_listener: key_combo_listener.stop() - user_input_thread.join() diff --git a/owocr/screen_coordinate_picker.py b/owocr/screen_coordinate_picker.py index 138537d..4d8d3cf 100644 --- a/owocr/screen_coordinate_picker.py +++ b/owocr/screen_coordinate_picker.py @@ -1,6 +1,7 @@ import multiprocessing import queue import mss +from loguru import logger from PIL import Image import sys try: @@ -170,7 +171,8 @@ def get_screen_selection(pil_image, permanent_process): global selector_process, result_queue, command_queue if not selector_available: - raise ValueError('tkinter or PIL with tkinter support are not installed, unable to open picker') + logger.error('tkinter or PIL with tkinter support are not installed, unable to open picker') + sys.exit(1) if selector_process is None or not selector_process.is_alive(): result_queue = multiprocessing.Queue() @@ -188,6 +190,10 @@ def get_screen_selection(pil_image, permanent_process): except: continue if not permanent_process: + terminate_selector_if_running() + return result + +def terminate_selector_if_running(): + if selector_process and selector_process.is_alive(): command_queue.put(False) selector_process.join() - return result diff --git a/owocr_config.ini b/owocr_config.ini index 1cb2d2c..d484e7b 100644 --- a/owocr_config.ini +++ b/owocr_config.ini @@ -23,8 +23,7 @@ ;pause_at_startup = False ;Automatically pause the program after the specified amount of seconds since -;the last successful text recognition. Will be ignored when reading with screen -;capture. 0 to disable. +;the last successful text recognition. 0 to disable. ;auto_pause = 0 ;Delete image files after processing when reading from a directory. @@ -52,8 +51,7 @@ ;combo_pause = ;Combo to wait on for switching the OCR engine. As an example: -;"++a". To be used with combo_pause. The list of keys can be found -;here: +;"++a". The list of keys can be found here: ;https://pynput.readthedocs.io/en/latest/keyboard.html#pynput.keyboard.Key ;combo_engine_switch =