python用 号画空心矩形_如何使用Python在屏幕上绘制空白矩形

本文介绍了一种使用Python在屏幕上实时跟随鼠标移动显示空心矩形的方法。通过win32gui库实现像素点的设置和屏幕刷新,解决旧矩形残留问题。示例代码中使用了SetPixel和InvalidateRect函数,通过不断更新屏幕来避免闪烁和提高性能。
摘要由CSDN通过智能技术生成

I am not an expert and I am trying to show a rectangle on screen which follows mouse movements from a settle starting point, just as when you select something in word or paint. I came with this code:

import win32gui

m=win32gui.GetCursorPos()

while True:

n=win32gui.GetCursorPos()

for i in range(n[0]-m[0]):

win32gui.SetPixel(dc, m[0]+i, m[1], 0)

win32gui.SetPixel(dc, m[0]+i, n[1], 0)

for i in range(n[1]-m[1]):

win32gui.SetPixel(dc, m[0], m[1]+i, 0)

win32gui.SetPixel(dc, n[0], m[1]+i, 0)

As you can see, the code will draw the rectangle, but the previous ones will remain until the screen updates.

The only solution I've came with is to take the pixel values i will paint before set them black, and redraw them every time, but this makes my code pretty slow. Is there an easy way to update the screen faster to prevent this?

...

Edited with solution.

As suggested by @Torxed, using win32gui.InvalidateRect solved the updating problem. However, I found that setting only the color of the points I need to be set is cheaper than asking for a rectangle. The first solution renders quite clean, while the second remains a little glitchy. At the end, the code that worked the best for me is:

import win32gui

m=win32gui.GetCursorPos()

dc = win32gui.GetDC(0)

while True:

n=win32gui.GetCursorPos()

win32gui.InvalidateRect(hwnd, (m[0], m[1], GetSystemMetrics(0), GetSystemMetrics(1)), True)

back=[]

for i in range((n[0]-m[0])//4):

win32gui.SetPixel(dc, m[0]+4*i, m[1], 0)

win32gui.SetPixel(dc, m[0]+4*i, n[1], 0)

for i in range((n[1]-m[1])//4):

win32gui.SetPixel(dc, m[0], m[1]+4*i, 0)

win32gui.SetPixel(dc, n[0], m[1]+4*i, 0)

The division and multiplication by four is necessary to avoid flickering, but is visually the same as using DrawFocusRect.

This will only work if you remain bellow and to the right from your initial position, but it is just what I needed. Not difficult to improve it to accept any secondary position.

解决方案

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.

以下是一个使用Python和Pygame库绘制一个固定空心矩形的示例程序,并且确保鼠标能够透过它。 ```python import pygame # 初始化pygame pygame.init() # 设置屏幕尺寸 screen_width, screen_height = 640, 480 screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("Transparent Rectangle") # 设置矩形的位置和尺寸 rect_x, rect_y = 200, 150 rect_width, rect_height = 200, 100 # 定义矩形颜色和透明度 rect_color = (255, 255, 255) # 白色 rect_alpha = 128 # 设置透明度,范围为0-255,0为完全透明,255为完全不透明 # 游戏主循环 running = True while running: # 处理事件 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 填充背景色 screen.fill((0, 0, 0)) # 黑色 # 创建矩形的表面对象 rect_surface = pygame.Surface((rect_width, rect_height), pygame.SRCALPHA) pygame.draw.rect(rect_surface, (rect_color[0], rect_color[1], rect_color[2], rect_alpha), (0, 0, rect_width, rect_height), 3) # 将矩形表面对象绘制屏幕上 screen.blit(rect_surface, (rect_x, rect_y)) # 更新屏幕显示 pygame.display.flip() # 退出程序 pygame.quit() ``` 此程序使用Pygame库来创建一个窗口,并在屏幕绘制一个固定位置和尺寸的空心矩形矩形的颜色和透明度可以通过修改`rect_color`和`rect_alpha`变量来调整。在每次循环迭代中,程序都会检查是否有退出事件,以便在点击关闭按钮时退出程序。矩形的表面对象使用`pygame.Surface()`创建,并通过`pygame.draw.rect()`在表面上绘制矩形轮廓。最后,使用`screen.blit()`将矩形表面对象绘制屏幕上,并通过`pygame.display.flip()`更新屏幕显示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值