1

I've been looking through Python GUI libs such as Tkinter , kivy and pygame for a way to make a transparent window with Opaque assets (such as a button or a sprite) so that only the window is transparent letting me see whats behind it, but the button, image etc is still solid appearing to float in mid air.

In the case example of the sprite image, it would look like only the sprite is floating on the screen with no bordering window, box etc.

Tkinter has attributes('-alpha',07) but this is applied to all assets.

kivy has a background_color which affects opacity but 0 doesn't make it transparent only grey.

From what I have read this appears to be a difficult problem and possibly requires OS level changes, some related stack questions with no answers linked here and here.

but I wanted to ask if anyone had a potential solution?

Murchie85
  • 815
  • 2
  • 12
  • 27
  • Have you tried setting the background to something like `"red"` and then using: `.attributes("-transparentcolor", "red")`. That will make all of the things that are coloured red transparent. – TheLizzard Jun 21 '21 at 19:11
  • Him yes unfortunately I tried that but no luck as it doesn't work on Mac or Linux the error looks like `bad attribute "-transparentcolor": must be -alpha, -fullscreen, -modified, -notify, -titlepath, -topmost, or -transparent` – Murchie85 Jun 21 '21 at 19:15
  • 1
    You might be able to do this with shaped windows, which Kivy supports on some platforms. In this case the unused space in the window wouldn't be transparent but rather not there at all. – inclement Jun 21 '21 at 19:50

1 Answers1

1

Note: Only works on windows. I am looking into other OSs, but there is not standard cross-platform solution as far as I can tell. You will always have to find a solution on a C level and adapt it to something you can use from python.


I create a small package and shared it on github. Look at test_pkg.py for a small example on how to use. Currently, the package implements the below solution for windows and another using the Shape Extension for X11 based systems.


If you want the entire window to become invisible except for a few parts, without blending with the background, you can use SetLayeredWindowAttributes as mentioned in this answer. I am showing the code for pygame, but similar stuff should also be possible with other libraries.

The important parts of the windows C interface are here:

import pygame
import ctypes
from ctypes import windll, WINFUNCTYPE
from ctypes import wintypes

prototype = WINFUNCTYPE(
    wintypes.BOOL,
    wintypes.HWND,
    wintypes.COLORREF,
    wintypes.BYTE,
    wintypes.DWORD
)
paramflags = (1, "hwnd"), (1, "crKey"), (1, "bAlpha"), (1, "dwFlags")
SetLayeredWindowAttributes = prototype(("SetLayeredWindowAttributes", windll.user32), paramflags)

prototype = WINFUNCTYPE(
    wintypes.LONG,
    wintypes.HWND,
    ctypes.c_int,
    wintypes.LONG,
)
paramflags = (1, "hwnd"), (1, "nIndex"), (1, "dwNewLong")
SetWindowLongA = prototype(("SetWindowLongA", windll.user32), paramflags)

prototype = WINFUNCTYPE(
    wintypes.LONG,
    wintypes.HWND,
    ctypes.c_int,
)
paramflags = (1, "hwnd"), (1, "nIndex")
GetWindowLongA = prototype(("GetWindowLongA", windll.user32), paramflags)

GWL_EXSTYLE = -20
WS_EX_LAYERED = 0x00080000

LWA_ALPHA = 0x2
LWA_COLORKEY = 0x1


def get_handle():
    wm_info = pygame.display.get_wm_info()
    return wm_info["window"]

It can then be used like this:

INVISIBLE = 255, 0, 255

# Window has to have been created at this point

hwnd = pygame.display.get_wm_info()["window"]
SetWindowLongA(hwnd, GWL_EXSTYLE, GetWindowLongA(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED)
SetLayeredWindowAttributes(hwnd, wintypes.RGB(*INVISIBLE), 0, LWA_COLORKEY)

This will make everything colored exactly INVISIBLE not be drawn. You can also use some other random color (or black if you want) instead of the 'error pink' I am using.

Note that you probably also want to use pygame.NOFRAME as flag for set_mode to not render the border. Note that you can't access the close button then.

MegaIng
  • 7,361
  • 1
  • 22
  • 35
  • I already suggested that with `tkinter` but it only works for Windows as OP pointed out (in the comments attached to the question). I think OP wants something that will also work on Linux and MacOS – TheLizzard Jun 21 '21 at 21:23
  • @TheLizzard Yeah, I missed that. Seems like while windows has builtin support for this, linux (e.g. X11) doesn't. – MegaIng Jun 21 '21 at 21:31
  • Although I have to admit that this is an interesting way of doing it. Calling a win api function wouldn't have been on my list of choices :D. – TheLizzard Jun 21 '21 at 21:35
  • @TheLizzard If I remember correctly, it is somewhat standard to do such stuff with pygame. That is the reason why the handle is so accessible. (there are a few other thinks on might want to call, like 'always on top' or 'make the window icon blink', I think). And I don't think other OSs will be any better... – MegaIng Jun 21 '21 at 21:37
  • You can do all of these things in `tkinter` by just calling methods on the window. There is functionality that is lacking but it's rare to need it. – TheLizzard Jun 21 '21 at 21:40
  • @TheLizzard Look at the edit I added. I got it a new solution for X11 servers, which should cover a lot of unix distributions. I sadly don't have access to a mac. – MegaIng Jun 22 '21 at 01:55
  • I don't have a mac either. In `xlib.py`, you have `for name, arg in func.__annotations__.items():` and then `eval(arg)`. It is safe to have the `eval(arg)`? Also shouldn't all annotations be classes of datatypes? – TheLizzard Jun 22 '21 at 08:44
  • @TheLizzard I am using the helper function only inside of xlib, so `eval` should be safe. While annotations in general should only be used for typing, here I am 'misusing' them to create function ctypes signatures. I will maybe refactor everything to make it a bit more clean soon. – MegaIng Jun 22 '21 at 11:28
  • If I add `"print(\"Hi\")"` as the annotation for one of the inputs, it is executed. I think you should just remove the `arg = eval(arg) if isinstance(arg, str) else arg` line. – TheLizzard Jun 22 '21 at 11:41
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/234065/discussion-between-megaing-and-thelizzard). – MegaIng Jun 22 '21 at 12:02