├── README.md ├── pyhk.py └── samples └── basic.py /README.md: -------------------------------------------------------------------------------- 1 | This project is not maintained anymore. There is a Python 3 version here: [https://github.com/FunkMastaZ/pyhk3](https://github.com/FunkMastaZ/pyhk3) 2 | 3 | 4 | # PYHK 5 | 6 | PYHK is python module that allows for simple hotkey registration in any program. 7 | It extends pyhook to have normal hotkey functionality like autohotkey (AHK) scripts. 8 | 9 | Main features: 10 | * Simple hotkey registration 11 | * Hotkey removal by hotkey or id 12 | * Option to run trigger function in thread 13 | * Option to run trigger function on a key up event 14 | 15 | Tested with Python 2.6, 2.7 16 | 17 | Total downloads before moving to github: 2562 18 | 19 | PYHK is as simple as this: 20 | 21 | ```python 22 | import pyhk 23 | 24 | def fun(): 25 | print "Do something" 26 | 27 | #create pyhk class instance 28 | hot = pyhk.pyhk() 29 | 30 | #add hotkey 31 | hot.addHotkey(['Ctrl', 'Alt','7'],fun) 32 | 33 | #start looking for hotkey. 34 | hot.start() 35 | ``` 36 | 37 | ## Documentation 38 | http://www.schurpf.com/python/python-hotkey-module/pyhk-end-user-documentation/ 39 | 40 | ## Old version 41 | http://www.schurpf.com/python/python-hotkey-module/#download 42 | 43 | ## Dependencies 44 | [Pyhook](http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=Main_Page) 45 | 46 | ## Links 47 | 48 | http://www.schurpf.com/python/python-hotkey-module/ - Project home page 49 | http://www.schurpf.com/python/python-hotkey-module/pyhk-end-user-documentation/ - End user documentation 50 | 51 | ## Contact Author 52 | 53 | michael at schurpf dot com 54 | Please write in English or German only. 55 | 56 | ##K nown Issues 57 | 58 | Python IDLE freezes at times. Best use is to call your script directly from the command line with python YOURSCRIPT.py. 59 | 60 | After logout or sleep on some machines the hotkeys get triggered by only pressing the modifiers. 61 | 62 | ## Alternatives 63 | 64 | [https://github.com/IronManMark20/hooked](https://github.com/IronManMark20/hooked) 65 | 66 | ## License 67 | 68 | Distributed under GNU General Public License version 2. 69 | -------------------------------------------------------------------------------- /pyhk.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #pyhk.py 3 | # 4 | # Copyright (C) 2011 Michael Schurpf 5 | # E-mail: michaelschuerpf AT gmail DOT com 6 | # 7 | # Extends pyhook to have normal hotkey functionality like autohotkey (AHK) scripts. 8 | # Hotkeys have to be entered in lists! Even if single items. 9 | # Hotkeys can be entered in ID list or human readble. 10 | # Example: human readble: ['Lcontrol','7'] for left CTRL 7. 11 | # or ['Ctrl','7'] for CTRL 7 12 | # mouse example: ['mouse left','A'] for mouse left and A together 13 | # See createKeyLookup function for all labels, 14 | # 15 | # Remark: 16 | # mouse move, mouse wheel up, mouse wheel down can only be used on its own 17 | # Example: ['mouse wheel down'] 18 | # Not working ['mouse wheel down','4'] 19 | # 20 | # Scripts maintained at http://www.swisstard.com 21 | # Comments, suggestions and bug reports welcome. 22 | # 23 | # Released subject to the GNU Public License 24 | # 25 | # This program is free software; you can redistribute it and/or 26 | # modify it under the terms of the GNU General Public License 27 | # as published by the Free Software Foundation; either version 2 28 | # of the License, or (at your option) any later version. 29 | # 30 | # In addition to the permissions in the GNU General Public License, the 31 | # authors give you unlimited permission to link or embed the compiled 32 | # version of this file into combinations with other programs, and to 33 | # distribute those combinations without any restriction coming from the 34 | # use of this file. (The General Public License restrictions do apply in 35 | # other respects; for example, they cover modification of the file, and 36 | # distribution when not linked into a combine executable.) 37 | # 38 | # This program is distributed in the hope that it will be useful, 39 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 40 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 41 | # GNU General Public License for more details. 42 | # 43 | # You should have received a copy of the GNU General Public License 44 | # along with this program; if not, write to the Free Software 45 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 46 | 47 | import pythoncom, pyHook, ctypes, thread 48 | 49 | class pyhk: 50 | """Hotkey class extending pyhook""" 51 | 52 | def __init__(self ): 53 | #initiate internal hotkey list 54 | self.KeyDownID = [] 55 | self.KeyDown = [] 56 | 57 | #initiate user hotkey list 58 | self.UserHKF = [] 59 | self.UserHKFUp = [] 60 | self.HKFIDDict = {} 61 | 62 | #create Lookup for event keys and ids 63 | #for keyboards 64 | self.ID2Key, self.Key2ID = self.createKeyLookup() 65 | #for mouse, artifical lookup first 66 | self.mouseDown_MID2eventMessage, self.mouseDown_eventMessage2MID, self.mouseUp_MID2eventMessage, self.mouseUp_eventMessage2MID = self.createMouseLookup() 67 | 68 | #create list for singleEvent, ie there is only a key down, no key up 69 | self.singleEventMouseMessage, self.singleEventMID = self.createSingleEventMouse() 70 | 71 | #creat list for merged keys like Ctrl <= Lcontrol, Rcontrol 72 | self.KeyID2MEID = self.createMergeKeys() 73 | 74 | # create a hook manager 75 | self.hm = pyHook.HookManager() 76 | 77 | # watch for all keyboard events 78 | self.hm.KeyDown = self.OnKeyDown 79 | self.hm.KeyUp = self.OnKeyUp 80 | 81 | # watch for all mouse events 82 | self.hm.MouseAllButtonsDown = self.OnKeyDown 83 | self.hm.MouseAllButtonsUp = self.OnKeyUp 84 | 85 | self.hm.MouseMove = self.OnSingleEventMouse 86 | self.hm.MouseWheel = self.OnSingleEventMouse 87 | 88 | # set the hook 89 | self.hm.HookKeyboard() 90 | self.hm.HookMouse() 91 | 92 | #set Ending hotkey 93 | self.EndHotkey = ['Ctrl','Shift','Q'] 94 | self.setEndHotkey(self.EndHotkey) 95 | 96 | def start(self): 97 | """Start pyhk to check for hotkeys""" 98 | pythoncom.PumpMessages() 99 | 100 | def end(self): 101 | """End pyhk to check for hotkeys""" 102 | ctypes.windll.user32.PostQuitMessage(0) 103 | 104 | #-------------------------------------------------------- 105 | 106 | def isIDHotkey(self,hotkey): 107 | """Test if hotkey is coded in IDs""" 108 | for key in hotkey: 109 | if type(key) == str: 110 | return False 111 | return True 112 | 113 | def isHumanHotkey(self,hotkey): 114 | """Test if hotkey is coded human readable. Ex ALT F2""" 115 | try: 116 | [self.Key2ID[key] for key in hotkey] 117 | except: 118 | return False 119 | return True 120 | 121 | def hotkey2ID(self,hotkey): 122 | """Converts human readable hotkeys to IDs""" 123 | if self.isHumanHotkey(hotkey): 124 | return [self.Key2ID[key] for key in hotkey] 125 | else: 126 | raise Exception("Invalid Hotkey") 127 | 128 | def getHotkeyList(self,hotkey): 129 | """Create a IDlist of hotkeys if necessary to ensure functionality of merged hotkeys""" 130 | hotkeyVariationList = [] 131 | hotkeyList = [] 132 | 133 | #convert everyting into ID,MID,MEID 134 | if self.isIDHotkey(hotkey): 135 | IDHotkey = hotkey 136 | else: 137 | IDHotkey = self.hotkey2ID(hotkey) 138 | 139 | IDHotkeyTemp = IDHotkey[:] 140 | 141 | #check if there is a MEID and create accorind hotkeyVariationList 142 | for Key in self.KeyID2MEID: 143 | 144 | if self.KeyID2MEID[Key] in IDHotkeyTemp: 145 | #merged hotkey in hotkey 146 | #get MEID 147 | MEIDTemp = self.KeyID2MEID[Key] 148 | #get all KeyIDs 149 | KeyIDVariationTemp = [k for k in self.KeyID2MEID if self.KeyID2MEID[k] == MEIDTemp] 150 | 151 | #remove MEID from IDHotekey 152 | IDHotkeyTemp.remove(MEIDTemp) 153 | 154 | #store according MEID and KeyIDList 155 | hotkeyVariationList.append(KeyIDVariationTemp) 156 | 157 | if len(hotkeyVariationList) > 0: 158 | hotkeyVariationList.append(IDHotkeyTemp) 159 | #get all possible permutations 160 | hotkeyList = UniquePermutation(hotkeyVariationList) 161 | else: 162 | hotkeyList = [IDHotkey] 163 | 164 | return hotkeyList 165 | 166 | def addHotkey(self,hotkey,fhot, isThread=False, up = False): 167 | """Add hotkeys with according function""" 168 | hotkeyList = self.getHotkeyList(hotkey) 169 | 170 | newHKFID = self.getNewHKFID() 171 | self.HKFIDDict[newHKFID] = [] 172 | 173 | if up: 174 | 175 | if len(hotkey)<2: 176 | if isThread: 177 | t = ExecFunThread(fhot) 178 | for IDHotKeyItem in hotkeyList: 179 | self.UserHKFUp.append([IDHotKeyItem,t.Start]) 180 | self.HKFIDDict[newHKFID].append([IDHotKeyItem,t.Start]) 181 | 182 | else: 183 | for IDHotKeyItem in hotkeyList: 184 | self.UserHKFUp.append([IDHotKeyItem,fhot]) 185 | self.HKFIDDict[newHKFID].append([IDHotKeyItem,fhot]) 186 | 187 | else: 188 | 189 | if isThread: 190 | t = ExecFunThread(fhot) 191 | for IDHotKeyItem in hotkeyList: 192 | self.UserHKF.append([IDHotKeyItem,t.Start]) 193 | self.HKFIDDict[newHKFID].append([IDHotKeyItem,t.Start]) 194 | 195 | else: 196 | for IDHotKeyItem in hotkeyList: 197 | self.UserHKF.append([IDHotKeyItem,fhot]) 198 | self.HKFIDDict[newHKFID].append([IDHotKeyItem,fhot]) 199 | 200 | return newHKFID 201 | 202 | def removeHotkey(self,hotkey=False, id=False): 203 | """Remove hotkeys and corresponding function""" 204 | HKFID = id 205 | try: 206 | if hotkey: 207 | hotkeyList = self.getHotkeyList(hotkey) 208 | try: 209 | UserHKFTemp = [[hotk, fun] for hotk, fun in self.UserHKF if not(hotk in hotkeyList)] 210 | self.UserHKF = UserHKFTemp[:] 211 | except: 212 | pass 213 | try: 214 | UserHKFTemp = [[hotk, fun] for hotk, fun in self.UserHKFUp if not(hotk in hotkeyList)] 215 | self.UserHKFUp = UserHKFTemp[:] 216 | except: 217 | pass 218 | elif HKFID: 219 | for item in self.HKFIDDict[HKFID]: 220 | try: 221 | self.UserHKF.remove(item) 222 | except: 223 | self.UserHKFUp.remove(item) 224 | self.HKFIDDict.pop(HKFID) 225 | else: 226 | self.UserHKF = [] 227 | self.UserHKFUp = [] 228 | except: 229 | pass 230 | 231 | 232 | def setEndHotkey(self, hotkey): 233 | """Add exit hotkeys""" 234 | self.removeHotkey(self.EndHotkey) 235 | self.EndHotkey = hotkey 236 | self.addHotkey(hotkey,self.end) 237 | 238 | #-------------------------------------------------------- 239 | #ID functions for HKFID 240 | def getNewHKFID(self): 241 | try: 242 | return max(self.HKFIDDict.keys())+1 243 | except: 244 | return 1 245 | 246 | #-------------------------------------------------------- 247 | 248 | def isHotkey(self,hotkey): 249 | """Check if hotkey is pressed down 250 | Hotkey is given as KeyID""" 251 | 252 | try: 253 | #make sure exact hotkey is pressed 254 | if not(len(hotkey) == len(self.KeyDownID)): 255 | return False 256 | for hotk in hotkey: 257 | if not(hotk in self.KeyDownID): 258 | return False 259 | except: 260 | return False 261 | 262 | return True 263 | 264 | def OnKeyDown(self,event): 265 | 266 | if not "mouse" in event.MessageName: 267 | #check for merged keys first 268 | eventID = event.KeyID 269 | else: 270 | eventID = self.mouseDown_eventMessage2MID[event.Message] 271 | 272 | #make sure key only gets presse once 273 | if not(eventID in self.KeyDownID): 274 | 275 | self.KeyDownID.append(eventID) 276 | 277 | #Add user hotkeys and functions 278 | for hk, fun in self.UserHKF: 279 | if self.isHotkey(hk): 280 | fun() 281 | 282 | return True 283 | 284 | 285 | def OnKeyUp(self,event): 286 | 287 | if not "mouse" in event.MessageName: 288 | eventID = event.KeyID 289 | else: 290 | eventID = self.mouseUp_eventMessage2MID[event.Message] 291 | 292 | #check for hotkey up keys 293 | for hk, fun in self.UserHKFUp: 294 | if hk[0] == eventID: 295 | fun() 296 | 297 | 298 | try: 299 | self.KeyDownID.remove(eventID) 300 | 301 | except: 302 | pass 303 | return True 304 | 305 | def OnSingleEventMouse(self, event): 306 | """Function to excetue single mouse events""" 307 | 308 | if event.Message in self.singleEventMouseMessage: 309 | #test for mouse wheel: 310 | if event.Message == 522: 311 | if event.Wheel == 1: 312 | eventID = 1004 313 | else: 314 | eventID = 1005 315 | #test mouse move 316 | elif event.Message == 512: 317 | eventID = 1000 318 | else: 319 | return False 320 | 321 | self.KeyDownID.append(eventID) 322 | 323 | #Add user hotkeys and functions 324 | for hk, fun in self.UserHKF: 325 | if self.isHotkey(hk): 326 | fun() 327 | 328 | self.KeyDownID.remove(eventID) 329 | 330 | return True 331 | 332 | 333 | #-------------------------------------------------------- 334 | 335 | def createKeyLookup(self): 336 | """Creates Key look up dictionaries, change names as you please""" 337 | ID2Key = { 8: 'Back', 338 | 9: 'Tab', 339 | 13: 'Return', 340 | 20: 'Capital', 341 | 27: 'Escape', 342 | 32: 'Space', 343 | 33: 'Prior', 344 | 34: 'Next', 345 | 35: 'End', 346 | 36: 'Home', 347 | 37: 'Left', 348 | 38: 'Up', 349 | 39: 'Right', 350 | 40: 'Down', 351 | 44: 'Snapshot', 352 | 46: 'Delete', 353 | 48: '0', 354 | 49: '1', 355 | 50: '2', 356 | 51: '3', 357 | 52: '4', 358 | 53: '5', 359 | 54: '6', 360 | 55: '7', 361 | 56: '8', 362 | 57: '9', 363 | 65: 'A', 364 | 66: 'B', 365 | 67: 'C', 366 | 68: 'D', 367 | 69: 'E', 368 | 70: 'F', 369 | 71: 'G', 370 | 72: 'H', 371 | 73: 'I', 372 | 74: 'J', 373 | 75: 'K', 374 | 76: 'L', 375 | 77: 'M', 376 | 78: 'N', 377 | 79: 'O', 378 | 80: 'P', 379 | 81: 'Q', 380 | 82: 'R', 381 | 83: 'S', 382 | 84: 'T', 383 | 85: 'U', 384 | 86: 'V', 385 | 87: 'W', 386 | 88: 'X', 387 | 89: 'Y', 388 | 90: 'Z', 389 | 91: 'Lwin', 390 | 92: 'Rwin', 391 | 93: 'App', 392 | 95: 'Sleep', 393 | 96: 'Numpad0', 394 | 97: 'Numpad1', 395 | 98: 'Numpad2', 396 | 99: 'Numpad3', 397 | 100: 'Numpad4', 398 | 101: 'Numpad5', 399 | 102: 'Numpad6', 400 | 103: 'Numpad7', 401 | 104: 'Numpad8', 402 | 105: 'Numpad9', 403 | 106: 'Multiply', 404 | 107: 'Add', 405 | 109: 'Subtract', 406 | 110: 'Decimal', 407 | 111: 'Divide', 408 | 112: 'F1', 409 | 113: 'F2', 410 | 114: 'F3', 411 | 115: 'F4', 412 | 116: 'F5', 413 | 117: 'F6', 414 | 118: 'F7', 415 | 119: 'F8', 416 | 120: 'F9', 417 | 121: 'F10', 418 | 122: 'F11', 419 | 123: 'F12', 420 | 144: 'Numlock', 421 | 160: 'Lshift', 422 | 161: 'Rshift', 423 | 162: 'Lcontrol', 424 | 163: 'Rcontrol', 425 | 164: 'Lmenu', 426 | 165: 'Rmenu', 427 | 186: 'Oem_1', 428 | 187: 'Oem_Plus', 429 | 188: 'Oem_Comma', 430 | 189: 'Oem_Minus', 431 | 190: 'Oem_Period', 432 | 191: 'Oem_2', 433 | 192: 'Oem_3', 434 | 219: 'Oem_4', 435 | 220: 'Oem_5', 436 | 221: 'Oem_6', 437 | 222: 'Oem_7', 438 | 1001: 'mouse left', #mouse hotkeys 439 | 1002: 'mouse right', 440 | 1003: 'mouse middle', 441 | 1000: 'mouse move', #single event hotkeys 442 | 1004: 'mouse wheel up', 443 | 1005: 'mouse wheel down', 444 | 1010: 'Ctrl', #merged hotkeys 445 | 1011: 'Alt', 446 | 1012: 'Shift', 447 | 1013: 'Win'} 448 | 449 | Key2ID = dict(map(lambda x,y: (x,y),ID2Key.values(),ID2Key.keys())) 450 | 451 | return ID2Key, Key2ID 452 | 453 | def createMouseLookup(self): 454 | """Takes a event.Message from mouse and converts it to artificial KeyID""" 455 | mouseDown_MID2eventMessage = { 456 | 1001:513, 457 | 1002:516, 458 | 1003:519} 459 | mouseDown_eventMessage2MID = dict(map(lambda x,y: (x,y),mouseDown_MID2eventMessage.values(),mouseDown_MID2eventMessage.keys())) 460 | 461 | mouseUp_MID2eventMessage = { 462 | 1001:514, 463 | 1002:517, 464 | 1003:520} 465 | mouseUp_eventMessage2MID = dict(map(lambda x,y: (x,y),mouseUp_MID2eventMessage.values(),mouseUp_MID2eventMessage.keys())) 466 | 467 | return mouseDown_MID2eventMessage, mouseDown_eventMessage2MID, mouseUp_MID2eventMessage, mouseUp_eventMessage2MID 468 | 469 | def createSingleEventMouse(self): 470 | """Store events that get executed on single event like wheel up 471 | MID event.Message pyhk hotkey comments 472 | 1000 512 mouse move 473 | 1004 522 mouse wheel up event.Wheel = 1 474 | 1005 522 mouse wheel up event.Wheel = 1""" 475 | 476 | singleEventMouseMessage = [512, 522] 477 | singleEventMID = [1000,1004,1005] 478 | 479 | return singleEventMouseMessage, singleEventMID 480 | 481 | def createMergeKeys(self): 482 | """Merge two keys into one 483 | KeyID MEID MergeHumanHotkey 484 | 162 1010 Ctrl (Lcontrol) 485 | 163 1010 Ctrl (Rcontrol 486 | 164 1011 Alt (Lmenu) 487 | 165 1011 Alt (Rmenu) 488 | 160 1012 Shift (Lshift) 489 | 161 1012 Shift (Rshift) 490 | 91 1013 Win (Lwin) 491 | 92 1013 Win (Rwin)""" 492 | 493 | KeyID2MEID = {162:1010, 494 | 163:1010, 495 | 164:1011, 496 | 165:1011, 497 | 160:1012, 498 | 161:1012, 499 | 91:1013, 500 | 92:1013} 501 | return KeyID2MEID 502 | 503 | def getHotkeyListNoSingleNoModifiers(self): 504 | """return a list of all hotkeys without single events and modifiers""" 505 | TempID2Key = self.ID2Key.copy() 506 | 507 | #get rid of single events and modifiers 508 | getRid = [91,92,160,161,162,163,164,165,1000,1004,1005,1010,1011,1012,1013] 509 | 510 | #get rid of Lwin and oems 511 | moreRid = [186,187,188,189,190,191,192,219,220,221,222] 512 | 513 | for item in moreRid: 514 | getRid.append(item) 515 | 516 | for gR in getRid: 517 | TempID2Key.pop(gR) 518 | 519 | LTempID2Key = TempID2Key.values() 520 | 521 | return LTempID2Key 522 | 523 | 524 | #permutation functions needed for merged hotkeys 525 | def UniquePermutation2(l1,l2): 526 | """"Return UP of two lists""" 527 | ltemp =[] 528 | for x1 in l1: 529 | for x2 in l2: 530 | ltemp.append([x1,x2]) 531 | 532 | return ltemp 533 | 534 | def UniquePermutation(li): 535 | """Return UP of a general list""" 536 | lcurrent = li[0] 537 | depth = 0 538 | for xl in li[1:]: 539 | lcurrenttemp = list() 540 | lcurrenttemp = UniquePermutation2(lcurrent,xl) 541 | 542 | if depth > 0: 543 | lcurrent = list() 544 | for item in lcurrenttemp: 545 | item0 = list(item[0]) 546 | item0.append(item[1]) 547 | lcurrent.append(item0) 548 | else: 549 | lcurrent = lcurrenttemp[:] 550 | depth += 1 551 | return lcurrent 552 | 553 | #class for thread 554 | class ExecFunThread: 555 | def __init__(self, fun): 556 | self.fun = fun 557 | 558 | def Start(self): 559 | self.running = True 560 | thread.start_new_thread(self.Run, ()) 561 | 562 | def IsRunning(self): 563 | return self.running 564 | 565 | def Run(self): 566 | self.fun() 567 | self.running = False 568 | 569 | if __name__ == '__main__': 570 | 571 | import win32gui 572 | 573 | #define test function 574 | def funk1(): 575 | print "Hotkey pressed: Lcontrol 7" 576 | 577 | def funM(): 578 | print "Mousy" 579 | 580 | def funW(): 581 | print "Wheel up" 582 | 583 | def funWin(): 584 | print win32gui.GetForegroundWindow() 585 | 586 | #create pyhk class instance 587 | hot = pyhk() 588 | 589 | print hot.getHotkeyListNoSingleNoModifiers() 590 | 591 | #add hotkey 592 | id1 = hot.addHotkey(['Ctrl', 'Alt','7'],funk1) 593 | id2 = hot.addHotkey(['Ctrl', 'Alt','7'],funk1) 594 | id3 = hot.addHotkey(['Ctrl', 'Alt','7'],funk1) 595 | 596 | id4 = hot.addHotkey(['7'],funWin,isThread = True, up=True) 597 | id4 = hot.addHotkey(['7'],funWin,isThread = True, up=True) 598 | 599 | #add hotkey 600 | hot.addHotkey(['mouse middle','1'],funM) 601 | 602 | #add hotkey 603 | hot.addHotkey(["mouse wheel up"],funW) 604 | 605 | #start looking for hotkey. Loop is ended with CTRL Shift Q 606 | hot.start() 607 | 608 | #add several hotkeys to the same function 609 | hot.addHotkey(['Ctrl','8'],funk1) 610 | 611 | #remove hotkey Lcontrol 7 612 | hot.removeHotkey(id = id4) 613 | 614 | #change exit hotkey, 615 | #default is CTRL Shift Q and will be used if no EndHotkey is set 616 | hot.setEndHotkey(['Alt','Q']) 617 | 618 | #start looking for hotkey 619 | hot.start() 620 | 621 | 622 | 623 | 624 | -------------------------------------------------------------------------------- /samples/basic.py: -------------------------------------------------------------------------------- 1 | import pyhk 2 | 3 | def fun(): 4 | print "Do something" 5 | 6 | #create pyhk class instance 7 | hot = pyhk.pyhk() 8 | 9 | #add hotkey 10 | hot.addHotkey(['Ctrl', 'Alt','7'],fun) 11 | 12 | #start looking for hotkey. 13 | hot.start() 14 | --------------------------------------------------------------------------------