python - 在 Windows 10 中使用 python 来启用鼠标环绕
问题描述
我正在尝试使用自动光标定位与 pyWinhook 的某种组合在多屏桌面设置上实现光标环绕。最终,当我点击屏幕的最右侧或最左侧边缘时,我希望将代码的功能扩展到不同的系统,但现在我通过尝试使其在单个上起作用而忽略了网络位系统。
最初,我使用 pyautogui 将这个简单的脚本放在一起进行包装:
import pyautogui
MAX_X, MAX_Y = pyautogui.size()
MAX_X -= 1
MAX_Y -= 1
def detect_wrap():
x, y = pyautogui.position()
if x >= MAX_X:
pyautogui.moveTo(0,y)
elif x <= 0:
pyautogui.moveTo(MAX_X,y)
elif y >= MAX_Y:
pyautogui.moveTo(x, 0)
elif y <= 0:
pyautogui.moveTo(x, MAX_Y)
return
print(pyautogui.size())
while True:
detect_wrap()
哪个工作正常,但最后有一个烦人的无限循环,所以我尝试寻找一种更干净地处理它的方法,偶然发现 pyWinhook 和 pythoncom 并编写了一个类似的脚本来尝试实现同样的事情。但是,当我这样做时,不是从最底部跳到顶部,从最右边跳到左边等等。脚本卡在边缘并停留在那里。虽然我不确定为什么。我放入了一些打印语句以试图揭示一些信息,并看到鼠标确实移动到了对面的边界,但是有些东西让它一直返回。我深入研究 pyautogui 并弄清楚它是如何移动光标的(使用 ctypes.windll.user32.SetCursorPos(xpos, ypos)),所以我将 pyautogui 排除在外。我剩下的看起来像这样(请注意,这是一个正在进行的工作):
import win32api
import pyWinhook as pyHook
import pythoncom
import ctypes
import win32api, win32con
import win32.win32gui as win32gui
import os, sys, time
SCREEN_WIDTH = 0 # This will actually be set later, when we know the size of the attached screen(s)
SCREEN_HEIGHT = 0 # This will actually be set later, when we know the size of the attached screen(s)
XMAX = 0 # This will actually be set later, when we know the size of the attached screen(s)
YMAX = 0 # This will actually be set later, when we know the size of the attached screen(s)
XMIN = 1
YMIN = 1
def setWrapPos(xpos, ypos, border):
win32gui.ReleaseCapture()
if border == 'left':
xpos = SCREEN_WIDTH - 1
elif border == 'top':
ypos = SCREEN_HEIGHT - 1
elif border == 'right':
xpos = XMIN
elif border == 'bottom':
ypos = YMIN
else:
print('ERROR: Illegal border passed to setWrapPos()')
ctypes.windll.user32.SetCursorPos(xpos, ypos)
time.sleep(0.01)
return
def onclick(event):
print('Detected mouseclick at (%d,%d)' % event.Position)
return True
def trackMouse(event):
hm.UnhookMouse()
flags, hcursor, coords = win32gui.GetCursorInfo()
xpos, ypos = coords
print("Mouse at (%d, %d)" % (xpos, ypos))
if (xpos <= XMIN):
setWrapPos(xpos, ypos, 'left')
elif (ypos <= YMIN):
setWrapPos(xpos, ypos, 'top')
elif (xpos >= XMAX):
setWrapPos(xpos, ypos, 'right')
elif (ypos >= YMAX):
setWrapPos(xpos, ypos, 'bottom')
flags, hcursor, coords = win32gui.GetCursorInfo()
xpos, ypos = coords
print("Mouse moved to (%d, %d)" % (xpos, ypos))
hm.HookMouse()
return True
def onWinCombo(event):
if event.Key == 'X':
print('Lcontrol-X was detected. Exiting.')
hm.UnhookMouse()
hm.UnhookKeyboard()
os._exit(0)
else:
hm.KeyDown = onKeyboardEvent
return False
def noMoreCombo(event):
hm.KeyDown = onKeyboardEvent
hm.KeyUp = None
return False
def onKeyboardEvent(event):
print('Keyboard action detected, ' + str(event.Key) + ' was pressed.')
if str(event.Key) == 'Lcontrol':
print('Lcontrol detected.')
hm.KeyDown = onWinCombo
hm.KeyUp = noMoreCombo
return True
curr_right = 0
mons = win32api.EnumDisplayMonitors()
i = 0
while i < win32api.GetSystemMetrics(win32con.SM_CMONITORS):
minfo = win32api.GetMonitorInfo(mons[i][0])
if minfo['Monitor'][2] > SCREEN_HEIGHT:
SCREEN_HEIGHT = minfo['Monitor'][3]
print("Monitor #" + str(i+1) + ": " + str(minfo['Monitor'][2] - curr_right) + "x" + str(minfo['Monitor'][3]))
i += 1
curr_right += minfo['Monitor'][2]
minfo = win32api.GetMonitorInfo(mons[i-1][0])
SCREEN_WIDTH = minfo['Monitor'][2]
XMAX = SCREEN_WIDTH - 1
YMAX = SCREEN_HEIGHT - 1
hm = pyHook.HookManager()
hm.SubscribeMouseAllButtonsDown(onclick)
hm.SubscribeMouseMove(trackMouse)
hm.KeyDown = onKeyboardEvent
hm.HookMouse()
hm.HookKeyboard()
pythoncom.PumpMessages()
该代码有效,但无论我尝试了什么,我似乎都无法避免让光标弹回边界而不是跳到另一边。我的想法已经用完了,所以想也许这里有人会对正在发生的事情有所了解。
提前感谢您的任何见解。
解决方案
更新:我创建了一个名为 wrap.py 的工作鼠标环绕 python 脚本,并将结果放在 GitHub 的https://github.com/XyzzyBob/wrap上。我也在Win10下用pyinstaller编译了它,但无法上传文件,因为我无法从代码所在的位置传输文件。
如 github 页面所述,python 脚本需要一堆库,并且高度仅适用于 Windows。如果我发现需要的话,我可能会在某个时候为 Linux 和 Mac 重做它,但现在 Windows 是我使用它的地方。
仍在开发用于多系统控制的网络版本,但这是一个更大的程序。这方面的进展可能会显示为 github 页面上的一个分支。
======================== xxxxxxxxxxxxxxxxxx ======================== ==
找到了一种方法来使用不同的库来实现我想要做的事情。鼠标库显然处理低级挂钩,并具有获取和设置鼠标状态项(如位置)的例程。解决方案基本上是这样的:
import mouse
...
def mouseEvent(event):
if isinstance(event, mouse._mouse_event.MoveEvent):
trackMouse(event)
elif isinstance(event, mouse._mouse_event.ButtonEvent):
onclick(event)
else:
print(event)
return True
...
def trackMouse(event):
xpos = event.x
ypos = event.y
print("Mouse at (%d, %d)" % (xpos, ypos))
if (xpos <= XMIN):
setWrapPos(xpos, ypos, 'left')
elif (ypos <= YMIN):
setWrapPos(xpos, ypos, 'top')
elif (xpos >= XMAX):
setWrapPos(xpos, ypos, 'right')
elif (ypos >= YMAX):
setWrapPos(xpos, ypos, 'bottom')
return True
...
def onclick(event):
print(event.event_type + ' for ' + event.button + ' button at ' + str(mouse.get_position()))
return True
### Main ###
hm = mouse.hook(mouseEvent)
pythoncom.PumpMessages()
现在只需添加键盘挂钩和其余部分即可使其成为一个连贯的包。
推荐阅读
- sql-server - 在 T-SQL 中对字符范围使用通配符
- firebase - 将数据从 Firestore 拉入 Flutter 配置文件页面
- python - PyQt5:程序崩溃并且不显示更多错误信息?
- java - 如何在数组中安排 Firebase 检索?
- html - 使用 XSLT 将 XML 拆分为多个 HTML 页面?
- azure - 如何检查 Azure Sql Server 服务器主体(登录)是否已存在
- r - 数字四舍五入的功能
- spring - 使用 OAuth2 (keycloak) 保护的 Jhipster/Spring Security 不加载角色
- javascript - 如何通过 AJAX 将对象列表发布到 Spring 控制器?
- assembly - 为什么 mov ax, [[num] + val] 与将其分解为指令不同