Package: xyw-macro
说明文档快速开始这个库的灵感来自于一个全键可编程的小键盘，那个键盘可以通过按键切换不同的配置，9个按键可以当成36个快捷键使用。本来想买的，但是想想对我真没啥用，就干脆写了这么一个库模拟体验一下。importtimefromxyw_macroimport*deffunc1():"""命令函数1"""time.sleep(4)print('func1')deffunc2():"""命令函数2"""print('func2')deffunc3():"""命令函数3"""print('func3')# 创建宏实例macro=Macro()# 默认配置切换按键为F1，长按可以进入或退出键盘宏模式，宏模式下短按可以循环切换配置# 创建配置实例1config1=Configuration('01','part',Configuration.FUNCTION)# 向配置1中添加命令config1.add_command(Condition(VK('VK_F3'),'打印'),func1)# 创建配置实例2config2=Configuration('02','all',Configuration.FUNCTION)# 向配置2中添加命令config2.add_command(Condition(VK('VK_F2'),'打印'),func2)# 向宏实例中添加之前定义的配置实例macro.add_config(config1)macro.add_config(config2)# 向宏实例中所有现有的配置实例中添加同一命令macro.add_command_to_all(Condition(VK('VK_F4'),'打印'),func3)# 运行键盘宏macro.run()command模块模拟键盘输入以按下Ctrl+C为例，代码如下：fromxyw_macro.commandimport*press_key(VK('VK_CONTROL'))press_key(ord('C'))release_key(VK('VK_CONTROL'))release_key(ord('C'))time.sleep(0.2)# 对于这种功能快捷键最好加上0.2s的睡眠时间，等待系统响应完成注意：press_key与release_key函数必须成对使用，同时请勿使用其他第三方库中的键盘输入函数代替，非本模块中的键盘输入函数会被拦截当做普通的键盘输入处理。有时游戏中为了避免脚本检测，需要模拟真人按键，此时可以在每个按键的按松动作之间插入random_sleep函数，示例如下：press_key(ord('C'))random_sleep(0.2,0.5)# 休眠时间波动范围为0.2*(1-0.5)~0.2*(1+0.5)release_key(ord('C'))输入字符串模拟键盘输入unicode字符串：input_chars('pip install xyw-macro --upgrade')注意：为安全起见，请勿使用此函数输入账号密码，如必须使用，可以参见安全使用一节。获取选中文件绝对地址直接调用win32的api很难直接获得资源管理器中选中文件的绝对地址，所以在这里采用了一种折中的做法，首先调用Ctrl+C复制选中文件到系统剪切板，然后读取剪切板中的文件名信息：print(get_clipboard_files())# 单个文件夹>>>('C:\\Users\\Administrator\\Desktop\\keyboard',)# 单个文件>>>('C:\\Users\\Administrator\\Desktop\\keyboard\\setup.py',)# 多个文件或文件夹>>>('C:\\Users\\Administrator\\Desktop\\keyboard\\xyw_macro','C:\\Users\\Administrator\\Desktop\\keyboard\\LICENSE','C:\\Users\\Administrator\\Desktop\\keyboard\\README.md','C:\\Users\\Administrator\\Desktop\\keyboard\\setup.py')注意：此函数设计的初衷是为了方便实现一键处理文件的宏功能。打开应用快捷方式以打开企业微信为例：run_cmd(r"C:\Program Files (x86)\WXWork\WXWork.exe")注意：此处的地址可以通过快捷方式的属性查看。打开文件或文件夹open_file('hook.py')打开网址open_url('www.baidu.com',r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe")注意：默认情况下只需要第一个参数即可，此时会以系统默认浏览器打开地址。第二个为可选参数，可以指定需要使用的浏览器的程序地址。切换输入法change_to_en()# 切换到英文键盘布局change_to_zh()# 切换到中文键盘布局切换配置如果总共有两套配置，即通过add_config方法添加了两次配置信息，可以通过配置的index手动切换配置：change_to_config(0)# 切换到第一套配置更改当前窗口状态minimize_window()# 最小化当前窗口maximize_window()# 最大化当前窗口restore_window()# 还原当前窗口close_window()# 关闭当前窗口topmost_window()# 置顶当前窗口notopmost_window()# 取消置顶当前窗口set_window_alpha(180)# 更改当前窗口透明度，0-255change_window_state()# 更改当前窗口状态，更改顺序为最小化、正常、最大化循环change_window_topmost()# 更改当前窗口置顶状态change_window_alpha()# 循环切换当前窗口透明度，有63、127、191、255四档播放wav格式音频此功能是对simpleaudio库的简单封装，只支持wav格式音频文件，如需更多文件格式支持及更复杂的播放功能可以使用pyaudio等第三方库自行实现：play_wave('test.wav')交互框确认框tkinter中本身就有一系列消息框，在编写命令函数时都可以正常使用。对于确认框，在引入本模块后可以通过装饰器更方便的使用：fromxyw_macroimport*@confirm_box('确定打开本网站吗？')defopen_sec_url(url):open_url(url)change_to_config(0)config3=Configuration('地址','all',excepts=[VK('VK_RETURN'),VK('VK_ESCAPE')])config3.add_command(Condition(ord('B'),'百度'),lambda:open_sec_url(r'https://www.baidu.com/'))输入框对于命令函数中的参数输入也可以使用装饰器轻松地完成：@input_box(InputField(name='输入框',type='entry',default='默认值',focus=True),InputField('文件选择','file','默认值'),InputField('文件夹选择','dir','默认值'),InputField(name='下拉框',type='combobox',default=0,options=['1','2']))deftest(*args):print(args)config3.add_command(Condition(VK('VK_F7'),'测试'),test)注意：input_box装饰器目前仅支持四种类型的输入框，且所有输入框最终输出的参数皆为字符串形式，数据类型需要在命令函数中自行转换。InputField共有5个参数，name：参数名（提示用），type：输入框类型，default：默认值（combobox类型的默认值为int类型，代表options参数中值的index），options：下拉框选项（combobox类型独占参数），focus：是否获得焦点（用于设置初始焦点位置）。安全使用如果脚本中包含部分隐私信息，可以在脚本中添加mac地址验证，同时将脚本打包编译为pwd格式文件，脚本内容示例如下：# macro.pyfromxyw_macroimport*fromxyw_macro.commandimport*macro=Macro()ifconfirm_mac('0C-9D-92-0F-42-65'):deffunc():print('True function')config=Configuration('true','part',Configuration.FUNCTION)config.add_command(Condition(VK('VK_F2'),'打印'),func)macro.add_config(config)else:config=Configuration('false','all')macro.add_config(config)将macro.py文件编译为pwd格式后需要再写一个入口脚本，重命名为pyw后缀后即可无命令窗后台运行：# run.pywfrommacro3importmacroif__name__=='__main__':macro.run()注意：此方法并不是完全安全，只能说是降低了安全风险，不过一个宏命令脚本也不会有人专门去研究。说到底也就是和门锁一样，防君子不防小人。实例演示# 内置模块importosimporturllibimporttkinter.messagebox# 第三方库importimg2pdfimportfitzimportpyperclipimportwin32gui# 本模块fromxyw_macroimport*fromxyw_macro.commandimport*# 创建实例，设置切换按键为右altmacro=Macro(VK('VK_RMENU'))# 检测电脑mac地址是否匹配ifget_mac()=='84-E6-F6-0D-0E-D5':defsearch_selected_text():"""在浏览器中百度搜索选定的文本:return:"""touch_keys([VK('VK_CONTROL'),ord('C')])random_sleep(0.2)text=pyperclip.paste()search_text=urllib.parse.quote(text.encode('gbk'))url='https://www.baidu.com/s?wd='+search_textopen_nor_url(url)deftouch_keys(keys):"""模拟同时按下多个按键:param keys: 按键虚拟键号列表:return:"""ifisinstance(keys,int):keys=[keys]ifnotisinstance(keys,(list,tuple)):raiseTypeError('param keys must be int or list')forkeyinkeys:press_key(key)forkeyinkeys:release_key(key)definput_text(text):"""输入文本后切换回第一套配置:param text::return:"""input_chars(text)change_to_config(0)defpassword_q():"""输入邮箱1174543101@qq.com:return:"""change_to_en()touch_key(VK('VK_NUMPAD1'))touch_key(VK('VK_NUMPAD1'))touch_key(VK('VK_NUMPAD7'))touch_key(VK('VK_NUMPAD4'))touch_key(VK('VK_NUMPAD5'))touch_key(VK('VK_NUMPAD4'))touch_key(VK('VK_NUMPAD3'))touch_key(VK('VK_NUMPAD1'))touch_key(VK('VK_NUMPAD0'))touch_key(VK('VK_NUMPAD1'))press_key(VK('VK_SHIFT'))random_sleep(0.01)touch_key(ord('2'))random_sleep(0.01)release_key(VK('VK_SHIFT'))touch_key(ord('Q'))touch_key(ord('Q'))touch_key(VK('VK_DECIMAL'))touch_key(ord('C'))touch_key(ord('O'))touch_key(ord('M'))change_to_zh()change_to_config(0)@confirm_box('确定打开本网站吗？')defopen_sec_url(url):"""打开网址，确认框确认后打开:param url::return:"""open_url(url)change_to_config(0)defopen_nor_url(url):"""打开网址:param url::return:"""open_url(url)change_to_config(0)defopen_path(path):"""在资源管理器中打开指定地址:param path::return:"""open_file(path)change_to_config(0)defimg_to_pdf():"""将文件夹中所有图片合成到一个pdf文件中，并以文件夹名称命名:return:"""random_sleep()target=get_clipboard_files()iftargetisNone:target=InputBox('输入框',InputField('图片','dir')).show()iftargetisNone:raiseRuntimeError('there is no file selected')iflen(target)==1andos.path.isdir(target[0]):dir_path=target[0]else:raiseRuntimeError('target must be a dir')imgs=[]forroot,dirs,filesinos.walk(dir_path):forfileinfiles:filename,ext=os.path.splitext(file)ext=ext.lower()ifextin['.png','.jpg','.jpeg']:imgs.append(filename+ext)iflen(imgs)==0:raiseRuntimeError('there is no image in this dir')imgs=[os.path.join(dir_path,img)forimginimgs]imgs.sort()save_dir,pdf_filename=os.path.split(dir_path)ifos.path.isfile(os.path.join(save_dir,pdf_filename+'.pdf')):index=1whileTrue:ifnotos.path.isfile(os.path.join(save_dir,pdf_filename+'_%d.pdf'%index)):pdf_filename=pdf_filename+'_%d.pdf'%indexbreakelse:index+=1else:pdf_filename=pdf_filename+'.pdf'page_size=(img2pdf.mm_to_pt(210),img2pdf.mm_to_pt(297))layout_fun=img2pdf.get_layout_fun(page_size)try:withopen(os.path.join(save_dir,pdf_filename),'wb')asf:f.write(img2pdf.convert(imgs,layout_fun=layout_fun))exceptimg2pdf.AlphaChannelError:os.remove(os.path.join(save_dir,pdf_filename))raiseRuntimeError('Image contains transparency which cannot be retained in PDF')exceptExceptionase:raiseRuntimeError(str(e))change_to_config(0)tkinter.messagebox.showinfo('提示','合并pdf成功！')defpdf_to_img():"""将pdf文件每一页保存为同一文件夹中的jpg图片，文件夹名为pdf文件名:return:"""random_sleep()target=get_clipboard_files()iftargetisNone:target=InputBox('输入框',InputField('PDF文件','file')).show()iftargetisNone:raiseRuntimeError('there is no file selected')iflen(target)==1andos.path.isfile(target[0]):file_path=target[0]else:raiseRuntimeError('target must be a file')dir_path,file=os.path.split(file_path)filename,ext=os.path.splitext(file)ifext.lower()!='.pdf':raiseRuntimeError('target file type must be pdf')save_dir=os.path.join(dir_path,filename)ifos.path.isdir(save_dir):index=1whileTrue:save_dir=os.path.join(dir_path,filename+'_%d'%index)ifnotos.path.isdir(save_dir):breakelse:index+=1print(index)os.makedirs(save_dir)try:trans=fitz.Matrix(2,2)withfitz.open(file_path)aspdf:forpginrange(pdf.pageCount):page=pdf[pg]pix=page.get_pixmap(alpha=False,matrix=trans)img_name=filename+'_%s.jpg'%str(pg+1).zfill(4)pix.save(os.path.join(save_dir,img_name))exceptException:os.removedirs(save_dir)change_to_config(0)tkinter.messagebox.showinfo('提示','pdf转换图片成功！')@confirm_box('确定要切换到工作网络吗？')defchange_to_work_mode():"""切换到工作网络，即禁用无线网卡，启用有线网卡win7可用，其他未测试:return:"""enable_network_adapter('本地连接')disable_network_adapter('无线网络连接')change_to_config(0)@confirm_box('确定要切换到个人网络吗？')defchange_to_personal_mode():"""切换到个人网络，即启用有线网卡，禁用无线网卡:return:"""enable_network_adapter('无线网络连接')disable_network_adapter('本地连接')change_to_config(0)config1=Configuration('默认','part',Configuration.FUNCTION)config1.add_command(Condition(VK('VK_F5'),'音量+',True),lambda:touch_keys(VK('VK_VOLUME_UP')))config1.add_command(Condition(VK('VK_F6'),'音量-',True),lambda:touch_keys(VK('VK_VOLUME_DOWN')))config1.add_command(Condition(VK('VK_F7'),'上一桌面',True),lambda:touch_keys([VK('VK_CONTROL'),VK('VK_LWIN'),VK('VK_LEFT')]))config1.add_command(Condition(VK('VK_F8'),'下一桌面',True),lambda:touch_keys([VK('VK_CONTROL'),VK('VK_LWIN'),VK('VK_RIGHT')]))config1.add_command(Condition(VK('VK_F9'),'关闭窗口'),close_window)config1.add_command(Condition(VK('VK_F10'),'窗口状态'),change_window_state)config1.add_command(Condition(VK('VK_F11'),'窗口置顶'),change_window_topmost)config1.add_command(Condition(VK('VK_F12'),'窗口透明'),change_window_alpha)config2=Configuration('地址','all',excepts=[VK('VK_RETURN'),VK('VK_ESCAPE')])config2.add_command(Condition(ord('B'),'百度'),lambda:open_nor_url(r'https://www.baidu.com/'))config2.add_command(Condition(ord('L'),'bili'),lambda:open_nor_url(r'https://www.bilibili.com/'))config2.add_command(Condition(ord('T'),'翻译'),lambda:open_nor_url(r'https://translate.google.cn/'))config2.add_command(Condition(ord('Z'),'知乎'),lambda:open_nor_url(r'https://www.zhihu.com/'))config2.add_command(Condition(ord('D'),'斗鱼'),lambda:open_nor_url(r'https://www.douyu.com/room/my_follow/'))config2.add_command(Condition(ord('N'),'NGA'),lambda:open_nor_url(r'https://bbs.nga.cn/'))config2.add_command(Condition(ord('P'),'pypi'),lambda:open_nor_url(r'https://pypi.org/'))config2.add_command(Condition(ord('M'),'bimi'),lambda:open_sec_url(r'http://www.bimiacg.com/'))config2.add_command(Condition(ord('G'),'G盘'),lambda:open_path(r'G:'))config2.add_command(Condition(ord('F'),'F盘'),lambda:open_path(r'F:'))config3=Configuration('文字','all',excepts=[VK('VK_RETURN'),VK('VK_ESCAPE')])config3.add_command(Condition(ord('P')),lambda:input_text('python setup.py sdist bdist_wheel'))config3.add_command(Condition(ord('T')),lambda:input_text('twine upload dist/*'))config3.add_command(Condition(ord('N')),lambda:input_text('nuitka --mingw64 --module --show-progress --output-dir=o '))config3.add_command(Condition(ord('Q')),password_q)config4=Configuration('脚本','all',excepts=[VK('VK_RETURN'),VK('VK_ESCAPE')])config4.add_command(Condition(ord('P'),'PDF'),img_to_pdf)config4.add_command(Condition(ord('I'),'IMAGE'),pdf_to_img)config4.add_command(Condition(ord('S'),'搜索'),search_selected_text)config4.add_command(Condition(ord('W'),'工作网络'),change_to_work_mode)config4.add_command(Condition(ord('Q'),'个人网络'),change_to_personal_mode)macro.add_config(config1)macro.add_config(config2)macro.add_config(config3)macro.add_config(config4)macro.add_command_to_all(Condition(VK('VK_F1'),'配置默认'),lambdaindex=0:change_to_config(index))macro.add_command_to_all(Condition(VK('VK_F2'),'配置地址'),lambdaindex=1:change_to_config(index))macro.add_command_to_all(Condition(VK('VK_F3'),'配置文本'),lambdaindex=2:change_to_config(index))macro.add_command_to_all(Condition(VK('VK_F4'),'配置脚本'),lambdaindex=3:change_to_config(index))else:config=Configuration('功能','all')macro.add_config(config)注：为了方便切换，此处将切换键设置为了右alt，如果有可编程鼠标的话，可以将右alt映射到鼠标上，切换起来会更加方便。Release Notes0.0.1初次发布，实现了键盘宏的基本功能0.0.2将GUI框架由pyqt5换为tkinter，提高兼容性新增command模块，包含部分常用键盘宏命令函数新增部分源码注释修复了部分bug0.0.3优化了Macro类新增部分源码注释command模块中新增了部分常用函数0.0.4更改了提示窗口尺寸，缩小为原来的一半更改了提示窗口显示时间，减少为原来的一半添加部分说明文档修复了command模块中的部分bug修复了键盘回调函数中的部分bug0.0.5修复了不能自定义切换键的bug0.0.6修复了长按触发命令中的部分bug0.0.7修复了不能设置同一个键按下和松开的逻辑bug修复了命令运行时非功能区按键无法使用的bug修复了切换键的bugCondition类添加了show参数，可以控制提示框是否出现Configition类添加了excepts参数，可以排除all模式下的部分按键notify模块中添加了确认框装饰器、参数输入框装饰器0.0.8优化了command模块中部分函数新增了Macro类中的两个属性，可以更改当前配置和运行状态新增了部分注释0.0.9修复了Configition类中excepts参数的bug修复了command模块中部分函数bug0.0.10更改了notify模块中InputBox的参数形式，增加了combobox类型输入框Macro类新增add_command_to_all方法，用于向所有配置中批量统一添加命令0.0.11为touch_key函数增加了按下和松开的间隔时间command模块新增了两个函数，change_to_zh以及change_to_en，用于切换输入法状态0.0.12修复了一个显示bug0.0.13command模块新增了两个函数enable_network_adapter以及disable_network_adapter，用于启用和禁用网络适配器（网卡）command模块中新增了部分窗口操作相关的函数0.0.14修复了win7上更改窗体透明度报错的bug新增了播放音频的函数更新了说明文档0.0.15新增了命令提示框的颜色设置选项，可以设置前景色和背景色0.0.16Macro类中pool属性变为可访问