python3注冊(cè)全局熱鍵的實(shí)現(xiàn)
之前用python3做游戲自動(dòng)化腳本,用過(guò)很多東西,然后最終有一套完整的方案。在這里隨便闡述一下核心思路:
游戲輔助的窗體設(shè)計(jì)方面:
不需要pyqt這種大型軟件,寫小工具用自帶的tkinter就行了。當(dāng)然,并不是自己純手敲代碼,是通過(guò)拖拽來(lái)實(shí)現(xiàn)的。怎么,你還不知道tkinter可以界面拖拽生成代碼就行VB一樣?
呵呵,PAGE了解一下。
游戲輔助的應(yīng)用發(fā)布方面:
自然是用pyinstaller打包成32位版的exe發(fā)布了,帶上程序圖標(biāo),版本信息,都不是事兒
游戲核心模擬方面:
當(dāng)然不是通過(guò)手敲代碼實(shí)現(xiàn)了,而是通過(guò)調(diào)用目前市場(chǎng)上強(qiáng)大的dll插件了。比如com組件如大漠插件、樂(lè)玩插件。或者說(shuō),把易語(yǔ)言的一些模塊編譯成windll來(lái)調(diào)用也行哦
輔助窗體熱鍵注冊(cè)方面:
這些需要用到底層的東西了,用win32的東西實(shí)現(xiàn)的,可以實(shí)現(xiàn)注冊(cè)全局熱鍵。原理是單獨(dú)一個(gè)線程用于檢測(cè)熱鍵按下,然后熱鍵按下后單獨(dú)開(kāi)辟線程執(zhí)行需要的功能。鑒于原生的太難寫,我自己封裝了并且寫了一個(gè)demo。注冊(cè)全局組合鍵和單獨(dú)的熱鍵都是沒(méi)問(wèn)題的。
前面三個(gè)方面仁者見(jiàn)仁了。后面這個(gè)我就貼個(gè)核心源碼吧,免得以后找不到了。
下面貼一段新的代碼:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # File : 簡(jiǎn)單熱鍵.py # Author: DaShenHan&道長(zhǎng)-----先苦后甜,任憑晚風(fēng)拂柳顏------ # Date : 2020/3/4 import win32con import ctypes import ctypes.wintypes from threading import Thread,activeCount, enumerate from time import sleep,time class Hotkey(Thread): user32 = ctypes.windll.user32 hkey_list = {} hkey_flags = {} #按下 hkey_running = {} #啟停 _reg_list = {} #待注冊(cè)熱鍵信息 def regiskey(self, hwnd=None, flagid=0, fnkey=win32con.MOD_ALT, vkey=win32con.VK_F9): # 注冊(cè)熱鍵,默認(rèn)一個(gè)alt+F9 return self.user32.RegisterHotKey(hwnd, flagid, fnkey, vkey) def get_reginfo(self): return self._reg_list def get_id(self,func): self_id = None for id in self.get_reginfo(): if self.get_reginfo()[id]["func"] == func: self_id = id break if self_id: self.hkey_running[self_id] = True return self_id def get_running_state(self,self_id): if self.hkey_running.get(self_id): return self.hkey_running[self_id] else: return False def reg(self,key,func,args=None): id = int(str(round(time()*10))[-6:]) fnkey = key[0] vkey = key[1] info = { "fnkey":fnkey, "vkey":vkey, "func":func, "args":args } self._reg_list[id] = info # print(info) #這里待注冊(cè)的信息 sleep(0.1) return id def fast_reg(self,id,key = (0,win32con.VK_HOME),func = lambda:print('熱鍵注冊(cè)開(kāi)始')): if not self.regiskey(None, id, key[0], key[1]): print("熱鍵注冊(cè)失敗") return None self.hkey_list[id] = func self.hkey_flags[id] = False return id def callback(self): def inner(self = self): for flag in self.hkey_flags: self.hkey_flags[flag] = False while True: for id, func in self.hkey_list.items(): if self.hkey_flags[id]: args = self._reg_list[id]["args"] if args: # print(args) #這里打印傳入給注冊(cè)函數(shù)的參數(shù) thread_it(func,*args) else: thread_it(func) self.hkey_flags[id] = False return inner def run(self): for id in self._reg_list: reg_info = self._reg_list[id] fnkey = reg_info["fnkey"] vkey = reg_info["vkey"] func = reg_info["func"] self.fast_reg(id,(fnkey, vkey), func) fn = self.callback() thread_it(fn) # 啟動(dòng)監(jiān)聽(tīng)熱鍵按下線程 try: msg = ctypes.wintypes.MSG() while True: if self.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0: if msg.message == win32con.WM_HOTKEY: if msg.wParam in self.hkey_list: self.hkey_flags[msg.wParam] = True self.user32.TranslateMessage(ctypes.byref(msg)) self.user32.DispatchMessageA(ctypes.byref(msg)) finally: for id in self.hkey_list: self.user32.UnregisterHotKey(None, id) def thread_it(func, *args): t = Thread(target=func, args=args) t.setDaemon(True) t.start() def jump(func,hotkey): self_id = hotkey.get_id(func) while hotkey.get_running_state(self_id): print(f"{self_id : } 你正在1秒1次的跳動(dòng)") sleep(1) def stop_jump(start_id,hotkey): hotkey.hkey_running[start_id] = False print(f"{start_id} 即將停止") sleep(1) print(f'當(dāng)前線程列表:{activeCount()}', enumerate()) def main(): hotkey = Hotkey() start_id = hotkey.reg(key = (win32con.MOD_ALT,win32con.VK_HOME),func=jump,args=(jump,hotkey)) #alt home鍵 開(kāi)始 hotkey.reg(key = (0,win32con.VK_END),func=stop_jump,args=(start_id,hotkey)) #alt end鍵 結(jié)束 hotkey.start() #啟動(dòng)熱鍵主線程 print(f"當(dāng)前總線程數(shù)量:{activeCount()}") print('當(dāng)前線程列表:', enumerate()) print('熱鍵注冊(cè)初始化完畢,嘗試按組合鍵alt+Home 或者單鍵END看效果') if __name__ == '__main__': main()
以下是舊的代碼,用起來(lái)比較麻煩。
#!/usr/bin/env python3 # _*_ coding: utf-8 _*_ # File : demo.py # Author: DaShenHan&道長(zhǎng)-----先苦后甜,任憑晚風(fēng)拂柳顏------ # Date : 2019/6/28 import win32con import ctypes import ctypes.wintypes from threading import Thread, Timer, activeCount, enumerate from time import sleep h_ids = [i for i in range(2)] # 創(chuàng)建兩個(gè)熱鍵序列 h_keys = {i: False for i in h_ids} # 初始化所有熱鍵序列的標(biāo)志符為False h_dict = {} # 初始化一個(gè)空的字典,記錄id與func class Hotkey(Thread): # 創(chuàng)建一個(gè)Thread的擴(kuò)展類 user32 = ctypes.windll.user32 # 加載user32.dll # global h_ids, h_keys,h_dict def regiskey(self, hwnd=None, flagid=0, fnkey=win32con.MOD_ALT, vkey=win32con.VK_F9): # 注冊(cè)熱鍵,默認(rèn)一個(gè)alt+F9 return self.user32.RegisterHotKey(hwnd, flagid, fnkey, vkey) def callback(self, id, func): h_dict[id] = func # 這個(gè)id對(duì)應(yīng)這個(gè)func,沒(méi)有就是新增,有就是修改 def inner(): for key, value in h_dict.items(): print(f'總的熱鍵池:{h_ids},當(dāng)前熱鍵序號(hào):{key}, 當(dāng)前熱鍵功能:{value},當(dāng)前熱鍵狀態(tài):{h_keys[h_ids[key]]}') while True: for key, value in h_dict.items(): if h_keys[h_ids[key]]: thread_it(value) # 另外開(kāi)線程執(zhí)行value h_keys[h_ids[key]] = False return inner def run(self): # print(self.user32) if not self.regiskey(None,h_ids[0],win32con.MOD_ALT,win32con.VK_F9): # 注冊(cè)快捷鍵alt+F9并判斷是否成功,該熱鍵用于執(zhí)行一次需要執(zhí)行的內(nèi)容。 print(f"熱鍵注冊(cè)失??! id{h_ids[0]}") # 返回一個(gè)錯(cuò)誤信息 if not self.regiskey(None,h_ids[1],0,win32con.VK_F10): # 注冊(cè)快捷鍵F10并判斷是否成功,該熱鍵用于結(jié)束程序,且最好這么結(jié)束,否則影響下一次注冊(cè)熱鍵。 print(f"熱鍵注冊(cè)失?。?id{h_ids[1]}") # 以下為檢測(cè)熱鍵是否被按下,并在最后釋放快捷鍵 try: msg = ctypes.wintypes.MSG() while True: if self.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0: if msg.message == win32con.WM_HOTKEY: if msg.wParam in h_ids: h_keys[msg.wParam] = True self.user32.TranslateMessage(ctypes.byref(msg)) self.user32.DispatchMessageA(ctypes.byref(msg)) finally: for i in h_ids: self.user32.UnregisterHotKey(None, i) # 必須得釋放熱鍵,否則下次就會(huì)注冊(cè)失敗,所以當(dāng)程序異常退出,沒(méi)有釋放熱鍵, # 那么下次很可能就沒(méi)辦法注冊(cè)成功了,這時(shí)可以換一個(gè)熱鍵測(cè)試 def thread_it(func, *args): t = Thread(target=func, args=args) t.setDaemon(True) t.start() def settimeout(func, sec): def inner(): func() Timer(sec, inner).start() thread_it(inner) def setinterval(func, sec, tmrname, flag=True): global timer_dict timer_dict[tmrname] = flag print("已設(shè)置tqtimer啟用狀態(tài)為:{}".format(flag)) def inner(): global timer_dict if timer_dict[tmrname]: func() Timer(sec, inner).start() thread_it(inner) def clearinterval(timername): global timer_dict timer_dict[timername] = False flag = timer_dict[timername] print("已設(shè)置tqtimer啟用狀態(tài)為:{}".format(flag)) def test_start(): print("按下了開(kāi)始鍵...the programe is running") def test_stop(): print("按下了停止鍵...the programe is stopped") def run_ok(): hotkey = Hotkey() hotkey.start() fn = hotkey.callback(0, test_start) fn = hotkey.callback(1, test_stop) thread_it(fn) sleep(0.5) count = activeCount() print(f"當(dāng)前總線程數(shù)量:{count}") print('當(dāng)前線程列表:', enumerate()) print('熱鍵注冊(cè)初始化完畢,嘗試按組合鍵alt+F9 或者單鍵F10看效果') while True: pass if __name__ == '__main__': run_ok()
這里是沒(méi)弄界面的源碼,所以我就把主線程死循環(huán)阻塞了。運(yùn)行后按alt+F9會(huì)打印按下了開(kāi)始鍵,按F10會(huì)打印按下了停止鍵。
如果你在tkinter里面跑,直接把run_ok函數(shù)后面的while True:pass刪掉,然后在init函數(shù)里面加入run_ok()就行了。這里指的用PAGE設(shè)計(jì)的tkinter程序哈!
那么窗體創(chuàng)建完畢就會(huì)自動(dòng)阻塞主線程,其他監(jiān)控?zé)徭I的線程隨主線程結(jié)束。啟動(dòng)期間獨(dú)立運(yùn)行互不干擾。
到此這篇關(guān)于python3注冊(cè)全局熱鍵的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)python3 注冊(cè)全局熱鍵內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python實(shí)現(xiàn)將一個(gè)大文件按段落分隔為多個(gè)小文件的簡(jiǎn)單操作方法
這篇文章主要介紹了Python實(shí)現(xiàn)將一個(gè)大文件按段落分隔為多個(gè)小文件的簡(jiǎn)單操作方法,涉及Python針對(duì)文件的讀取、遍歷、轉(zhuǎn)換、寫入等相關(guān)操作技巧,需要的朋友可以參考下2017-04-04

Python實(shí)現(xiàn)提取語(yǔ)句中的人名

Pytorch BCELoss和BCEWithLogitsLoss的使用

Pytest初學(xué)者快速上手高效Python測(cè)試指南