In order to refresh the old drawn area, you need to either call win32gui.UpdateWindow or something similar to update your specific window, but since you're not technically drawing on a surface, but the entire monitor. You'll need to invalidate the entire region of your monitor in order to tell windows to re-draw anything on it (or so I understand it).
And to overcome the slowness, instead of using for loops to create the boundary which will take X cycles to iterate over before completing the rectangle, you could use win32ui.Rectangle
to draw it in one go:
import win32gui, win32ui
from win32api import GetSystemMetrics
dc = win32gui.GetDC(0)
dcObj = win32ui.CreateDCFromHandle(dc)
hwnd = win32gui.WindowFromPoint((0,0))
monitor = (0, 0, GetSystemMetrics(0), GetSystemMetrics(1))
while True:
m = win32gui.GetCursorPos()
dcObj.Rectangle((m[0], m[1], m[0]+30, m[1]+30))
win32gui.InvalidateRect(hwnd, monitor, True) # Refresh the entire monitor
Further optimizations could be done here, like not update the entire monitor, only the parts where you've drawn on and so on. But this is the basic concept :)
And to create a rectangle without the infill, you could swap Rectangle
for DrawFocusRect
for instance. Or for more control, even use win32gui.PatBlt
And apparently setPixel
is the fastest, so here's my final example with color and speed, altho it's not perfect as the RedrawWindow
doesn't force a redraw, it simply asks windows to do it, then it's up to windows to honor it or not. InvalidateRect
is a bit nicer on performance as it asks the event handler to clear the rect when there's free time to do so. But I haven't found a way more agressive than RedrawWindow
, even tho that is still quite gentle. An example to this is, hide the desktop icons and the below code won't work.
import win32gui, win32ui, win32api, win32con
from win32api import GetSystemMetrics
dc = win32gui.GetDC(0)
dcObj = win32ui.CreateDCFromHandle(dc)
hwnd = win32gui.WindowFromPoint((0,0))
monitor = (0, 0, GetSystemMetrics(0), GetSystemMetrics(1))
red = win32api.RGB(255, 0, 0) # Red
past_coordinates = monitor
while True:
m = win32gui.GetCursorPos()
rect = win32gui.CreateRoundRectRgn(*past_coordinates, 2 , 2)
win32gui.RedrawWindow(hwnd, past_coordinates, rect, win32con.RDW_INVALIDATE)
for x in range(10):
win32gui.SetPixel(dc, m[0]+x, m[1], red)
win32gui.SetPixel(dc, m[0]+x, m[1]+10, red)
for y in range(10):
win32gui.SetPixel(dc, m[0], m[1]+y, red)
win32gui.SetPixel(dc, m[0]+10, m[1]+y, red)
past_coordinates = (m[0]-20, m[1]-20, m[0]+20, m[1]+20)
Issues with positions and resolution? Be aware that high DPI systems tend to cause a bunch of issues. And I haven't found many ways around this other than going over to a OpenGL solution or using frameworks such as wxPython or OpenCV other than this post: Marking Your Python Program as High DPI Aware Seamlessly Windows
Or changing the Windows display scale to 100%
:
This causes the positioning issue to go away, perhaps take this to account by querying the OS for the scale and compensate.
The only reference I could find on the "clearing the old drawings" was this post: win32 content changed but doesn't show update unless window is moved tagged c++
win winapi
. Hopefully this saves some people from searching before finding a good example.