Skip to content

Instantly share code, notes, and snippets.

@trappedinspacetime
Created May 22, 2022 10:53
Show Gist options
  • Save trappedinspacetime/e31822c75c5339bd0622db2001060857 to your computer and use it in GitHub Desktop.
Save trappedinspacetime/e31822c75c5339bd0622db2001060857 to your computer and use it in GitHub Desktop.
drag transparent image
#!/usr/bin/env python3
# taken from https://github.com/s-zeid/bin/blob/main/display-transparent
"""Displays an image without a window border or background.
Based on code from http://bit.ly/qfvule
"""
import argparse
import os
import sys
from collections import namedtuple
import gi # apt install python3-gi
gi.require_version("cairo", "1.0") # apt install python3-gi-cairo
from gi.repository import cairo
gi.require_version("Gtk", "3.0") # apt install gir1.2-gtk-3.0
from gi.repository import Gtk as gtk
gi.require_version("Gdk", "3.0") # same Debian package as for GTK+ 3
from gi.repository import Gdk as gdk
from gi.repository import GdkPixbuf as gdk_pixbuf
Padding = namedtuple("Padding", "top,right,bottom,left")
def _close_callback(window, data):
"""Called when the window is closed."""
window.hide()
gtk.main_quit()
def _drag_callback(window, event):
"""Called when the window is clicked on."""
window.begin_move_drag(
event.button,
int(round(event.x + window.get_window().get_root_origin()[0])),
int(round(event.y + window.get_window().get_root_origin()[1])),
event.time
)
def make_transparent(window, cr):
"""Makes a window transparent."""
cr.set_source_rgba(0, 0, 0, 0)
cr.set_operator(cairo.Operator.SOURCE)
cr.paint()
cr.set_operator(cairo.Operator.OVER)
def set_window_position(window, x, y):
pos = list(window.get_position())
if x is not None:
pos[0] = int(x)
if y is not None:
pos[1] = int(y)
if x is not None or y is not None:
window.move(*pos)
def display_transparent(image_filename, size_factor=1, opacity=1,
x=None, y=None, padding=None, nearest=False):
if isinstance(padding, str):
padding = [int(i.strip()) for i in padding.split(",")]
if not padding:
padding = [0, 0, 0, 0]
elif len(padding) == 1:
padding = padding * 4
elif len(padding) == 2:
padding = padding * 2
elif len(padding) == 3:
padding = [padding[0], padding[1], padding[2], padding[1]]
elif len(padding) > 4:
padding = padding[:4]
padding = Padding(*[int(round(i)) for i in padding])
win = gtk.Window()
win.set_title(os.path.basename(image_filename))
win.set_decorated(False)
win.set_resizable(False)
gtk.Widget.set_opacity(win, opacity)
# Make sure that the main loop terminates when the window is closed
win.connect("delete-event", _close_callback)
# Makes the window paintable, so we can draw directly on it
win.set_app_paintable(True)
# This sets the windows colormap, so it supports transparency.
# This will only work if the wm support alpha channel
screen = win.get_screen()
rgba = screen.get_rgba_visual()
win.set_visual(rgba)
# This will actually make the window transparent.
#win.connect("draw", make_transparent)
# Load and scale the image
pixbuf = gdk_pixbuf.Pixbuf.new_from_file(image_filename)
if size_factor != 1:
pixbuf = pixbuf.scale_simple(
int(round(pixbuf.get_width() * size_factor)),
int(round(pixbuf.get_height() * size_factor)),
gdk_pixbuf.InterpType.BILINEAR if not nearest else gdk_pixbuf.InterpType.NEAREST,
)
# Add the image to a Fixed container for padding purposes
fixed = gtk.Fixed()
img = gtk.Image.new_from_pixbuf(pixbuf)
fixed.add(img)
fixed.move(img, padding.left, padding.top)
# Add the container to the window
width_padded = pixbuf.get_width() + padding.left + padding.right
height_padded = pixbuf.get_height() + padding.top + padding.bottom
win.set_size_request(width_padded, height_padded)
win.add(fixed)
# Make the window draggable
win.connect("button-press-event", _drag_callback)
win.add_events(gdk.EventMask.BUTTON_PRESS_MASK)
# Show and position window
win.connect("map-event", lambda widget, event: set_window_position(widget, x, y))
win.show_all()
# Enter GTK main loop
gtk.main()
def main(argv):
parser = argparse.ArgumentParser(
prog=os.path.basename(argv[0]),
description="Displays an image with no background or window decorations."
)
parser.add_argument("filename", help="The image to display")
parser.add_argument("-s", "--size", type=float, default=100.,
help="Desired size of the image as a percentage")
parser.add_argument("-n", "--nearest", action="store_true",
help="Use nearest neighbor sampling when scaling images"
" (results in a pixelated appearance when scaling up)")
parser.add_argument("-o", "--opacity", type=float, default=100.,
help="Desired opacity of the image as a percentage")
parser.add_argument("-x", type=str, default=None,
help="Initial X position (default: auto)")
parser.add_argument("-y", type=str, default=None,
help="Initial X position (default: auto)")
parser.add_argument("-p", "--padding", default=None,
help="Padding (default: None; format:"
" {all-sides} | {top/bottom},{left/right} |"
" {t},{r/l},{b} | {t},{r},{b},{l})")
try:
args = parser.parse_args(argv[1:])
except SystemExit as exit:
return exit.code
display_transparent(
args.filename,
size_factor=args.size * 0.01,
opacity=args.opacity * 0.01,
x=args.x,
y=args.y,
padding=args.padding,
nearest=args.nearest,
)
if __name__ == "__main__":
try:
sys.exit(main(sys.argv))
except (KeyboardInterrupt, EOFError):
pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment