So far my progress is this:
hwnd = win32gui.FindWindow(None, "Demo")
posX, posY, width, height = win32gui.GetWindowPlacement(hwnd)[4]
windowStyles = win32con.WS_EX_LAYERED | win32con.WS_EX_TRANSPARENT
win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, windowStyles)
win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, posX,posY, 0,0, win32con.SWP_NOSIZE)
windowAlpha = 180
win32gui.SetLayeredWindowAttributes(hwnd, win32api.RGB(0,0,0),
windowAlpha, win32con.LWA_ALPHA)
This code makes the "Demo" window click-through and transparent. But it includes all the controls and contents inside as well. I need only to make the window background (or everything with alpha 0) transparent and click-through, nothing else will be affected. Something like a desktop widget which will also be draggable. How can I achieve this?
Related
I am creating a multi touch accuracy checking device that indicates where the user should touch the screen. It overlays a semi transparent image over an exe called using subprocess with wx as shown here.
I want to output to terminal to prompt user for each data collection point but I can't exit MainLoop() to prompt the user, collect data, and repeat. Press and unpress of tab records one instance of touch in the exe.
Thank you
def scale_bitmap(bitmap, width, height):
image = wx.ImageFromBitmap(bitmap)
image = image.Scale(width, height, wx.IMAGE_QUALITY_HIGH)
result = wx.Bitmap(image)
return result
for x in range(1, 7):
app = wx.App()
trans = 100
frame1 = wx.Frame(None, -1, "KEA", style=wx.CLIP_CHILDREN | wx.STAY_ON_TOP)
# create the class instance
frame1.ShowFullScreen(True)
image_file = "6dataPoints.jpg"
bmp1 = wx.Image(image_file, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
bmp1 = scale_bitmap(bmp1, GetSystemMetrics(1) * 1.5, GetSystemMetrics(1))
bitmap1 = wx.StaticBitmap(frame1, -1, bmp1, (-100, 0))
hwnd = frame1.GetHandle()
extendedStyleSettings = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE,
extendedStyleSettings | win32con.WS_EX_LAYERED | win32con.WS_EX_TRANSPARENT)
win32gui.SetLayeredWindowAttributes(hwnd, 0, 255, win32con.LWA_ALPHA)
frame1.SetTransparent(trans)
print("Place fingers over data collection point %d" % (x))
pyautogui.keyDown("tab")
pyautogui.keyUp("tab")
app.MainLoop()
Why do you want to use the terminal for output and prompts instead of doing it in the GUI? It would make sense to just show some (modal) dialog in the GUI instead.
If you really want to use both (blocking) terminal IO and GUI from the same program, the simplest way is to do it from two different threads, with one thread reserved for the GUI stuff only.
I'm trying to use skia-python with glfw to draw various shapes and text onto a transparent floating overlay. I have a small demo working for my purposes, but it behaves differently if the window is created with full screen resolution versus created with anything smaller.
The code here alternates between drawing some red text or a green circle, and attempts to clear the canvas in between using canvas.clear(skia.ColorTRANSPARENT). This behaves exactly as I want if the window is created with dimensions anything less than 1920x1080 (the resolution of my screen). If I create the window with these full screen dimensions, instead of clearing the canvas this call fills the screen with black (and then the rest of the code still works, alternating between text and circle).
import contextlib, glfw, skia
from OpenGL import GL
import time
WIDTH, HEIGHT = 1920, 1080
#WIDTH, HEIGHT = 1919, 1079
#contextlib.contextmanager
def glfw_window():
if not glfw.init():
raise RuntimeError('glfw.init() failed')
glfw.window_hint(glfw.STENCIL_BITS, 8) # ?
glfw.window_hint(glfw.SAMPLES, 14) # ?
glfw.window_hint(glfw.DECORATED, 0)
glfw.window_hint(glfw.TRANSPARENT_FRAMEBUFFER, 1)
glfw.window_hint(glfw.FLOATING, 1)
window = glfw.create_window(WIDTH, HEIGHT, '', None, None)
glfw.make_context_current(window)
yield window
glfw.terminate()
#contextlib.contextmanager
def skia_surface(window):
context = skia.GrDirectContext.MakeGL()
(fb_width, fb_height) = glfw.get_framebuffer_size(window)
backend_render_target = skia.GrBackendRenderTarget(
fb_width,
fb_height,
0, # sampleCnt
0, # stencilBits
skia.GrGLFramebufferInfo(0, GL.GL_RGBA8))
surface = skia.Surface.MakeFromBackendRenderTarget(
context, backend_render_target, skia.kBottomLeft_GrSurfaceOrigin,
skia.kRGBA_8888_ColorType, skia.ColorSpace.MakeSRGB())
assert surface is not None
yield surface
context.abandonContext()
def drawString(canvas):
paint = skia.Paint(AntiAlias=True, Color=skia.ColorRED)
font = skia.Font(skia.Typeface('meiryo'), 36)
canvas.drawString('あかさたな', 100, 100, font, paint)
def drawCircle(canvas):
paint = skia.Paint(Color=skia.ColorGREEN)
canvas.drawCircle(100, 100, 40, paint)
with glfw_window() as window:
GL.glClear(GL.GL_COLOR_BUFFER_BIT)
with skia_surface(window) as surface:
with surface as canvas:
while (glfw.get_key(window, glfw.KEY_ESCAPE) != glfw.PRESS
and not glfw.window_should_close(window)):
canvas.clear(skia.ColorTRANSPARENT)
if int(time.time()) % 2 == 0:
drawString(canvas)
else:
drawCircle(canvas)
surface.flushAndSubmit()
glfw.swap_buffers(window)
glfw.poll_events()
#glfw.wait_events()
For the purpose of demonstration, this is the entire working demo. Most of it comes from this skia-python documentation. I think the only packages it needs are glfw (2.5.3), PyOpenGl (3.1.6), and skia-python (87.4)
My actual use case will use a slightly different loop and doesn't need to draw to the screen as frequently as this demo, but it does need to periodically clear the canvas. I don't actually need it to be perfectly full screen, I can use 1919x1079, mostly I'm just curious what's going on here. This is being tested on Windows 10 with Python 3.10 by the way
I am currently controlling a game with python by sending mouse and keystroke commands. What I am looking to do is have a transparent Tkinter window lay overtop of the game to provide some information such as mouse location and pixel color.
I am familiar with changing the window's alpha attribute to make it transparent but have no idea how to always keep that window in front and have mouse clicks pass through it.
My current method of controlling the game involves taking screenshots in certain locations and analyzing the color content. I will also need some way to do this without the Tkinter window interfering.
Pyscreenshot is used for screenshots
win32api is used for clicking
Thank you,
Alec
you can use the SetWindowLong function of win32gui module. If you want a transparent click through window you have to apply GWL_EXSTYLE's ony our window. Therefore you need the windowhandle of your Window.
hwnd = win32gui.FindWindow(None, "Your window title") # Getting window handle
# hwnd = root.winfo_id() getting hwnd with Tkinter windows
# hwnd = root.GetHandle() getting hwnd with wx windows
lExStyle = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
lExStyle |= win32con.WS_EX_TRANSPARENT | win32con.WS_EX_LAYERED
win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE , lExStyle )
If you want to change the transparency of your window via winapi use SetLayeredWindowAttributes.
EDIT: Examplecode for an overlay always-on-top transparent window, which pass through clicks. It gets the current desktopimage and creates a transparent overlay, so you can enjoy your desktop background image.
from win32api import GetSystemMetrics
import win32con
import win32gui
import wx
def scale_bitmap(bitmap, width, height):
image = wx.ImageFromBitmap(bitmap)
image = image.Scale(width, height, wx.IMAGE_QUALITY_HIGH)
result = wx.BitmapFromImage(image)
return result
app = wx.App()
trans = 50
# create a window/frame, no parent, -1 is default ID
# change the size of the frame to fit the backgound images
frame1 = wx.Frame(None, -1, "KEA", style=wx.CLIP_CHILDREN | wx.STAY_ON_TOP)
# create the class instance
frame1.ShowFullScreen(True)
image_file = win32gui.SystemParametersInfo(win32con.SPI_GETDESKWALLPAPER,0,0)
bmp1 = wx.Image(image_file, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
bmp1 = scale_bitmap(bmp1,GetSystemMetrics(1)*1.5,GetSystemMetrics(1))
bitmap1 = wx.StaticBitmap(frame1, -1, bmp1, (-100, 0))
hwnd = frame1.GetHandle()
extendedStyleSettings = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, extendedStyleSettings | win32con.WS_EX_LAYERED | win32con.WS_EX_TRANSPARENT)
win32gui.SetLayeredWindowAttributes(hwnd, 0, 255, win32con.LWA_ALPHA)
frame1.SetTransparent(trans)
def onKeyDown(e):
global trans
key = e.GetKeyCode()
if key==wx.WXK_UP:
print trans
trans+=10
if trans >255:
trans = 255
elif key==wx.WXK_DOWN:
print trans
trans-=10
if trans < 0:
trans = 0
try:
win32gui.SetLayeredWindowAttributes(hwnd, 0, trans, win32con.LWA_ALPHA)
except:
pass
frame1.Bind(wx.EVT_KEY_DOWN, onKeyDown)
app.MainLoop()
You can dynamically change the transparency with the arrow keys Up/Down.
Notice, the windowframe is created with 'wx', but should work with tkinter also.
Feel free to use the code as you like.
I got a problem to set the transparent background of drawing area.
The reason what I want this is that I have a main window where is background
def draw_pixbuf(self,widget, event):
path = 'test.jpg'
pixbuf = gtk.gdk.pixbuf_new_from_file(path)
scaled_buf = pixbuf.scale_simple(800,480,gtk.gdk.INTERP_BILINEAR)
widget.window.draw_pixbuf(widget.style.bg_gc[gtk.STATE_NORMAL], scaled_buf, 0, 0, 0,0)
self.window = gtk.Window()
self.window.connect("delete-event", gtk.main_quit)
self.window.set_decorated(False)
self.window.set_position(gtk.WIN_POS_CENTER_ALWAYS)
self.window.set_size_request(800,480)
hbbox = gtk.HBox()
hbbox.connect('expose-event', self.draw_pixbuf)
so my HBox has this background and I have two Fixed containers in it - one button and one drawing area.
fix = gtk.Fixed()
image = gtk.Image()
image.set_from_file("close.png")
event_box = gtk.EventBox()
event_box.add(image)
event_box.set_size_request(30,30)
event_box.set_visible_window(False)
event_box.connect("button_press_event",gtk.mainquit)
fix.put(event_box,140,0)
self.darea = gtk.DrawingArea()
self.darea.set_size_request(450,300)
self.darea.connect("expose-event", self.expose)
fix2 = gtk.Fixed()
fix2.put(self.darea,175,90)
hbbox.pack_start(fix2, True, False, 10)
hbbox.pack_end(fix, True, False, 10)
#hbbox.pack_start(self.darea,True,False,10)
self.window.add(hbbox)
self.window.show_all()
But the drawing area overrides that HBox's background with its own default background(grey). I am able to change background by modify_bg function, but I want it transparent to write the cairo animated text on the HBox's background.
So the goal is to have background image of the window and draw the caito animated text onto it and not to grey rectangle (drawing area's background).
I am new to GTK so maybe I miss something important how to do it.
I hope you can help. Thank you.
I also have not found solution and swiched to Cairo (Pycairo)
See more there: GTK drawable area transparent background color
Short question:
I know how to draw text on a wx.Bitmap, but how can I draw text on a wx.Icon in wxpython so that it does not appear transparent?
Long question:
I have a wxpython based GUI application, that has a taskbar icon, which I set using mytaskbaricon.SetIcon("myicon.ico").
Now I would like to dynamically put some text on the icon, so I tried to use the wx .DrawText method as explained here.This works fine if I test this for bitmaps (which I use in menu items).
However, the taskbar requires an wxIcon instead of a wxBitmap. So I figured I'll convert the icon to a bitmap, draw the text, and then convert it back to an icon. This works, except that the text is not shown transparent. Why ? And how can I make the text NOT transparent ?
My code is as roughly follows:
import wx
class MyTaskBarIcon(wx.TaskBarIcon):
...
icon = wx.Icon("myicon.ico", wx.BITMAP_TYPE_ICO)
bmp = wx.Bitmap("myicon.ico", wx.BITMAP_TYPE_ICO)
memDC = wx.MemoryDC()
memDC.SetTextForeground(wx.RED)
memDC.SelectObject(bmp)
memDC.DrawText("A", 0, 0)
icon.CopyFromBitmap(bmp)
self.SetIcon(icon, APP_NAME_WITH_VERSION)
...
So, no errors raised and myicon.ico is shown, but the letter A is transparant (instead of red). If I use a .bmp file to start with (myicon.bmp) the text appears in the correct color (but the borders are jagged). I've played around with masks, foreground and background colors, but that didn't help.
(I am using Windows 7, Python 2.6, wxpython 2.8)
Edit: I've shortened my explanation, and made the code more self-contained
Short answer: It seems to me that there is a bug in this particular piece of wx code. I am going to report it and see what comes out of it.
Long answer: You can hack your way around. Setup a color, which is not used in the image. Then draw using that color and when done, fix alpha values and color of those pixels to match your expectation:
import wx
from wx import ImageFromStream, BitmapFromImage, EmptyIcon
import cStringIO, zlib
# ================================ ICON ======================================
def getData():
return zlib.decompress(
'x\xda\x01\x97\x03h\xfc\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\
\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\
\x08\x08\x08|\x08d\x88\x00\x00\x03NIDAT8\x8dm\xd2ML\x9bu\x00\xc7\xf1\xef\xf3\
<\xed\xda><\xa3#\xcb\x8a\x0cp\xac8\x15\x87\x89/ \x11\xd1d&:5&#n\xc9\\\xa2\
\xc6\xc3b\xe2\xd1y0Y2\xa3q^\xcc\xb8\x9a\xb9\xf9rQc\xc6\x0es\xa4\xd1\x91\xe98\
\xc8\x96\xb98H\xc3\x8b\xc0\xc6\x00\x91\xd2\xb2\xa7}\xda\xe7\xa5\xcf\xd3\xf6\
\xf9{0\xa2\x07\xbf\xf7_\xf29\xfc$\x00\xf1>\xb2\xd9\xc7\tI0$\xc0\xd5d\x06\xa5\
\x17q\xf9O\xa5\x0b$$\x85KB\xa2\xec\xcb\xbc\x1e}\x81\xdf\x01$q\x9a`>\xce\xc9`\
\xc7\x91#\xa1\xce\xa3;\xed\xdbg\xb3s\x19c\xe1\x9cz\xbe*A\x0f\x80\x80\xf4A\
\xeb\xb0\xfcPG\xa2;\x10\x8aI\xe5\xd9\x93\x8bB\xe6`l\x88U)\xf3-\xc7\xc3\xbb_{\
;r\xef\xe1Vci\xa4\xb0\xbc:\x17\xb8\xdczQ\xd3B5"A\x1f\x00\xa7"\xe39\x16\xfb\
\xd6_\xb1wu\x1f#\xa9\x15-k\xe6\xd4j\xa2D\xbf\xec\x95\x91\xe5PGX_\x18),.\xcei\
W\xdb\xbf\xd3:\xb7{49\x0e\xeem\x1dkAG+Z\xb4l\xdf\xc6o-\xc3\xea\x9fK\xbf\x84\
\xe5\xa6\xfe&\xa1>\xa8\xad)\xec\x96n}\xc6`E\xa8g7\x95d\xdbD\xf2\x82\xda\xae\
\x06\x08\xd95\x1e\xeej\xa2\xa1^F \xa1\x1b5\xae\xcf\xe5\xa8D\x14\xea\xf4\xf3\
\xdco\x9es\xb7\x9933\xe1Z\xe9U\t\xe0\xd8\xe7\x17?\t4\xecz7\x99\xd0hp\x05\x87\
\xf6u\x927\x0c6-\x87\xf6\xd6\x16\x00\xaa\x02\xbeN\xdd\xc2\xd7\x04\x99\xec:9K\
\xf9\xf8\xd37\x07\x8e\xcb\x00\x99\xca=\xbd\xbe\x00\xbf\xe4\xb1wO\x0c\xbb*\
\x08\x06\x83\x8c\xfd\xf8\x03E\xc3\xa0\xe2\xba\\\x1a\xfb\x99\xee=q\x8c\xac\
\x83#7RtC\x03\x00\x01\x80r\xd9\xea\xa9z2\x86\xeb\x13\x8bEpk\x82:U\xe5\x8f\
\x95\x15\xc6~\x1a\'=5\xc9\xb3\xcf\xef\xa7q\x87Jn\xd3A4\x04)\x97\xad\x1e\x00\
\x19\xc0\xb3-,\xbb\x82\xe3\xf9\xb85\xa8\xf8\x905J\xd4i\x1a\xe9\xa9I^:0\xc4#\
\xbd}\xb8U\xa8x>\x96]\xc1\xb3-\xb6\x04^\xd9N\x17K\x91gv\xc6\x03,el\xeek\x8b\
\x82\x1c\xe6\xd1\xc7\xfby\xa0g/j\xb4\x1e\xd3\x85\xd5\x8cE0"\x91+\xd9xe;\xfd\
\xaf\xc0\xb1\xae\x14\r\x03\xbd\xecr\xf5\xe6\x06\xc1\x10\xd4\x85\x83<5\xf8$\
\xf1\xc6zB\x80\x16\x86_of\xf1\xf0(\x1a\x06\x9ec]\xd9\x12\xb8\xb63\xea:\xe6\
\xa1\xd9\x9a\xd2-\xb7U\xf9bD\xf0\\o\x82\xaeD\x1d\x08X\xc9Z\x8c^\xcbP4\xd6\
\x99\xdf\xb00\xf3k3\x08e\x14#\xfa\xe7\xeb}GO\xbd\xf5Xr\xc7\xf0BAS[\xe3\x1a\
\xb1P\x08\xc5\x97\xa9\xf9\x82\x8aT\xc5\xf0\\\xaa\xd5*\xaa\xb8k\xa7\xefl\xbes\
\xfd\xcc\xb1\xd3[\x02\x80\xe17\x9e\x98\x8fF\xa3jv3_;12\xaf\xccJ*\xb2\x12\x06\
\xc0\xaf\x95iV+\xbc\xf7rR\xc8rcD\xa2kv\xe0\xcc\xdf;\x19 \x95J5\x17\n\x85\xef\
\xc3\xe10f\xa9`\x98\xf9;\x1f\xda\xb9\xe9qk\xe3\x86nm\xdc\xd0\xed\xdc\xf4\xf8\
\xf2\xf2\xfc\x07\x85B\xdel\x8e\xc7%]\xd7/\xa7R\xa9\xe4\x96\xc04M\xc7q\x9c\
\xb5\x89\x89\x89N!\xc4\xd3S\xdf|4\xcd\xfftw\xff\x97_]\xd3\xf5I\xc0\xf2}\xdf\
\x02\xf8\x0b\xc1.\x9e\xd8Y.\x85\x85\x00\x00\x00\x00IEND\xaeB`\x822\x86\xba\
\xb3' )
def getBitmap():
return BitmapFromImage(getImage())
def getImage():
stream = cStringIO.StringIO(getData())
return ImageFromStream(stream)
def getIcon():
icon = EmptyIcon()
icon.CopyFromBitmap(getBitmap())
return icon
# ============================================================================
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.number = 0
self.Bind(wx.EVT_CLOSE, self.OnClose)
self.panel = wx.Panel(self)
self.button = wx.Button(self.panel, label="Test")
self.button.Bind(wx.EVT_BUTTON, self.OnButton)
self.tbicon = wx.TaskBarIcon()
self.tbicon.SetIcon(getIcon(), "Test")
self.sizer = wx.BoxSizer()
self.sizer.Add(self.button)
self.panel.SetSizerAndFit(self.sizer)
self.Show()
# --------------------------------------------------------------------------
def OnClose(self, e):
self.tbicon.Destroy()
self.Destroy()
wx.Exit()
# --------------------------------------------------------------------------
def OnButton(self, e):
# HERE WE GO!
self.number += 1
bitmap = getBitmap()
# Find unused color
image = bitmap.ConvertToImage()
my_solid_color = wx.Color(*image.FindFirstUnusedColour(0, 0, 0)[1:])
# Use the unused *unique* color to draw
dc = wx.MemoryDC()
dc.SetTextForeground(my_solid_color)
dc.SelectObject(bitmap)
dc.DrawText(str(self.number), 0, 0)
dc.SelectObject(wx.NullBitmap)
# Convert the bitmap to Image again
# and fix the alpha of pixels with that color
image = bitmap.ConvertToImage()
for x in range(image.GetWidth()):
for y in range(image.GetHeight()):
p = wx.Colour(image.GetRed(x, y),
image.GetGreen(x, y),
image.GetBlue(x, y))
if p == my_solid_color:
image.SetAlpha(x, y, 255) # Clear the alpha
image.SetRGB(x, y, 0, 0, 0) # Set the color that we want
# Convert back to Bitmap and save to Icon
bitmap = image.ConvertToBitmap()
icon = wx.IconFromBitmap(bitmap)
self.tbicon.SetIcon(icon, "Test")
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
Note: A had to add some icon. You can ignore that part of the code.
Just a guess, but perhaps create your initial icon as an "EmptyIcon", then copy the bmp to it.
import wx
class MyTaskBarIcon(wx.TaskBarIcon):
...
icon = wx.EmptyIcon()
bmp = wx.Bitmap("myicon.ico", wx.BITMAP_TYPE_ICO)
bmp = WriteTextOnBitmap("A", bmp, color=wx.RED) #this function is as in the link above
icon.CopyFromBitmap(bmp)
self.SetIcon(icon, APP_NAME_WITH_VERSION)
...