Skip to content

Instantly share code, notes, and snippets.

@RozeFound
Last active February 27, 2023 08:44
Show Gist options
  • Select an option

  • Save RozeFound/996dc4a78ab0f4d397010b4869650466 to your computer and use it in GitHub Desktop.

Select an option

Save RozeFound/996dc4a78ab0f4d397010b4869650466 to your computer and use it in GitHub Desktop.
Script to copy to clipboard any text on screen
from jeepney import DBusAddress, new_method_call
from jeepney.bus_messages import message_bus, MatchRule
from jeepney.io.blocking import open_dbus_connection, Proxy
from urllib.parse import unquote
from pathlib import Path
from tempfile import mkstemp
import pyocr, io, subprocess, numpy, cv2, screeninfo, time
from PIL import Image
def take_screenshot() -> bytes:
portal = DBusAddress('/org/freedesktop/portal/desktop',
bus_name='org.freedesktop.portal.Desktop',
interface='org.freedesktop.portal.Screenshot')
connection = open_dbus_connection()
token = "python_screen_ocr"
sender_name = connection.unique_name[1:].replace('.', '_')
handle = f"/org/freedesktop/portal/desktop/request/{sender_name}/{token}"
response_rule = MatchRule(type='signal', interface='org.freedesktop.portal.Request', path=handle)
Proxy(message_bus, connection).AddMatch(response_rule)
with connection.filter(response_rule) as responses:
body = ('', {'handle_token': ('s', token), 'interactive': ('b', True)})
request = new_method_call(portal, 'Screenshot', 'sa{sv}', body)
connection.send_and_get_reply(request)
response_message = connection.recv_until_filtered(responses)
response, results = response_message.body
if response != 0: raise RuntimeError()
result_uri = results['uri'][1].split('file://', 1)[-1]
file = Path(unquote(result_uri))
image = file.read_bytes(); file.unlink()
return image
def gnome_screenshot() -> bytes:
portal = DBusAddress('/org/gnome/Shell/Screenshot',
bus_name='org.gnome.Shell.Screenshot',
interface='org.gnome.Shell.Screenshot')
connection = open_dbus_connection()
request = new_method_call(portal, 'SelectArea')
result = connection.send_and_get_reply(request)
if result.body[0] == "Operation was cancelled":
raise RuntimeError
else: x, y, width, height = result.body
_, filename = mkstemp(); file = Path(filename)
body = (x, y, width, height, True, filename)
request = new_method_call(portal, 'ScreenshotArea', 'iiiibs', body)
result = connection.send_and_get_reply(request)
image = file.read_bytes(); file.unlink()
return image
def get_monitor_info():
monitor = screeninfo.get_monitors()[0]
width = monitor.width
height = monitor.height
x = monitor.x
y = monitor.y
return x, y, width, height
def gnome_freeze_screenshot() -> bytes:
portal = DBusAddress('/org/gnome/Shell/Screenshot',
bus_name='org.gnome.Shell.Screenshot',
interface='org.gnome.Shell.Screenshot')
connection = open_dbus_connection()
_, filename = mkstemp()
body = (*get_monitor_info(), False, filename)
request = new_method_call(portal, 'ScreenshotArea', 'iiiibs', body)
result = connection.send_and_get_reply(request)
eog = subprocess.Popen(["eog", filename, "--fullscreen"])
time.sleep(0.5)
request = new_method_call(portal, 'SelectArea')
result = connection.send_and_get_reply(request)
eog.kill()
if result.body[0] != "Operation was cancelled":
file = Path(filename)
np_array = numpy.frombuffer(file.read_bytes(), numpy.uint8)
image = cv2.imdecode(np_array, cv2.IMREAD_COLOR)
s_x, s_y, width, height = result.body
m_x, m_y, _, _ = get_monitor_info()
d_x, d_y = s_x - m_x, s_y - m_y
_, new_image = cv2.imencode('.png', image[d_y:d_y+height, d_x:d_x+width])
return new_image
else: raise RuntimeError
def image_to_text(image: Image) -> str:
tool = pyocr.get_available_tools()[0]
print(f"{tool.get_name()} will be used for text recognition.")
text = tool.image_to_string(image, "jpn_best+eng_best")
return text
def copy_to_clipboard(text: str) -> None:
cmd=f"wl-copy '{text.strip()}'"
subprocess.check_call(cmd, shell=True)
def copy_from_clipboard() -> bytes:
reslut = subprocess.run("wl-paste", capture_output=True, shell=True)
if reslut.returncode != 0: raise RuntimeError
buffer = reslut.stdout
np_array = numpy.frombuffer(buffer, numpy.uint8)
image = cv2.imdecode(np_array, cv2.IMREAD_GRAYSCALE)
if image.size > 0: return buffer
else: raise RuntimeError
def main() -> int:
data = copy_from_clipboard()
handle = io.BytesIO(data)
image = Image.open(handle)
text = image_to_text(image)
copy_to_clipboard(text)
return 0
if __name__ == "__main__":
try: main()
except KeyboardInterrupt:
print("Operation aborted by user.")
exit(-1)
except RuntimeError:
print("Something goes wrong.")
exit(-1)
@RozeFound
Copy link
Author

RozeFound commented Jan 28, 2023

Python dependencies: jeepney, pyocr, pillow
System dependencies: tesseract, tessdata, wl-clipboard
Tested on EndeavourOS, Gnome 43.2

Screenshot mechanism adapted from pyscreenshot.
And yeah, almost zero error-checking coz I'm lazy.

@RozeFound
Copy link
Author

Added a few experiments, now it just grabs image from clipboard

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment