python3注册全局热键的实现


Posted in Python onMarch 22, 2020

之前用python3做游戏自动化脚本,用过很多东西,然后最终有一套完整的方案。在这里随便阐述一下核心思路:

游戏辅助的窗体设计方面:

不需要pyqt这种大型软件,写小工具用自带的tkinter就行了。当然,并不是自己纯手敲代码,是通过拖拽来实现的。怎么,你还不知道tkinter可以界面拖拽生成代码就行VB一样?

呵呵,PAGE了解一下。

游戏辅助的应用发布方面:

自然是用pyinstaller打包成32位版的exe发布了,带上程序图标,版本信息,都不是事儿

 游戏核心模拟方面:

当然不是通过手敲代码实现了,而是通过调用目前市场上强大的dll插件了。比如com组件如大漠插件、乐玩插件。或者说,把易语言的一些模块编译成windll来调用也行哦

辅助窗体热键注册方面:

这些需要用到底层的东西了,用win32的东西实现的,可以实现注册全局热键。原理是单独一个线程用于检测热键按下,然后热键按下后单独开辟线程执行需要的功能。鉴于原生的太难写,我自己封装了并且写了一个demo。注册全局组合键和单独的热键都是没问题的。

前面三个方面仁者见仁了。后面这个我就贴个核心源码吧,免得以后找不到了。

下面贴一段新的代码:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# File : 简单热键.py
# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------
# 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 = {} #待注册热键信息
 
  def regiskey(self, hwnd=None, flagid=0, fnkey=win32con.MOD_ALT, vkey=win32con.VK_F9): # 注册热键,默认一个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) #这里待注册的信息
    sleep(0.1)
    return id
 
  def fast_reg(self,id,key = (0,win32con.VK_HOME),func = lambda:print('热键注册开始')):
    if not self.regiskey(None, id, key[0], key[1]):
      print("热键注册失败")
      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)  #这里打印传入给注册函数的参数
              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) # 启动监听热键按下线程
 
    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次的跳动")
    sleep(1)
 
def stop_jump(start_id,hotkey):
  hotkey.hkey_running[start_id] = False
  print(f"{start_id} 即将停止")
  sleep(1)
  print(f'当前线程列表:{activeCount()}', enumerate())
 
def main():
  hotkey = Hotkey()
  start_id = hotkey.reg(key = (win32con.MOD_ALT,win32con.VK_HOME),func=jump,args=(jump,hotkey)) #alt home键 开始
  hotkey.reg(key = (0,win32con.VK_END),func=stop_jump,args=(start_id,hotkey)) #alt end键 结束
  hotkey.start() #启动热键主线程
 
  print(f"当前总线程数量:{activeCount()}")
  print('当前线程列表:', enumerate())
  print('热键注册初始化完毕,尝试按组合键alt+Home 或者单键END看效果')
 
if __name__ == '__main__':
  main()

以下是旧的代码,用起来比较麻烦。

#!/usr/bin/env python3
# _*_ coding: utf-8 _*_
# File : demo.py
# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------
# 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)] # 创建两个热键序列
h_keys = {i: False for i in h_ids} # 初始化所有热键序列的标志符为False
h_dict = {} # 初始化一个空的字典,记录id与func
 
 
class Hotkey(Thread): # 创建一个Thread的扩展类
  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): # 注册热键,默认一个alt+F9
    return self.user32.RegisterHotKey(hwnd, flagid, fnkey, vkey)
 
  def callback(self, id, func):
    h_dict[id] = func # 这个id对应这个func,没有就是新增,有就是修改
 
    def inner():
      for key, value in h_dict.items():
        print(f'总的热键池:{h_ids},当前热键序号:{key}, 当前热键功能:{value},当前热键状态:{h_keys[h_ids[key]]}')
      while True:
        for key, value in h_dict.items():
          if h_keys[h_ids[key]]:
            thread_it(value) # 另外开线程执行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):  # 注册快捷键alt+F9并判断是否成功,该热键用于执行一次需要执行的内容。
      print(f"热键注册失败! id{h_ids[0]}") # 返回一个错误信息
    if not self.regiskey(None,h_ids[1],0,win32con.VK_F10):  # 注册快捷键F10并判断是否成功,该热键用于结束程序,且最好这么结束,否则影响下一次注册热键。
      print(f"热键注册失败! id{h_ids[1]}")
 
    # 以下为检测热键是否被按下,并在最后释放快捷键
    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)
        # 必须得释放热键,否则下次就会注册失败,所以当程序异常退出,没有释放热键,
        # 那么下次很可能就没办法注册成功了,这时可以换一个热键测试
 
 
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("已设置tqtimer启用状态为:{}".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("已设置tqtimer启用状态为:{}".format(flag))
 
 
def test_start():
  print("按下了开始键...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"当前总线程数量:{count}")
  print('当前线程列表:', enumerate())
  print('热键注册初始化完毕,尝试按组合键alt+F9 或者单键F10看效果')
  while True:
    pass
 
 
if __name__ == '__main__':
  run_ok()

这里是没弄界面的源码,所以我就把主线程死循环阻塞了。运行后按alt+F9会打印按下了开始键,按F10会打印按下了停止键。

如果你在tkinter里面跑,直接把run_ok函数后面的while True:pass删掉,然后在init函数里面加入run_ok()就行了。这里指的用PAGE设计的tkinter程序哈!

那么窗体创建完毕就会自动阻塞主线程,其他监控热键的线程随主线程结束。启动期间独立运行互不干扰。

到此这篇关于python3注册全局热键的实现的文章就介绍到这了,更多相关python3 注册全局热键内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
使用Python的Twisted框架构建非阻塞下载程序的实例教程
May 25 Python
Python脚本处理空格的方法
Aug 08 Python
python实现Adapter模式实例代码
Feb 09 Python
python 获取当天凌晨零点的时间戳方法
May 22 Python
Python实现读写INI配置文件的方法示例
Jun 09 Python
Django-Rest-Framework 权限管理源码浅析(小结)
Nov 12 Python
django2笔记之路由path语法的实现
Jul 17 Python
在pytorch中为Module和Tensor指定GPU的例子
Aug 19 Python
Python实现钉钉订阅消息功能
Jan 14 Python
python和php哪个容易学
Jun 19 Python
Python 分布式缓存之Reids数据类型操作详解
Jun 24 Python
python opencv旋转图片的使用方法
Jun 04 Python
浅谈Python线程的同步互斥与死锁
Mar 22 #Python
Django 项目布局方法(值得推荐)
Mar 22 #Python
python实现吃苹果小游戏
Mar 21 #Python
python实现贪吃蛇游戏源码
Mar 21 #Python
python实现微信打飞机游戏
Mar 24 #Python
Python类的动态绑定实现原理
Mar 21 #Python
Python类和实例的属性机制原理详解
Mar 21 #Python
You might like
php使用websocket示例详解
2014/03/12 PHP
PHP处理二进制数据的实现方法
2016/06/13 PHP
php模拟post提交请求调用接口示例解析
2020/08/07 PHP
JQuery live函数
2010/12/24 Javascript
jQuery.autocomplete 支持中文输入(firefox)修正方法
2011/03/10 Javascript
js获取单选框或复选框值及操作
2012/12/18 Javascript
jQuery easyui datagrid动态查询数据实例讲解
2013/02/26 Javascript
js统计录入文本框中字符的个数并加以限制不超过多少
2014/05/23 Javascript
网页下载文件期间如何防止用户对网页进行其他操作
2014/06/27 Javascript
jquery实现图片左右切换的方法
2015/05/07 Javascript
jQuery动画显示和隐藏效果实例演示(附demo源码下载)
2015/12/31 Javascript
Bootstrap树形控件使用方法详解
2016/01/27 Javascript
关于微信jssdk实现多图片上传的一点心得分享
2016/12/13 Javascript
js 作用域和变量详解
2017/02/16 Javascript
NodeJS处理Express中异步错误
2017/03/26 NodeJs
vue.js删除列表中的一行
2018/06/30 Javascript
编写更好的JavaScript条件式和匹配条件的技巧(小结)
2019/06/27 Javascript
基于Django的python验证码(实例讲解)
2017/10/23 Python
python使用代理ip访问网站的实例
2018/05/07 Python
python筛选出两个文件中重复行的方法
2018/05/31 Python
python使用pipeline批量读写redis的方法
2019/02/18 Python
详解Python使用Plotly绘图工具,绘制甘特图
2019/04/02 Python
Python 函数绘图及函数图像微分与积分
2019/11/20 Python
关于win10在tensorflow的安装及在pycharm中运行步骤详解
2020/03/16 Python
新西兰网上购物,折扣店:BestDeals.co.nz
2019/03/20 全球购物
一套Delphi的笔试题一
2016/02/14 面试题
护士演讲稿范文
2014/01/05 职场文书
韩国商务邀请函
2014/01/14 职场文书
《雷雨》教学反思
2014/02/20 职场文书
实习单位评语
2014/04/26 职场文书
党员活动总结
2015/02/04 职场文书
雷锋之歌观后感
2015/06/10 职场文书
欠条格式范本
2015/07/03 职场文书
2016年优秀党员教师先进事迹材料
2016/02/29 职场文书
springmvc直接不经过controller访问WEB-INF中的页面问题
2022/02/24 Java/Android
十大最强妖精系宝可梦,哲尔尼亚斯实力最强,第五被称为大力士
2022/03/18 日漫