Package lib :: Module scripting
[hide private]
[frames] | no frames]

Source Code for Module lib.scripting

  1  # -*- coding: utf-8 -*- 
  2   
  3  # Copyright (C) 2011 Chris Dekter 
  4  # 
  5  # This program is free software: you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation, either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 17   
 18  import subprocess, threading, time, re, gtk 
 19  #import common, model, iomediator 
 20   
21 -class Keyboard:
22 """ 23 Provides access to the keyboard for event generation. 24 """ 25
26 - def __init__(self, mediator):
27 self.mediator = mediator
28
29 - def send_keys(self, keyString):
30 """ 31 Send a sequence of keys via keyboard events 32 33 Usage: C{keyboard.send_keys(keyString)} 34 35 @param keyString: string of keys (including special keys) to send 36 """ 37 self.mediator.interface.begin_send() 38 self.mediator.send_string(keyString.decode("utf-8")) 39 self.mediator.interface.finish_send()
40
41 - def send_key(self, key, repeat=1):
42 """ 43 Send a keyboard event 44 45 Usage: C{keyboard.send_key(key, repeat=1)} 46 47 @param key: they key to be sent (e.g. "s" or "<enter>") 48 @param repeat: number of times to repeat the key event 49 """ 50 for x in xrange(repeat): 51 self.mediator.send_key(key.decode("utf-8")) 52 self.mediator.flush()
53
54 - def press_key(self, key):
55 """ 56 Send a key down event 57 58 Usage: C{keyboard.press_key(key)} 59 60 The key will be treated as down until a matching release_key() is sent. 61 @param key: they key to be pressed (e.g. "s" or "<enter>") 62 """ 63 self.mediator.press_key(key.decode("utf-8"))
64
65 - def release_key(self, key):
66 """ 67 Send a key up event 68 69 Usage: C{keyboard.release_key(key)} 70 71 If the specified key was not made down using press_key(), the event will be 72 ignored. 73 @param key: they key to be released (e.g. "s" or "<enter>") 74 """ 75 self.mediator.release_key(key.decode("utf-8"))
76
77 - def fake_keypress(self, key, repeat=1):
78 """ 79 Fake a keypress 80 81 Usage: C{keyboard.fake_keypress(key, repeat=1)} 82 83 Uses XTest to 'fake' a keypress. This is useful to send keypresses to some 84 applications which won't respond to keyboard.send_key() 85 86 @param key: they key to be sent (e.g. "s" or "<enter>") 87 @param repeat: number of times to repeat the key event 88 """ 89 for x in xrange(repeat): 90 self.mediator.fake_keypress(key.decode("utf-8"))
91
92 - def wait_for_keypress(self, key, modifiers=[], timeOut=10.0):
93 """ 94 Wait for a keypress or key combination 95 96 Note: this function cannot be used to wait for modifier keys on their own 97 98 Usage: C{keyboard.wait_for_keypress(self, key, modifiers=[], timeOut=10.0)} 99 100 @param key: they key to wait for 101 @param modifiers: list of modifiers that should be pressed with the key 102 @param timeOut: maximum time, in seconds, to wait for the keypress to occur 103 """ 104 w = iomediator.Waiter(key, modifiers, None, timeOut) 105 w.wait()
106 107
108 -class Mouse:
109 """ 110 Provides access to send mouse clicks 111 """
112 - def __init__(self, mediator):
113 self.mediator = mediator
114
115 - def click_relative(self, x, y, button):
116 """ 117 Send a mouse click relative to the active window 118 119 Usage: C{mouse.click_relative(x, y, button)} 120 121 @param x: x-coordinate in pixels, relative to upper left corner of window 122 @param y: y-coordinate in pixels, relative to upper left corner of window 123 @param button: mouse button to simulate (left=1, middle=2, right=3) 124 """ 125 self.mediator.send_mouse_click(x, y, button, True)
126
127 - def click_relative_self(self, x, y, button):
128 """ 129 Send a mouse click relative to the current mouse position 130 131 Usage: C{mouse.click_relative_self(x, y, button)} 132 133 @param x: x-offset in pixels, relative to current mouse position 134 @param y: y-offset in pixels, relative to current mouse position 135 @param button: mouse button to simulate (left=1, middle=2, right=3) 136 """ 137 self.mediator.send_mouse_click_relative(x, y, button)
138
139 - def click_absolute(self, x, y, button):
140 """ 141 Send a mouse click relative to the screen (absolute) 142 143 Usage: C{mouse.click_absolute(x, y, button)} 144 145 @param x: x-coordinate in pixels, relative to upper left corner of window 146 @param y: y-coordinate in pixels, relative to upper left corner of window 147 @param button: mouse button to simulate (left=1, middle=2, right=3) 148 """ 149 self.mediator.send_mouse_click(x, y, button, False)
150
151 - def wait_for_click(self, button, timeOut=10.0):
152 """ 153 Wait for a mouse click 154 155 Usage: C{mouse.wait_for_click(self, button, timeOut=10.0)} 156 157 @param key: they mouse button click to wait for as a button number, 1-9 158 @param timeOut: maximum time, in seconds, to wait for the keypress to occur 159 """ 160 button = int(button) 161 w = iomediator.Waiter(None, None, button, timeOut) 162 w.wait()
163 164
165 -class Store(dict):
166 """ 167 Allows persistent storage of values between invocations of the script. 168 """ 169
170 - def set_value(self, key, value):
171 """ 172 Store a value 173 174 Usage: C{store.set_value(key, value)} 175 """ 176 self[key] = value
177
178 - def get_value(self, key):
179 """ 180 Get a value 181 182 Usage: C{store.get_value(key)} 183 """ 184 return self[key]
185
186 - def remove_value(self, key):
187 """ 188 Remove a value 189 190 Usage: C{store.remove_value(key)} 191 """ 192 del self[key]
193 194
195 -class System:
196 """ 197 Simplified access to some system commands. 198 """ 199
200 - def exec_command(self, command, getOutput=True):
201 """ 202 Execute a shell command 203 204 Set getOutput to False if the command does not exit and return immediately. Otherwise 205 AutoKey will not respond to any hotkeys/abbreviations etc until the process started 206 by the command exits. 207 208 Usage: C{system.exec_command(command, getOutput=True)} 209 210 @param command: command to be executed (including any arguments) - e.g. "ls -l" 211 @param getOutput: whether to capture the (stdout) output of the command 212 @raise subprocess.CalledProcessError: if the command returns a non-zero exit code 213 """ 214 if getOutput: 215 p = subprocess.Popen(command, shell=True, bufsize=-1, stdout=subprocess.PIPE) 216 retCode = p.wait() 217 output = p.stdout.read()[:-1] 218 if retCode != 0: 219 raise subprocess.CalledProcessError(retCode, output) 220 else: 221 return output 222 else: 223 subprocess.Popen(command, shell=True, bufsize=-1)
224
225 - def create_file(self, fileName, contents=""):
226 """ 227 Create a file with contents 228 229 Usage: C{system.create_file(fileName, contents="")} 230 231 @param fileName: full path to the file to be created 232 @param contents: contents to insert into the file 233 """ 234 f = open(fileName, "w") 235 f.write(contents) 236 f.close()
237 238
239 -class GtkDialog:
240 """ 241 Provides a simple interface for the display of some basic dialogs to collect information from the user. 242 243 This version uses Zenity to integrate well with GNOME. To pass additional arguments to Zenity that are 244 not specifically handled, use keyword arguments. For example, to pass the --timeout argument to Zenity 245 pass C{timeout="15"} as one of the parameters. All keyword arguments must be given as strings. 246 247 A note on exit codes: an exit code of 0 indicates that the user clicked OK. 248 """ 249
250 - def __runZenity(self, title, args, kwargs):
251 for k, v in kwargs.iteritems(): 252 args.append("--" + k) 253 args.append(v) 254 255 p = subprocess.Popen(["zenity", "--title", title] + args, stdout=subprocess.PIPE) 256 retCode = p.wait() 257 output = p.stdout.read()[:-1] # Drop trailing newline 258 259 return (retCode, output)
260
261 - def info_dialog(self, title="Information", message="", **kwargs):
262 """ 263 Show an information dialog 264 265 Usage: C{dialog.info_dialog(title="Information", message="", **kwargs)} 266 267 @param title: window title for the dialog 268 @param message: message displayed in the dialog 269 @return: a tuple containing the exit code and user input 270 @rtype: C{tuple(int, str)} 271 """ 272 return self.__runZenity(title, ["--info", "--text", message], kwargs)
273
274 - def input_dialog(self, title="Enter a value", message="Enter a value", default="", **kwargs):
275 """ 276 Show an input dialog 277 278 Usage: C{dialog.input_dialog(title="Enter a value", message="Enter a value", default="", **kwargs)} 279 280 @param title: window title for the dialog 281 @param message: message displayed above the input box 282 @param default: default value for the input box 283 @return: a tuple containing the exit code and user input 284 @rtype: C{tuple(int, str)} 285 """ 286 return self.__runZenity(title, ["--entry", "--text", message, "--entry-text", default], kwargs)
287
288 - def password_dialog(self, title="Enter password", message="Enter password", **kwargs):
289 """ 290 Show a password input dialog 291 292 Usage: C{dialog.password_dialog(title="Enter password", message="Enter password")} 293 294 @param title: window title for the dialog 295 @param message: message displayed above the password input box 296 @return: a tuple containing the exit code and user input 297 @rtype: C{tuple(int, str)} 298 """ 299 return self.__runZenity(title, ["--entry", "--text", message, "--hide-text"], kwargs) 300 301 #def combo_menu(self, options, title="Choose an option", message="Choose an option"): 302 """ 303 Show a combobox menu - not supported by zenity 304 305 Usage: C{dialog.combo_menu(options, title="Choose an option", message="Choose an option")} 306 307 @param options: list of options (strings) for the dialog 308 @param title: window title for the dialog 309 @param message: message displayed above the combobox 310 """
311 #return self.__runZenity(title, ["--combobox", message] + options) 312
313 - def list_menu(self, options, title="Choose a value", message="Choose a value", default=None, **kwargs):
314 """ 315 Show a single-selection list menu 316 317 Usage: C{dialog.list_menu(options, title="Choose a value", message="Choose a value", default=None, **kwargs)} 318 319 @param options: list of options (strings) for the dialog 320 @param title: window title for the dialog 321 @param message: message displayed above the list 322 @param default: default value to be selected 323 @return: a tuple containing the exit code and user choice 324 @rtype: C{tuple(int, str)} 325 """ 326 327 choices = [] 328 #optionNum = 0 329 for option in options: 330 if option == default: 331 choices.append("TRUE") 332 else: 333 choices.append("FALSE") 334 335 #choices.append(str(optionNum)) 336 choices.append(option) 337 #optionNum += 1 338 339 return self.__runZenity(title, ["--list", "--radiolist", "--text", message, "--column", " ", "--column", "Options"] + choices, kwargs)
340 341 #return retCode, choice 342
343 - def list_menu_multi(self, options, title="Choose one or more values", message="Choose one or more values", defaults=[], **kwargs):
344 """ 345 Show a multiple-selection list menu 346 347 Usage: C{dialog.list_menu_multi(options, title="Choose one or more values", message="Choose one or more values", defaults=[], **kwargs)} 348 349 @param options: list of options (strings) for the dialog 350 @param title: window title for the dialog 351 @param message: message displayed above the list 352 @param defaults: list of default values to be selected 353 @return: a tuple containing the exit code and user choice 354 @rtype: C{tuple(int, str)} 355 """ 356 357 choices = [] 358 #optionNum = 0 359 for option in options: 360 if option in defaults: 361 choices.append("TRUE") 362 else: 363 choices.append("FALSE") 364 365 #choices.append(str(optionNum)) 366 choices.append(option) 367 #optionNum += 1 368 369 retCode, output = self.__runZenity(title, ["--list", "--checklist", "--text", message, "--column", " ", "--column", "Options"] + choices, kwargs) 370 results = output.split('|') 371 372 #choices = [] 373 #for choice in results: 374 # choices.append(choice) 375 376 return retCode, results
377
378 - def open_file(self, title="Open File", **kwargs):
379 """ 380 Show an Open File dialog 381 382 Usage: C{dialog.open_file(title="Open File", **kwargs)} 383 384 @param title: window title for the dialog 385 @return: a tuple containing the exit code and file path 386 @rtype: C{tuple(int, str)} 387 """ 388 #if rememberAs is not None: 389 # return self.__runZenity(title, ["--getopenfilename", initialDir, fileTypes, ":" + rememberAs]) 390 #else: 391 return self.__runZenity(title, ["--file-selection"], kwargs)
392
393 - def save_file(self, title="Save As", **kwargs):
394 """ 395 Show a Save As dialog 396 397 Usage: C{dialog.save_file(title="Save As", **kwargs)} 398 399 @param title: window title for the dialog 400 @return: a tuple containing the exit code and file path 401 @rtype: C{tuple(int, str)} 402 """ 403 #if rememberAs is not None: 404 # return self.__runZenity(title, ["--getsavefilename", initialDir, fileTypes, ":" + rememberAs]) 405 #else: 406 return self.__runZenity(title, ["--file-selection", "--save"], kwargs)
407
408 - def choose_directory(self, title="Select Directory", initialDir="~", **kwargs):
409 """ 410 Show a Directory Chooser dialog 411 412 Usage: C{dialog.choose_directory(title="Select Directory", **kwargs)} 413 414 @param title: window title for the dialog 415 @return: a tuple containing the exit code and path 416 @rtype: C{tuple(int, str)} 417 """ 418 #if rememberAs is not None: 419 # return self.__runZenity(title, ["--getexistingdirectory", initialDir, ":" + rememberAs]) 420 #else: 421 return self.__runZenity(title, ["--file-selection", "--directory"], kwargs) 422 423 #def choose_colour(self, title="Select Colour"): 424 """ 425 Show a Colour Chooser dialog - not supported by zenity 426 427 Usage: C{dialog.choose_colour(title="Select Colour")} 428 429 @param title: window title for the dialog 430 """
431 #return self.__runZenity(title, ["--getcolor"]) 432
433 - def calendar(self, title="Choose a date", format="%Y-%m-%d", date="today", **kwargs):
434 """ 435 Show a calendar dialog 436 437 Usage: C{dialog.calendar_dialog(title="Choose a date", format="%Y-%m-%d", date="YYYY-MM-DD", **kwargs)} 438 439 @param title: window title for the dialog 440 @param format: format of date to be returned 441 @param date: initial date as YYYY-MM-DD, otherwise today 442 @return: a tuple containing the exit code and date 443 @rtype: C{tuple(int, str)} 444 """ 445 if re.match(r"[0-9]{4}-[0-9]{2}-[0-9]{2}", date): 446 year = date[0:4] 447 month = date[5:7] 448 day = date[8:10] 449 date_args = ["--year=" + year, "--month=" + month, "--day=" + day] 450 else: 451 date_args = [] 452 return self.__runZenity(title, ["--calendar", "--date-format=" + format] + date_args, kwargs)
453 454
455 -class GtkClipboard:
456 """ 457 Read/write access to the X selection and clipboard - GTK version 458 """ 459
460 - def __init__(self, app):
461 self.clipBoard = gtk.Clipboard() 462 self.selection = gtk.Clipboard(selection="PRIMARY") 463 self.app = app
464
465 - def fill_selection(self, contents):
466 """ 467 Copy text into the X selection 468 469 Usage: C{clipboard.fill_selection(contents)} 470 471 @param contents: string to be placed in the selection 472 """ 473 #self.__execAsync(self.__fillSelection, contents) 474 self.__fillSelection(contents)
475
476 - def __fillSelection(self, string):
477 gtk.gdk.threads_enter() 478 self.selection.set_text(string.encode("utf-8")) 479 gtk.gdk.threads_leave()
480 #self.sem.release() 481
482 - def get_selection(self):
483 """ 484 Read text from the X selection 485 486 Usage: C{clipboard.get_selection()} 487 488 @return: text contents of the mouse selection 489 @rtype: C{str} 490 @raise Exception: if no text was found in the selection 491 """ 492 self.__execAsync(self.selection.request_text, self.__receive) 493 if self.text is not None: 494 return self.text.decode("utf-8") 495 else: 496 raise Exception("No text found in X selection")
497
498 - def __receive(self, cb, text, data=None):
499 self.text = text 500 self.sem.release()
501
502 - def fill_clipboard(self, contents):
503 """ 504 Copy text into the clipboard 505 506 Usage: C{clipboard.fill_clipboard(contents)} 507 508 @param contents: string to be placed in the selection 509 """ 510 self.__fillClipboard(contents)
511
512 - def __fillClipboard(self, string):
513 gtk.gdk.threads_enter() 514 self.clipBoard.set_text(string.encode("utf-8")) 515 gtk.gdk.threads_leave()
516 #self.sem.release() 517
518 - def get_clipboard(self):
519 """ 520 Read text from the clipboard 521 522 Usage: C{clipboard.get_clipboard()} 523 524 @return: text contents of the clipboard 525 @rtype: C{str} 526 @raise Exception: if no text was found on the clipboard 527 """ 528 self.__execAsync(self.clipBoard.request_text, self.__receive) 529 if self.text is not None: 530 return self.text.decode("utf-8") 531 else: 532 raise Exception("No text found on clipboard")
533
534 - def __execAsync(self, callback, *args):
535 self.sem = threading.Semaphore(0) 536 gtk.gdk.threads_enter() 537 callback(*args) 538 gtk.gdk.threads_leave() 539 self.sem.acquire()
540 541
542 -class Window:
543 """ 544 Basic window management using wmctrl 545 546 Note: in all cases where a window title is required (with the exception of wait_for_focus()), 547 two special values of window title are permitted: 548 549 :ACTIVE: - select the currently active window 550 :SELECT: - select the desired window by clicking on it 551 """ 552
553 - def __init__(self, mediator):
554 self.mediator = mediator
555
556 - def wait_for_focus(self, title, timeOut=5):
557 """ 558 Wait for window with the given title to have focus 559 560 Usage: C{window.wait_for_focus(title, timeOut=5)} 561 562 If the window becomes active, returns True. Otherwise, returns False if 563 the window has not become active by the time the timeout has elapsed. 564 565 @param title: title to match against (as a regular expression) 566 @param timeOut: period (seconds) to wait before giving up 567 @rtype: boolean 568 """ 569 regex = re.compile(title) 570 waited = 0 571 while waited <= timeOut: 572 if regex.match(self.mediator.interface.get_window_title()): 573 return True 574 575 if timeOut == 0: 576 break # zero length timeout, if not matched go straight to end 577 578 time.sleep(0.3) 579 waited += 0.3 580 581 return False
582
583 - def wait_for_exist(self, title, timeOut=5):
584 """ 585 Wait for window with the given title to be created 586 587 Usage: C{window.wait_for_exist(title, timeOut=5)} 588 589 If the window is in existence, returns True. Otherwise, returns False if 590 the window has not been created by the time the timeout has elapsed. 591 592 @param title: title to match against (as a regular expression) 593 @param timeOut: period (seconds) to wait before giving up 594 @rtype: boolean 595 """ 596 regex = re.compile(title) 597 waited = 0 598 while waited <= timeOut: 599 retCode, output = self.__runWmctrl(["-l"]) 600 for line in output.split('\n'): 601 if regex.match(line[14:].split(' ', 1)[-1]): 602 return True 603 604 if timeOut == 0: 605 break # zero length timeout, if not matched go straight to end 606 607 time.sleep(0.3) 608 waited += 0.3 609 610 return False
611
612 - def activate(self, title, switchDesktop=False, matchClass=False):
613 """ 614 Activate the specified window, giving it input focus 615 616 Usage: C{window.activate(title, switchDesktop=False, matchClass=False)} 617 618 If switchDesktop is False (default), the window will be moved to the current desktop 619 and activated. Otherwise, switch to the window's current desktop and activate it there. 620 621 @param title: window title to match against (as case-insensitive substring match) 622 @param switchDesktop: whether or not to switch to the window's current desktop 623 @param matchClass: if True, match on the window class instead of the title 624 """ 625 if switchDesktop: 626 args = ["-a", title] 627 else: 628 args = ["-R", title] 629 if matchClass: 630 args += ["-x"] 631 self.__runWmctrl(args)
632
633 - def close(self, title, matchClass=False):
634 """ 635 Close the specified window gracefully 636 637 Usage: C{window.close(title, matchClass=False)} 638 639 @param title: window title to match against (as case-insensitive substring match) 640 @param matchClass: if True, match on the window class instead of the title 641 """ 642 if matchClass: 643 self.__runWmctrl(["-c", title, "-x"]) 644 else: 645 self.__runWmctrl(["-c", title])
646
647 - def resize_move(self, title, xOrigin=-1, yOrigin=-1, width=-1, height=-1, matchClass=False):
648 """ 649 Resize and/or move the specified window 650 651 Usage: C{window.close(title, xOrigin=-1, yOrigin=-1, width=-1, height=-1, matchClass=False)} 652 653 Leaving and of the position/dimension values as the default (-1) will cause that 654 value to be left unmodified. 655 656 @param title: window title to match against (as case-insensitive substring match) 657 @param xOrigin: new x origin of the window (upper left corner) 658 @param yOrigin: new y origin of the window (upper left corner) 659 @param width: new width of the window 660 @param height: new height of the window 661 @param matchClass: if True, match on the window class instead of the title 662 """ 663 mvArgs = ["0", str(xOrigin), str(yOrigin), str(width), str(height)] 664 if matchClass: 665 xArgs = ["-x"] 666 else: 667 xArgs = [] 668 self.__runWmctrl(["-r", title, "-e", ','.join(mvArgs)] + xArgs)
669 670
671 - def move_to_desktop(self, title, deskNum, matchClass=False):
672 """ 673 Move the specified window to the given desktop 674 675 Usage: C{window.move_to_desktop(title, deskNum, matchClass=False)} 676 677 @param title: window title to match against (as case-insensitive substring match) 678 @param deskNum: desktop to move the window to (note: zero based) 679 @param matchClass: if True, match on the window class instead of the title 680 """ 681 if matchClass: 682 xArgs = ["-x"] 683 else: 684 xArgs = [] 685 self.__runWmctrl(["-r", title, "-t", str(deskNum)] + xArgs)
686 687
688 - def switch_desktop(self, deskNum):
689 """ 690 Switch to the specified desktop 691 692 Usage: C{window.switch_desktop(deskNum)} 693 694 @param deskNum: desktop to switch to (note: zero based) 695 """ 696 self.__runWmctrl(["-s", str(deskNum)])
697
698 - def set_property(self, title, action, prop, matchClass=False):
699 """ 700 Set a property on the given window using the specified action 701 702 Usage: C{window.set_property(title, action, prop, matchClass=False)} 703 704 Allowable actions: C{add, remove, toggle} 705 Allowable properties: C{modal, sticky, maximized_vert, maximized_horz, shaded, skip_taskbar, 706 skip_pager, hidden, fullscreen, above} 707 708 @param title: window title to match against (as case-insensitive substring match) 709 @param action: one of the actions listed above 710 @param prop: one of the properties listed above 711 @param matchClass: if True, match on the window class instead of the title 712 """ 713 if matchClass: 714 xArgs = ["-x"] 715 else: 716 xArgs = [] 717 self.__runWmctrl(["-r", title, "-b" + action + ',' + prop] + xArgs)
718
719 - def get_active_geometry(self):
720 """ 721 Get the geometry of the currently active window 722 723 Usage: C{window.get_active_geometry()} 724 725 @return: a 4-tuple containing the x-origin, y-origin, width and height of the window (in pixels) 726 @rtype: C{tuple(int, int, int, int)} 727 """ 728 active = self.mediator.interface.get_window_title() 729 result, output = self.__runWmctrl(["-l", "-G"]) 730 matchingLine = None 731 for line in output.split('\n'): 732 if active in line[34:].split(' ', 1)[-1]: 733 matchingLine = line 734 735 if matchingLine is not None: 736 output = matchingLine.split()[2:6] 737 return map(int, output) 738 else: 739 return None
740
741 - def get_active_title(self):
742 """ 743 Get the visible title of the currently active window 744 745 Usage: C{window.get_active_title()} 746 747 @return: the visible title of the currentle active window 748 @rtype: C{str} 749 """ 750 return self.mediator.interface.get_window_title()
751
752 - def get_active_class(self):
753 """ 754 Get the class of the currently active window 755 756 Usage: C{window.get_active_class()} 757 758 @return: the class of the currentle active window 759 @rtype: C{str} 760 """ 761 return self.mediator.interface.get_window_class()
762
763 - def __runWmctrl(self, args):
764 p = subprocess.Popen(["wmctrl"] + args, stdout=subprocess.PIPE) 765 retCode = p.wait() 766 output = p.stdout.read()[:-1] # Drop trailing newline 767 768 return (retCode, output)
769 770
771 -class Engine:
772 """ 773 Provides access to the internals of AutoKey. 774 775 Note that any configuration changes made using this API while the configuration window 776 is open will not appear until it is closed and re-opened. 777 """ 778
779 - def __init__(self, configManager, runner):
780 self.configManager = configManager 781 self.runner = runner 782 self.monitor = configManager.app.monitor 783 self.__returnValue = ''
784
785 - def get_folder(self, title):
786 """ 787 Retrieve a folder by its title 788 789 Usage: C{engine.get_folder(title)} 790 791 Note that if more than one folder has the same title, only the first match will be 792 returned. 793 """ 794 for folder in self.configManager.allFolders: 795 if folder.title == title: 796 return folder 797 return None
798
799 - def create_phrase(self, folder, description, contents):
800 """ 801 Create a text phrase 802 803 Usage: C{engine.create_phrase(folder, description, contents)} 804 805 A new phrase with no abbreviation or hotkey is created in the specified folder 806 807 @param folder: folder to place the abbreviation in, retrieved using C{engine.get_folder()} 808 @param description: description for the phrase 809 @param contents: the expansion text 810 """ 811 self.monitor.suspend() 812 p = model.Phrase(description, contents) 813 folder.add_item(p) 814 p.persist() 815 self.monitor.unsuspend() 816 self.configManager.config_altered(False)
817
818 - def create_abbreviation(self, folder, description, abbr, contents):
819 """ 820 Create a text abbreviation 821 822 Usage: C{engine.create_abbreviation(folder, description, abbr, contents)} 823 824 When the given abbreviation is typed, it will be replaced with the given 825 text. 826 827 @param folder: folder to place the abbreviation in, retrieved using C{engine.get_folder()} 828 @param description: description for the phrase 829 @param abbr: the abbreviation that will trigger the expansion 830 @param contents: the expansion text 831 @raise Exception: if the specified abbreviation is not unique 832 """ 833 if not self.configManager.check_abbreviation_unique(abbr, None): 834 raise Exception("The specified abbreviation is already in use") 835 836 self.monitor.suspend() 837 p = model.Phrase(description, contents) 838 p.modes.append(model.TriggerMode.ABBREVIATION) 839 p.abbreviation = abbr 840 folder.add_item(p) 841 p.persist() 842 self.monitor.unsuspend() 843 self.configManager.config_altered(False)
844
845 - def create_hotkey(self, folder, description, modifiers, key, contents):
846 """ 847 Create a text hotkey. 848 849 Usage: C{engine.create_hotkey(folder, description, modifiers, key, contents)} 850 851 When the given hotkey is pressed, it will be replaced with the given 852 text. Modifiers must be given as a list of strings, with the following 853 values permitted: 854 855 <ctrl> 856 <alt> 857 <super> 858 <shift> 859 860 The key must be an unshifted character (i.e. lowercase) 861 862 @param folder: folder to place the abbreviation in, retrieved using C{engine.get_folder()} 863 @param description: description for the phrase 864 @param modifiers: modifiers to use with the hotkey (as a list) 865 @param key: the hotkey 866 @param contents: the expansion text 867 @raise Exception: if the specified hotkey is not unique 868 """ 869 modifiers.sort() 870 if not self.configManager.check_hotkey_unique(modifiers, key, None): 871 raise Exception("The specified hotkey and modifier combination is already in use") 872 873 self.monitor.suspend() 874 p = model.Phrase(description, contents) 875 p.modes.append(model.TriggerMode.HOTKEY) 876 p.set_hotkey(modifiers, key) 877 folder.add_item(p) 878 p.persist() 879 self.monitor.unsuspend() 880 self.configManager.config_altered(False)
881
882 - def run_script(self, description):
883 """ 884 Run an existing script using its description to look it up 885 886 Usage: C{engine.run_script(description)} 887 888 @param description: description of the script to run 889 @raise Exception: if the specified script does not exist 890 """ 891 targetScript = None 892 for item in self.configManager.allItems: 893 if item.description == description and isinstance(item, model.Script): 894 targetScript = item 895 896 if targetScript is not None: 897 self.runner.execute(targetScript, "") 898 else: 899 raise Exception("No script with description '%s' found" % description)
900
901 - def run_script_from_macro(self, args):
902 """ 903 Used internally by AutoKey for phrase macros 904 """ 905 if len(args) > 1: 906 self.__macroArgs = args[1:] 907 else: 908 self.__macroArgs = [] 909 910 try: 911 self.run_script(args[0]) 912 except Exception, e: 913 self.set_return_value("{ERROR: %s}" % str(e))
914
915 - def get_macro_arguments(self):
916 """ 917 Get the arguments supplied to the current script via its macro. 918 919 @return: the arguments 920 @rtype: C{list(str())} 921 """ 922 return self.__macroArgs
923
924 - def set_return_value(self, val):
925 """ 926 Store a return value to be used by a phrase macro 927 928 Usage: C{engine.set_return_value(val)} 929 930 @param val: value to be stored 931 """ 932 self.__returnValue = val
933
934 - def get_return_value(self):
935 """ 936 Used internally by AutoKey for phrase macros 937 """ 938 ret = self.__returnValue 939 self.__returnValue = '' 940 return ret
941 942 #import iomediator 943