如何使用Python在屏幕上绘制空白矩形 [英] How to draw an empty rectangle on screen with Python

查看:1110
本文介绍了如何使用Python在屏幕上绘制空白矩形的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不是专家,我试图在屏幕上显示一个矩形,该矩形跟随鼠标从稳定的起点移动,就像您选择文字或绘画中的某些内容一样。我带有以下代码:

  import win32gui 
m = win32gui.GetCursorPos()
而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)

如您所见,代码将绘制矩形,但之前的矩形将保留直到屏幕更新。



我自带的唯一解决方案是在将像素值设置为黑色之前,先绘制要绘制的像素值,然后每次重新绘制它们,但这会使我的代码非常慢。有没有简便的方法可以更快地更新屏幕以防止这种情况发生?



...



使用解决方案进行了编辑



如@Torxed所建议,使用win32gui.InvalidateRect解决了更新问题。但是,我发现仅设置需要设置的点的颜色比要求矩形便宜。第一个解决方案呈现得很干净,而第二个解决方案仍然有些毛刺。最后,最适合我的代码是:

  import win32gui 

m = win32gui .GetCursorPos()
dc = win32gui.GetDC(0)

而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)i在范围((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)

除法和乘以四是避免闪烁所必需的,但在视觉上与使用DrawFocusRect相同。



这仅在您保持波纹管且从初始位置向右移动时才有效,但这正是我所需要的。改善它以接受任何次要职位并不难。

解决方案

为了刷新旧的绘制区域,您需要调用标签为 c ++ 赢得 winapi 。希望这可以使一些人在找到好的榜样之前不必进行搜索。


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在屏幕上绘制空白矩形的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆