• Как нажимать клавиши клавиатуры в зависимости от происходящего на мониторе?

    @Perforator
    Дмитрий, параметров он хочет
    PostMessage( [Handler], [Действие] , [Параметр/Клавиша], [lparam - некое магическое число при использовании особых комбинаций клавиш])
  • Как нажимать клавиши клавиатуры в зависимости от происходящего на мониторе?

    @Perforator
    Ну там же всё есть, просто модифицируй функцию PostKeyEx и всё.
    Например, вот так:
    код PostKeyEx
    PBYTE256 = ctypes.c_ubyte * 256
    _user32 = ctypes.WinDLL("user32")
    GetKeyboardState = _user32.GetKeyboardState
    SetKeyboardState = _user32.SetKeyboardState
    PostMessage = win32api.PostMessage
    SendMessage = win32gui.SendMessage
    FindWindow = win32gui.FindWindow
    IsWindow = win32gui.IsWindow
    GetCurrentThreadId = win32api.GetCurrentThreadId
    GetWindowThreadProcessId = _user32.GetWindowThreadProcessId
    AttachThreadInput = _user32.AttachThreadInput
    
    MapVirtualKeyA = _user32.MapVirtualKeyA
    MapVirtualKeyW = _user32.MapVirtualKeyW
    
    MakeLong = win32api.MAKELONG
    
    def PostKeyEx( game_Handler,keydown,key, shift, specialkey):
    	# game_Handler - хэндлер окна с игрой
    	# keydown - true/false - нажать клавишу/отпустить клавишу
    	# key - Клавиша ord('A') для "A", win32con.VK_SPACE  для пробелла, все константы можно найти тут: https://github.com/SublimeText/Pywin32/blob/master/lib/x32/win32/lib/win32con.py
    	# shift - массив модификаторов (alt/shift/ctrl, etc.) например, [win32con.VK_CONTROL] или пустой массив.
    	# specialkey - true/false - true если используются специальные клавиши
    	if IsWindow(game_Handler):
    	
    	ThreadId = GetWindowThreadProcessId(game_Handler, None)
    	
    	lparam = MakeLong(0, MapVirtualKeyA(key, 0))
    	if keydown:
    		msg_down=win32con.WM_KEYDOWN
    	else:
    		msg_down=win32con.WM_KEYUP
    	
    	if specialkey:
    		lparam = lparam | 0x1000000
    
    	if len(shift) > 0:
    		pKeyBuffers = PBYTE256()
    		pKeyBuffers_old = PBYTE256()
    
    		#ActivateWindow() #Раскоментировать для активации окна
    		AttachThreadInput(GetCurrentThreadId(), ThreadId, True)
    		GetKeyboardState( ctypes.byref(pKeyBuffers_old ))
    
    		
    		for modkey in shift:
    		if modkey == win32con.VK_MENU:
    			lparam = lparam | 0x20000000
    			if keydown:
    				msg_down=win32con.WM_SYSKEYDOWN
    			else:
    				msg_down=win32con.WM_SYSKEYUP
    		pKeyBuffers[modkey] |= 128
    
    		SetKeyboardState( ctypes.byref(pKeyBuffers) )
    		time.sleep(0.01)
    		PostMessage( game_Handler, msg_down, key, lparam)
    		time.sleep(0.01)
    		SetKeyboardState( ctypes.byref(pKeyBuffers_old) )
    		time.sleep(0.01)
    		AttachThreadInput(GetCurrentThreadId(), ThreadId, False)
    		#DeActivateWindow() #Раскоментировать для деактивации окна
    	else:
    		if keydown:
    			SendMessage( game_Handler, msg_down, key, lparam)
    		else:
    			SendMessage( game_Handler, msg_down, key, lparam | 0xC0000000)
    	else:
    	print ('no handle')
    
    PostKeyEx( game_Handler,true,win32con.VK_SPACE, [],false) #Нажать клавишу пробелл
    
    PostKeyEx( game_Handler,false,win32con.VK_SPACE, [],false) #Отпустить клавишу пробелл
  • Как нажимать клавиши клавиатуры в зависимости от происходящего на мониторе?

    @Perforator
    Да там почти всё из win32* используется, оно всё связано.
    +ctypes для создания правильных типов и использования виндовых dll.
    там что бы заработало это всё нужно.
    Оно описано в import
  • Как нажимать клавиши клавиатуры в зависимости от происходящего на мониторе?

    @Perforator
    Да в таком сценарии работать будет.
    То, что я выкладывал - это всего-лишь вспомогательные функции для нажатия клавиш итп.

    mouse:
    mouse_move() - двигает мышку
    MousePress() - зажимает кнопку
    MouseRelease() - отпускает кнопку
    MouseClick() - кликает в определённую область окна
    MouseDrag() - перетаскивает (в одном месте зажать кнопку, двинуть мышку до другой координаты, отпустить конпку)
    Rclick() - клик правой кнопкой мыши в определённом месте
    Lclick() - клик левой кнопкой мыши в определённом месте
    Dblclick() - двойной клик мышкой в определённом месте
    Mhover() - наводит мышку на определённые координаты
    away() - уводит мышку за пределы окна
    click_away() - уводит мышку за пределы окна и кликает
    get_mouse_pos() - возвращает координаты мышки
    return_mouse() - двигает мышь по ранее записанным координатам

    utils:
    random_sleep() - Затормаживает выполнение скрипта на случайное значение из заданного диапазона
    ClickPos() - выбирает координаты (точку) для клика по области (грубо говоря, в любую точку заданного прямоугольника)
    ActivateWindow() - активирует окно на время
    DeActivateWindow() - деактивирует окно, но я не использовал.
    text_type() - пишет текст (эмулирует нажатия клавиш на клавиатуре)
    PostKeyEx() - отправляет комбинацию клавиш в окно, например, CTRL+A = PostKeyEx(ord('A'),[w.VK_CONTROL],False)
    combo_key() - вспомогательная обёртка для PostKeyEx()

    Window:
    gethandler() - по заданному названию окна берёт handler этого окна
    getwindow() - Ищет окно по заданному Title
    getscreen() - Возвращает RGB массив с изображением окна

    Все данные сохранялись в глобальный объект params (его не привожу, т.к. там куча логики и собирался он полуавтоматически).
    Достаточно создать такой объект и по на создавать в нём структуру используемую в функциях, ну либо заменить их параметрами.
    Я очень много всего хранил в глобальных переменных что бы просто не передавать кучу параметров.
  • Как нажимать клавиши клавиатуры в зависимости от происходящего на мониторе?

    @Perforator
    Собственно, cv2 там был для распознавания изображения и функций распознавания текста.
    С его помощью хорошо получалось находить окна и известные иконки, плохо получалось распознавать текст даже с учётом того что оригинальный шрифт есть.
    Если есть наработки по распознаванию текста (особенно если распознавание возможно с применением шрифта) - хотелось бы увидеть (tesseract не предлагать, там процент ошибок больше чем если просто по изображениям искать)
  • Как нажимать клавиши клавиатуры в зависимости от происходящего на мониторе?

    @Perforator
    Ещё раз: Нажатия и захват изображения на свёрнутых окнах работать не будет. Но можно окно увести вбок за край экрана что бы не мешало и так работать будет. Окно при этом даже не обязательно должно быть активно.

    Весь этот код что я выложил работает на 3.5 питоне. Все библиотеки есть для этого.

    cv2, кажется, качал с оффсайта...
  • Как нажимать клавиши клавиатуры в зависимости от происходящего на мониторе?

    @Perforator
    В вк и других соцсетях меня нет.
    По поводу мультипроцессовости - это я делал очень давно ещё на python2 и ни одно решение на этом языке мне тогда не понравилось. Потому я сделал так:
    Были отдельно скрипты нажимающие на клавиши, скрипты сбора и обработки визуальной информации, управляемые через socket и отдельный управляющий скрипт с GUI на PyQT. Так я мог управлять сразу несколькими окнами.
    Состояния клиентов хранились в массиве на управляющем скрипте, там же была логика действий.
    В итоге на одно окно игры получалось 2 процесса + 1 общий управляющий процесс.
    Всё общалось через TCP/IP.
  • Как нажимать клавиши клавиатуры в зависимости от происходящего на мониторе?

    @Perforator
    Я просто вывожу через print(text).
    Вообще, я когда-то писал бота и все эти сложности проходил.
    С фоновым режимом - можно отправлять клавиши и просматривать неактивное окно, но там 2 условия:
    1) Должна быть установлена тема Windows Aero (Windows 7 PRO и выше)
    2) Окно с игрой не должно быть свёрнуто (оно может прятаться под другими окнами, таких окон может быть несколько, но сворачивать их нельзя)

    Вот тебе часть вспомогательных функций для работы с кнопками, мышкой и окном:
    spoiler
    import os
    import sys
    import win32ui, win32gui, win32con, win32api, win32com.client, win32file, win32event
    
    import cv2
    import numpy as np
    import os.path
    
    import ctypes
    import time
    import random
    import sympy
    import subprocess
    import re
    import socket
    import json
    
    
    from PIL import Image, ImageFont, ImageDraw, ImageTk
    
    
    
    game_Name = "Some Game Screen" #Название окна с игрой
    game_Handler = False
    gScreen = False
    
    
    #---------------------------------
    """		 win32api		   """
    
    PBYTE256 = ctypes.c_ubyte * 256
    _user32 = ctypes.WinDLL("user32")
    GetKeyboardState = _user32.GetKeyboardState
    SetKeyboardState = _user32.SetKeyboardState
    PostMessage = win32api.PostMessage
    SendMessage = win32gui.SendMessage
    FindWindow = win32gui.FindWindow
    IsWindow = win32gui.IsWindow
    GetCurrentThreadId = win32api.GetCurrentThreadId
    GetWindowThreadProcessId = _user32.GetWindowThreadProcessId
    AttachThreadInput = _user32.AttachThreadInput
    
    MapVirtualKeyA = _user32.MapVirtualKeyA
    MapVirtualKeyW = _user32.MapVirtualKeyW
    
    MakeLong = win32api.MAKELONG
    
    """		/win32api		   """
    #---------------------------------
    
    #-------------------------------
    """		 mouse		   """
    
    mouse={
    	'left':{
    		'msg':{
    			'down':win32con.WM_LBUTTONDOWN,
    			'up':win32con.WM_LBUTTONUP
    		},
    		'wparam':{
    			'down':win32con.MK_LBUTTON,
    			'up':win32con.MK_LBUTTON
    		},
    		'sleep':(0.05,0.25)
    	},
    	'right':{
    		'msg':{
    			'down':win32con.WM_RBUTTONDOWN,
    			'up':win32con.WM_RBUTTONUP
    		},
    		'wparam':{
    			'down':win32con.MK_RBUTTON,
    			'up':win32con.MK_RBUTTON
    		},
    		'sleep':(0.55,0.95)
    	},
    	'middle':{
    		'msg':{
    			'down':win32con.WM_MBUTTONDOWN,
    			'up':win32con.WM_MBUTTONUP
    		},
    		'wparam':{
    			'down':win32con.MK_MBUTTON,
    			'up':win32con.MK_MBUTTON
    		},
    		'sleep':(0.05,0.25)
    	},
    	'move':{
    		'sleep':(0.05,0.25)
    	}
    }
    
    def mouse_move(pos, get_global=True):
    
    	if IsWindow(game_Handler):
    		(x,y) = pos
    		lParam = (y << 16) | x 
    		SendMessage(game_Handler, win32con.WM_MOUSEMOVE, 0, lParam)
    	
    	else:
    		print('no handle')
    		params['iterate']=False
    
    def MouseTmp(pos, move=False):
    	if IsWindow(game_Handler):
    		client_pos = win32gui.ScreenToClient(game_Handler, pos)
    		tmp = MakeLong(client_pos[0], client_pos[1])
    		if move:
    			mouse_move(pos)
    			random_sleep(mouse['move']['sleep'])
    		return tmp
    	else:
    		print('no handle')
    		params['iterate']=False
    
    def MousePress(pos, button, move=False):
    	if IsWindow(game_Handler):
    		tmp=MouseTmp(pos, move)
    		SendMessage(game_Handler, mouse[button]['msg']['down'], mouse[button]['wparam']['down'], tmp)
    	else:
    		print ('no handle')
    		params['iterate']=False
    
    def MouseRelease(pos, button, move=False):
    	if IsWindow(game_Handler):
    		tmp=MouseTmp(pos, move)
    		SendMessage(game_Handler, mouse[button]['msg']['up'], mouse[button]['wparam']['up'], tmp)
    	else:
    		print ('no handle')
    		params['iterate']=False
    
    def MouseClick(pos, button, move=False,t=0):
    	win32gui.EnableWindow(game_Handler,0)
    	MousePress(pos, button, False)
    	random_sleep(mouse[button]['sleep'],t)
    	MouseRelease(pos, button, False)
    	win32gui.EnableWindow(game_Handler,1)
    
    def MouseDrag(pos1, pos2, button,t=0):
    	win32gui.EnableWindow(game_Handler,0)
    	MousePress(pos1, button, True)
    	random_sleep(mouse[button]['sleep'],t)
    	MouseRelease(pos2, button, True)
    	win32gui.EnableWindow(game_Handler,1)
    
    def Rclick(pos,t=0):
    	pos=ClickPos(pos)
    	win32gui.EnableWindow(game_Handler,0)
    	mouse_move(pos)
    	random_sleep(mouse['move']['sleep'],t)
    	MouseClick(pos,'right',False)
    	win32gui.EnableWindow(game_Handler,1)
    	random_sleep(mouse['right']['sleep'],t)
    	getscreen()
    	return pos
    
    def Lclick(pos,t=0.):
    	pos=ClickPos(pos)
    	win32gui.EnableWindow(game_Handler,0)
    	mouse_move(pos)
    	random_sleep(mouse['move']['sleep'],t)
    	MouseClick(pos,'left',False)
    	win32gui.EnableWindow(game_Handler,1)
    	random_sleep(mouse['left']['sleep'],t)
    	getscreen()
    	return pos
    
    def Dblclick(pos,t=0.):
    	pos=ClickPos(pos)
    	win32gui.EnableWindow(game_Handler,0)
    	mouse_move(pos)
    	random_sleep(mouse['move']['sleep'],t)
    	fastmove=params['gloabl']['fast_move']==False
    	params['gloabl']['fast_move']==True
    	MouseClick(pos,'left',False)
    	MouseClick(pos,'left',False)
    	params['gloabl']['fast_move']==fastmove
    	win32gui.EnableWindow(game_Handler,1)
    	random_sleep(mouse['left']['sleep'],t)
    	getscreen()
    	return pos
    
    def Mhover(pos,t=.3):
    	pos=ClickPos(pos)
    	win32gui.EnableWindow(game_Handler,0)
    	mouse_move(pos)
    	win32gui.EnableWindow(game_Handler,1)
    	random_sleep(mouse['move']['sleep'],t)
    	getscreen()
    
    def away(t=0.25):
    	pos=ClickPos((-params['away_point_delta'],-params['away_point_delta'],params['away_point_delta']-1,params['away_point_delta']-1))
    	win32gui.EnableWindow(game_Handler,0)
    	mouse_move(pos)
    	win32gui.EnableWindow(game_Handler,1)
    	if params['gloabl']['fast_move']==False:
    		time.sleep(t)
    
    def click_away(t=1): #Передаёт в окно клик за пределы окна
    	win32gui.EnableWindow(game_Handler,0)
    	self.Lclick((-params['away_point_delta'],-params['away_point_delta'],params['away_point_delta']-1,params['away_point_delta']-1))
    	win32gui.EnableWindow(game_Handler,1)
    	if params['gloabl']['fast_move']==False:
    		time.sleep(t)
    
    def get_mouse_pos():
    	print ("get cursor pos")
    	params['gloabl']['fast_move']=True
    	params['gloabl']['cursor_pos'] = win32api.GetCursorPos()
    
    	#flags, hcursor, params['gloabl']['cursor_pos'] = win32gui.GetCursorInfo()
    
    def return_mouse():
    	print ("set cursor pos")
    	params['gloabl']['fast_move']=False
    	win32api.SetCursorPos(params['gloabl']['cursor_pos'])
    
    
    """		 /mouse		   """
    #-------------------------------
    
    
    #-------------------------------
    """		 utils		   """	
    
    def random_sleep(time_interval,t=0):
    	if params['gloabl']['fast_move']==False:
    		(t_min,t_max)=time_interval
    		secs=(float(random.randrange(int(t_min*100),int(t_max*100),1))/100)+float(t)
    		time.sleep(secs)
    	else:
    		time.sleep(0.01)
    
    def ClickPos(pos):
    	(x,y,w,h)=pos
    	return (random.randint(x,w+x),random.randint(y,h+y))
    
    
    def ActivateWindow():
    	if IsWindow(game_Handler):
    		SendMessage(game_Handler, win32con.WM_ACTIVATE, win32con.WA_ACTIVE, 0)
    
    def DeActivateWindow():
    	#if IsWindow(self.w['handle']):
    	#	SendMessage(self.w['handle'], win32con.WM_ACTIVATE, win32con.WA_INACTIVE, 0)	
    	print ("deactivate_window")
    	
    	
    def text_type( text):
    	if IsWindow(game_Handler):
    		for char in text:
    			ActivateWindow()
    			SendMessage( game_Handler, win32con.WM_CHAR, ord(char), 0)
    			DeActivateWindow()
    			time.sleep(0.05)
    	else:
    		print ('no handle')
    		params['iterate']=False
    	
    
    def PostKeyEx( key, shift, specialkey):
    	if IsWindow(game_Handler):
    	
    		ThreadId = GetWindowThreadProcessId(game_Handler, None)
    	
    		lparam = MakeLong(0, MapVirtualKeyA(key, 0))
    		msg_down=win32con.WM_KEYDOWN
    		msg_up=win32con.WM_KEYUP
    	
    		if specialkey:
    			lparam = lparam | 0x1000000
    
    		if len(shift) > 0:
    			pKeyBuffers = PBYTE256()
    			pKeyBuffers_old = PBYTE256()
    
    			ActivateWindow()
    			AttachThreadInput(GetCurrentThreadId(), ThreadId, True)
    			GetKeyboardState( ctypes.byref(pKeyBuffers_old ))
    
    			for modkey in shift:
    				if modkey == win32con.VK_MENU:
    					lparam = lparam | 0x20000000
    					msg_down=win32con.WM_SYSKEYDOWN
    					msg_up=win32con.WM_SYSKEYUP
    				pKeyBuffers[modkey] |= 128
    
    			SetKeyboardState( ctypes.byref(pKeyBuffers) )
    			time.sleep(0.01)
    			PostMessage( game_Handler, msg_down, key, lparam)
    			time.sleep(0.01)
    			PostMessage( game_Handler, msg_up, key, lparam | 0xC0000000)
    			time.sleep(0.01)
    			SetKeyboardState( ctypes.byref(pKeyBuffers_old) )
    			time.sleep(0.01)
    			AttachThreadInput(GetCurrentThreadId(), ThreadId, False)
    			DeActivateWindow()	
    		else:
    			SendMessage( game_Handler, msg_down, key, lparam)
    			SendMessage( game_Handler, msg_up, key, lparam | 0xC0000000)
    	else:
    		print ('no handle')
    		params['iterate']=False
    	
    def combo_key(combo):
    	(key, shift, special)=combo
    	PostKeyEx(key, shift, special)
    
    """		 /utils		   """
    #-------------------------------
    
    
    """		 Window		   """
    #-------------------------------
    
    def gethandler():
    	global game_Handler
    	game_Handler=getwindow(game_Name) #def getwindow
    
    def getwindow(game_Name):
    	toplist, winlist = [], []
    	def enum_cb(hwnd, results):
    		if win32gui.GetWindowText(hwnd) == game_Name :
    			winlist.append(hwnd)
    	win32gui.EnumWindows(enum_cb, toplist)
    	game_Handler=winlist[0]
    	return game_Handler
    
    def getscreen():
    
    	global gScreen
    	if game_Handler==False:
    		gethandler()
    
    	(game_x,game_y,game_x2,game_y2) = win32gui.GetWindowRect(game_Handler)
    	game_w=game_x2-game_x
    	game_h=game_y2-game_y
    	game_DC = win32gui.GetWindowDC(game_Handler)
    	mfcDC  = win32ui.CreateDCFromHandle(game_DC)
    	saveDC = mfcDC.CreateCompatibleDC()
    	saveBitMap = win32ui.CreateBitmap()
    	saveBitMap.CreateCompatibleBitmap(mfcDC, game_w, game_h)
    	saveDC.SelectObject(saveBitMap)
    	saveDC.BitBlt(( 0, 0 ), (game_w, game_h),  mfcDC,  (game_x, game_y),  win32con.SRCCOPY)
    	im = saveBitMap.GetBitmapBits(False)
    	img = np.array(im).astype(dtype="uint8")
    	img.shape = (game_h,game_w,4)
    	cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
    
    	mfcDC.DeleteDC()
    	saveDC.DeleteDC()
    	win32gui.ReleaseDC(game_Handler, game_DC)
    	win32gui.DeleteObject(saveBitMap.GetHandle())
    	gScreen=img
    	return img
    
    """		 /Window		   """
    #-------------------------------
    
    def main():
    	screen=getscreen() #В переменной будет изображение, можно его сохранить через cv2.imwrite
  • Как нажимать клавиши клавиатуры в зависимости от происходящего на мониторе?

    @Perforator
    Там дофига всего надо.
    Для начала win32api для поиска и работы с окнами а так же для передачи нажатий клавиш и эмуляции мышки.
    Затем, cv2 (это OpenCV) для функций OCR, поиска изменений в скриншоте окна и выдачи соответствующей реакции.

    Для передачи нажатий клавиш и движений мышки используй win32gui.SendMessage
    Для передачи нажатий клавиш с модификаторами (alt/ctrl/shift) используй win32api.PostMessage
  • Как осуществить передачу комбинации клавиш (CTRL+A, etc.) неактивному окну?

    @Perforator Автор вопроса
    Забавно, в своё время именно по этой причине отказался от pywinauto...
    Рад что проект живёт и развивается!
    Всех благ!