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 
  19  import common#, model, iomediator 
  20  if common.USING_QT: 
  21      from PyQt4.QtGui import QClipboard, QApplication 
  22  else: 
  23      import gtk 
  24   
25 -class Keyboard:
26 """ 27 Provides access to the keyboard for event generation. 28 """ 29
30 - def __init__(self, mediator):
31 self.mediator = mediator
32
33 - def send_keys(self, keyString):
34 """ 35 Send a sequence of keys via keyboard events 36 37 Usage: C{keyboard.send_keys(keyString)} 38 39 @param keyString: string of keys (including special keys) to send 40 """ 41 self.mediator.interface.begin_send() 42 self.mediator.send_string(keyString.decode("utf-8")) 43 self.mediator.interface.finish_send()
44
45 - def send_key(self, key, repeat=1):
46 """ 47 Send a keyboard event 48 49 Usage: C{keyboard.send_key(key, repeat=1)} 50 51 @param key: they key to be sent (e.g. "s" or "<enter>") 52 @param repeat: number of times to repeat the key event 53 """ 54 for x in xrange(repeat): 55 self.mediator.send_key(key.decode("utf-8")) 56 self.mediator.flush()
57
58 - def press_key(self, key):
59 """ 60 Send a key down event 61 62 Usage: C{keyboard.press_key(key)} 63 64 The key will be treated as down until a matching release_key() is sent. 65 @param key: they key to be pressed (e.g. "s" or "<enter>") 66 """ 67 self.mediator.press_key(key.decode("utf-8"))
68
69 - def release_key(self, key):
70 """ 71 Send a key up event 72 73 Usage: C{keyboard.release_key(key)} 74 75 If the specified key was not made down using press_key(), the event will be 76 ignored. 77 @param key: they key to be released (e.g. "s" or "<enter>") 78 """ 79 self.mediator.release_key(key.decode("utf-8"))
80
81 - def fake_keypress(self, key, repeat=1):
82 """ 83 Fake a keypress 84 85 Usage: C{keyboard.fake_keypress(key, repeat=1)} 86 87 Uses XTest to 'fake' a keypress. This is useful to send keypresses to some 88 applications which won't respond to keyboard.send_key() 89 90 @param key: they key to be sent (e.g. "s" or "<enter>") 91 @param repeat: number of times to repeat the key event 92 """ 93 for x in xrange(repeat): 94 self.mediator.fake_keypress(key.decode("utf-8"))
95
96 - def wait_for_keypress(self, key, modifiers=[], timeOut=10.0):
97 """ 98 Wait for a keypress or key combination 99 100 Usage: C{keyboard.wait_for_keypress(self, key, modifiers=[], timeOut=10.0)} 101 102 Note: this function cannot be used to wait for modifier keys on their own 103 104 @param key: they key to wait for 105 @param modifiers: list of modifiers that should be pressed with the key 106 @param timeOut: maximum time, in seconds, to wait for the keypress to occur 107 """ 108 w = iomediator.Waiter(key, modifiers, None, timeOut) 109 w.wait()
110 111
112 -class Mouse:
113 """ 114 Provides access to send mouse clicks 115 """
116 - def __init__(self, mediator):
117 self.mediator = mediator
118
119 - def click_relative(self, x, y, button):
120 """ 121 Send a mouse click relative to the active window 122 123 Usage: C{mouse.click_relative(x, y, button)} 124 125 @param x: x-coordinate in pixels, relative to upper left corner of window 126 @param y: y-coordinate in pixels, relative to upper left corner of window 127 @param button: mouse button to simulate (left=1, middle=2, right=3) 128 """ 129 self.mediator.send_mouse_click(x, y, button, True)
130
131 - def click_relative_self(self, x, y, button):
132 """ 133 Send a mouse click relative to the current mouse position 134 135 Usage: C{mouse.click_relative_self(x, y, button)} 136 137 @param x: x-offset in pixels, relative to current mouse position 138 @param y: y-offset in pixels, relative to current mouse position 139 @param button: mouse button to simulate (left=1, middle=2, right=3) 140 """ 141 self.mediator.send_mouse_click_relative(x, y, button)
142
143 - def click_absolute(self, x, y, button):
144 """ 145 Send a mouse click relative to the screen (absolute) 146 147 Usage: C{mouse.click_absolute(x, y, button)} 148 149 @param x: x-coordinate in pixels, relative to upper left corner of window 150 @param y: y-coordinate in pixels, relative to upper left corner of window 151 @param button: mouse button to simulate (left=1, middle=2, right=3) 152 """ 153 self.mediator.send_mouse_click(x, y, button, False)
154
155 - def wait_for_click(self, button, timeOut=10.0):
156 """ 157 Wait for a mouse click 158 159 Usage: C{mouse.wait_for_click(self, button, timeOut=10.0)} 160 161 @param button: they mouse button click to wait for as a button number, 1-9 162 @param timeOut: maximum time, in seconds, to wait for the keypress to occur 163 """ 164 button = int(button) 165 w = iomediator.Waiter(None, None, button, timeOut) 166 w.wait()
167 168
169 -class Store(dict):
170 """ 171 Allows persistent storage of values between invocations of the script. 172 """ 173
174 - def set_value(self, key, value):
175 """ 176 Store a value 177 178 Usage: C{store.set_value(key, value)} 179 """ 180 self[key] = value
181
182 - def get_value(self, key):
183 """ 184 Get a value 185 186 Usage: C{store.get_value(key)} 187 """ 188 return self[key]
189
190 - def remove_value(self, key):
191 """ 192 Remove a value 193 194 Usage: C{store.remove_value(key)} 195 """ 196 del self[key]
197 198
199 -class QtDialog:
200 """ 201 Provides a simple interface for the display of some basic dialogs to collect information from the user. 202 203 This version uses KDialog to integrate well with KDE. To pass additional arguments to KDialog that are 204 not specifically handled, use keyword arguments. For example, to pass the --geometry argument to KDialog 205 to specify the desired size of the dialog, pass C{geometry="700x400"} as one of the parameters. All 206 keyword arguments must be given as strings. 207 208 A note on exit codes: an exit code of 0 indicates that the user clicked OK. 209 """ 210
211 - def __runKdialog(self, title, args, kwargs):
212 for k, v in kwargs.iteritems(): 213 args.append("--" + k) 214 args.append(v) 215 216 p = subprocess.Popen(["kdialog", "--title", title] + args, stdout=subprocess.PIPE) 217 retCode = p.wait() 218 output = p.stdout.read()[:-1] # Drop trailing newline 219 220 return (retCode, output)
221
222 - def info_dialog(self, title="Information", message="", **kwargs):
223 """ 224 Show an information dialog 225 226 Usage: C{dialog.info_dialog(title="Information", message="", **kwargs)} 227 228 @param title: window title for the dialog 229 @param message: message displayed in the dialog 230 @return: a tuple containing the exit code and user input 231 @rtype: C{tuple(int, str)} 232 """ 233 return self.__runKdialog(title, ["--msgbox", message], kwargs)
234
235 - def input_dialog(self, title="Enter a value", message="Enter a value", default="", **kwargs):
236 """ 237 Show an input dialog 238 239 Usage: C{dialog.input_dialog(title="Enter a value", message="Enter a value", default="", **kwargs)} 240 241 @param title: window title for the dialog 242 @param message: message displayed above the input box 243 @param default: default value for the input box 244 @return: a tuple containing the exit code and user input 245 @rtype: C{tuple(int, str)} 246 """ 247 return self.__runKdialog(title, ["--inputbox", message, default], kwargs)
248
249 - def password_dialog(self, title="Enter password", message="Enter password", **kwargs):
250 """ 251 Show a password input dialog 252 253 Usage: C{dialog.password_dialog(title="Enter password", message="Enter password", **kwargs)} 254 255 @param title: window title for the dialog 256 @param message: message displayed above the password input box 257 @return: a tuple containing the exit code and user input 258 @rtype: C{tuple(int, str)} 259 """ 260 return self.__runKdialog(title, ["--password", message], kwargs)
261
262 - def combo_menu(self, options, title="Choose an option", message="Choose an option", **kwargs):
263 """ 264 Show a combobox menu 265 266 Usage: C{dialog.combo_menu(options, title="Choose an option", message="Choose an option", **kwargs)} 267 268 @param options: list of options (strings) for the dialog 269 @param title: window title for the dialog 270 @param message: message displayed above the combobox 271 @return: a tuple containing the exit code and user choice 272 @rtype: C{tuple(int, str)} 273 """ 274 return self.__runKdialog(title, ["--combobox", message] + options, kwargs)
275
276 - def list_menu(self, options, title="Choose a value", message="Choose a value", default=None, **kwargs):
277 """ 278 Show a single-selection list menu 279 280 Usage: C{dialog.list_menu(options, title="Choose a value", message="Choose a value", default=None, **kwargs)} 281 282 @param options: list of options (strings) for the dialog 283 @param title: window title for the dialog 284 @param message: message displayed above the list 285 @param default: default value to be selected 286 @return: a tuple containing the exit code and user choice 287 @rtype: C{tuple(int, str)} 288 """ 289 290 choices = [] 291 optionNum = 0 292 for option in options: 293 choices.append(str(optionNum)) 294 choices.append(option) 295 if option == default: 296 choices.append("on") 297 else: 298 choices.append("off") 299 optionNum += 1 300 301 retCode, result = self.__runKdialog(title, ["--radiolist", message] + choices, kwargs) 302 choice = options[int(result)] 303 304 return retCode, choice
305
306 - def list_menu_multi(self, options, title="Choose one or more values", message="Choose one or more values", defaults=[], **kwargs):
307 """ 308 Show a multiple-selection list menu 309 310 Usage: C{dialog.list_menu_multi(options, title="Choose one or more values", message="Choose one or more values", defaults=[], **kwargs)} 311 312 @param options: list of options (strings) for the dialog 313 @param title: window title for the dialog 314 @param message: message displayed above the list 315 @param defaults: list of default values to be selected 316 @return: a tuple containing the exit code and user choice 317 @rtype: C{tuple(int, str)} 318 """ 319 320 choices = [] 321 optionNum = 0 322 for option in options: 323 choices.append(str(optionNum)) 324 choices.append(option) 325 if option in defaults: 326 choices.append("on") 327 else: 328 choices.append("off") 329 optionNum += 1 330 331 retCode, output = self.__runKdialog(title, ["--separate-output", "--checklist", message] + choices, kwargs) 332 results = output.split() 333 334 choices = [] 335 for index in results: 336 choices.append(options[int(index)]) 337 338 return retCode, choices
339
340 - def open_file(self, title="Open File", initialDir="~", fileTypes="*|All Files", rememberAs=None, **kwargs):
341 """ 342 Show an Open File dialog 343 344 Usage: C{dialog.open_file(title="Open File", initialDir="~", fileTypes="*|All Files", rememberAs=None, **kwargs)} 345 346 @param title: window title for the dialog 347 @param initialDir: starting directory for the file dialog 348 @param fileTypes: file type filter expression 349 @param rememberAs: gives an ID to this file dialog, allowing it to open at the last used path next time 350 @return: a tuple containing the exit code and file path 351 @rtype: C{tuple(int, str)} 352 """ 353 if rememberAs is not None: 354 return self.__runKdialog(title, ["--getopenfilename", initialDir, fileTypes, ":" + rememberAs], kwargs) 355 else: 356 return self.__runKdialog(title, ["--getopenfilename", initialDir, fileTypes], kwargs)
357
358 - def save_file(self, title="Save As", initialDir="~", fileTypes="*|All Files", rememberAs=None, **kwargs):
359 """ 360 Show a Save As dialog 361 362 Usage: C{dialog.save_file(title="Save As", initialDir="~", fileTypes="*|All Files", rememberAs=None, **kwargs)} 363 364 @param title: window title for the dialog 365 @param initialDir: starting directory for the file dialog 366 @param fileTypes: file type filter expression 367 @param rememberAs: gives an ID to this file dialog, allowing it to open at the last used path next time 368 @return: a tuple containing the exit code and file path 369 @rtype: C{tuple(int, str)} 370 """ 371 if rememberAs is not None: 372 return self.__runKdialog(title, ["--getsavefilename", initialDir, fileTypes, ":" + rememberAs], kwargs) 373 else: 374 return self.__runKdialog(title, ["--getsavefilename", initialDir, fileTypes], kwargs)
375
376 - def choose_directory(self, title="Select Directory", initialDir="~", rememberAs=None, **kwargs):
377 """ 378 Show a Directory Chooser dialog 379 380 Usage: C{dialog.choose_directory(title="Select Directory", initialDir="~", rememberAs=None, **kwargs)} 381 382 @param title: window title for the dialog 383 @param initialDir: starting directory for the directory chooser dialog 384 @param rememberAs: gives an ID to this file dialog, allowing it to open at the last used path next time 385 @return: a tuple containing the exit code and chosen path 386 @rtype: C{tuple(int, str)} 387 """ 388 if rememberAs is not None: 389 return self.__runKdialog(title, ["--getexistingdirectory", initialDir, ":" + rememberAs], kwargs) 390 else: 391 return self.__runKdialog(title, ["--getexistingdirectory", initialDir], kwargs)
392
393 - def choose_colour(self, title="Select Colour", **kwargs):
394 """ 395 Show a Colour Chooser dialog 396 397 Usage: C{dialog.choose_colour(title="Select Colour")} 398 399 @param title: window title for the dialog 400 @return: a tuple containing the exit code and colour 401 @rtype: C{tuple(int, str)} 402 """ 403 return self.__runKdialog(title, ["--getcolor"], kwargs)
404
405 - def calendar(self, title="Choose a date", format="%Y-%m-%d", date="today", **kwargs):
406 """ 407 Show a calendar dialog 408 409 Usage: C{dialog.calendar_dialog(title="Choose a date", format="%Y-%m-%d", date="YYYY-MM-DD", **kwargs)} 410 411 Note: the format and date parameters are not currently used 412 413 @param title: window title for the dialog 414 @param format: format of date to be returned 415 @param date: initial date as YYYY-MM-DD, otherwise today 416 @return: a tuple containing the exit code and date 417 @rtype: C{tuple(int, str)} 418 """ 419 return self.__runKdialog(title, ["--calendar"], kwargs)
420 421
422 -class System:
423 """ 424 Simplified access to some system commands. 425 """ 426
427 - def exec_command(self, command, getOutput=True):
428 """ 429 Execute a shell command 430 431 Usage: C{system.exec_command(command, getOutput=True)} 432 433 Set getOutput to False if the command does not exit and return immediately. Otherwise 434 AutoKey will not respond to any hotkeys/abbreviations etc until the process started 435 by the command exits. 436 437 @param command: command to be executed (including any arguments) - e.g. "ls -l" 438 @param getOutput: whether to capture the (stdout) output of the command 439 @raise subprocess.CalledProcessError: if the command returns a non-zero exit code 440 """ 441 if getOutput: 442 p = subprocess.Popen(command, shell=True, bufsize=-1, stdout=subprocess.PIPE) 443 retCode = p.wait() 444 output = p.stdout.read()[:-1] 445 if retCode != 0: 446 raise subprocess.CalledProcessError(retCode, output) 447 else: 448 return output 449 else: 450 subprocess.Popen(command, shell=True, bufsize=-1)
451
452 - def create_file(self, fileName, contents=""):
453 """ 454 Create a file with contents 455 456 Usage: C{system.create_file(fileName, contents="")} 457 458 @param fileName: full path to the file to be created 459 @param contents: contents to insert into the file 460 """ 461 f = open(fileName, "w") 462 f.write(contents) 463 f.close()
464 465
466 -class GtkDialog:
467 """ 468 Provides a simple interface for the display of some basic dialogs to collect information from the user. 469 470 This version uses Zenity to integrate well with GNOME. To pass additional arguments to Zenity that are 471 not specifically handled, use keyword arguments. For example, to pass the --timeout argument to Zenity 472 pass C{timeout="15"} as one of the parameters. All keyword arguments must be given as strings. 473 474 A note on exit codes: an exit code of 0 indicates that the user clicked OK. 475 """ 476
477 - def __runZenity(self, title, args, kwargs):
478 for k, v in kwargs.iteritems(): 479 args.append("--" + k) 480 args.append(v) 481 482 p = subprocess.Popen(["zenity", "--title", title] + args, stdout=subprocess.PIPE) 483 retCode = p.wait() 484 output = p.stdout.read()[:-1] # Drop trailing newline 485 486 return (retCode, output)
487
488 - def info_dialog(self, title="Information", message="", **kwargs):
489 """ 490 Show an information dialog 491 492 Usage: C{dialog.info_dialog(title="Information", message="", **kwargs)} 493 494 @param title: window title for the dialog 495 @param message: message displayed in the dialog 496 @return: a tuple containing the exit code and user input 497 @rtype: C{tuple(int, str)} 498 """ 499 return self.__runZenity(title, ["--info", "--text", message], kwargs)
500
501 - def input_dialog(self, title="Enter a value", message="Enter a value", default="", **kwargs):
502 """ 503 Show an input dialog 504 505 Usage: C{dialog.input_dialog(title="Enter a value", message="Enter a value", default="", **kwargs)} 506 507 @param title: window title for the dialog 508 @param message: message displayed above the input box 509 @param default: default value for the input box 510 @return: a tuple containing the exit code and user input 511 @rtype: C{tuple(int, str)} 512 """ 513 return self.__runZenity(title, ["--entry", "--text", message, "--entry-text", default], kwargs)
514
515 - def password_dialog(self, title="Enter password", message="Enter password", **kwargs):
516 """ 517 Show a password input dialog 518 519 Usage: C{dialog.password_dialog(title="Enter password", message="Enter password")} 520 521 @param title: window title for the dialog 522 @param message: message displayed above the password input box 523 @return: a tuple containing the exit code and user input 524 @rtype: C{tuple(int, str)} 525 """ 526 return self.__runZenity(title, ["--entry", "--text", message, "--hide-text"], kwargs) 527 528 #def combo_menu(self, options, title="Choose an option", message="Choose an option"): 529 """ 530 Show a combobox menu - not supported by zenity 531 532 Usage: C{dialog.combo_menu(options, title="Choose an option", message="Choose an option")} 533 534 @param options: list of options (strings) for the dialog 535 @param title: window title for the dialog 536 @param message: message displayed above the combobox 537 """
538 #return self.__runZenity(title, ["--combobox", message] + options) 539
540 - def list_menu(self, options, title="Choose a value", message="Choose a value", default=None, **kwargs):
541 """ 542 Show a single-selection list menu 543 544 Usage: C{dialog.list_menu(options, title="Choose a value", message="Choose a value", default=None, **kwargs)} 545 546 @param options: list of options (strings) for the dialog 547 @param title: window title for the dialog 548 @param message: message displayed above the list 549 @param default: default value to be selected 550 @return: a tuple containing the exit code and user choice 551 @rtype: C{tuple(int, str)} 552 """ 553 554 choices = [] 555 #optionNum = 0 556 for option in options: 557 if option == default: 558 choices.append("TRUE") 559 else: 560 choices.append("FALSE") 561 562 #choices.append(str(optionNum)) 563 choices.append(option) 564 #optionNum += 1 565 566 return self.__runZenity(title, ["--list", "--radiolist", "--text", message, "--column", " ", "--column", "Options"] + choices, kwargs)
567 568 #return retCode, choice 569
570 - def list_menu_multi(self, options, title="Choose one or more values", message="Choose one or more values", defaults=[], **kwargs):
571 """ 572 Show a multiple-selection list menu 573 574 Usage: C{dialog.list_menu_multi(options, title="Choose one or more values", message="Choose one or more values", defaults=[], **kwargs)} 575 576 @param options: list of options (strings) for the dialog 577 @param title: window title for the dialog 578 @param message: message displayed above the list 579 @param defaults: list of default values to be selected 580 @return: a tuple containing the exit code and user choice 581 @rtype: C{tuple(int, str)} 582 """ 583 584 choices = [] 585 #optionNum = 0 586 for option in options: 587 if option in defaults: 588 choices.append("TRUE") 589 else: 590 choices.append("FALSE") 591 592 #choices.append(str(optionNum)) 593 choices.append(option) 594 #optionNum += 1 595 596 retCode, output = self.__runZenity(title, ["--list", "--checklist", "--text", message, "--column", " ", "--column", "Options"] + choices, kwargs) 597 results = output.split('|') 598 599 #choices = [] 600 #for choice in results: 601 # choices.append(choice) 602 603 return retCode, results
604
605 - def open_file(self, title="Open File", **kwargs):
606 """ 607 Show an Open File dialog 608 609 Usage: C{dialog.open_file(title="Open File", **kwargs)} 610 611 @param title: window title for the dialog 612 @return: a tuple containing the exit code and file path 613 @rtype: C{tuple(int, str)} 614 """ 615 #if rememberAs is not None: 616 # return self.__runZenity(title, ["--getopenfilename", initialDir, fileTypes, ":" + rememberAs]) 617 #else: 618 return self.__runZenity(title, ["--file-selection"], kwargs)
619
620 - def save_file(self, title="Save As", **kwargs):
621 """ 622 Show a Save As dialog 623 624 Usage: C{dialog.save_file(title="Save As", **kwargs)} 625 626 @param title: window title for the dialog 627 @return: a tuple containing the exit code and file path 628 @rtype: C{tuple(int, str)} 629 """ 630 #if rememberAs is not None: 631 # return self.__runZenity(title, ["--getsavefilename", initialDir, fileTypes, ":" + rememberAs]) 632 #else: 633 return self.__runZenity(title, ["--file-selection", "--save"], kwargs)
634
635 - def choose_directory(self, title="Select Directory", initialDir="~", **kwargs):
636 """ 637 Show a Directory Chooser dialog 638 639 Usage: C{dialog.choose_directory(title="Select Directory", **kwargs)} 640 641 @param title: window title for the dialog 642 @return: a tuple containing the exit code and path 643 @rtype: C{tuple(int, str)} 644 """ 645 #if rememberAs is not None: 646 # return self.__runZenity(title, ["--getexistingdirectory", initialDir, ":" + rememberAs]) 647 #else: 648 return self.__runZenity(title, ["--file-selection", "--directory"], kwargs) 649 650 #def choose_colour(self, title="Select Colour"): 651 """ 652 Show a Colour Chooser dialog - not supported by zenity 653 654 Usage: C{dialog.choose_colour(title="Select Colour")} 655 656 @param title: window title for the dialog 657 """
658 #return self.__runZenity(title, ["--getcolor"]) 659
660 - def calendar(self, title="Choose a date", format="%Y-%m-%d", date="today", **kwargs):
661 """ 662 Show a calendar dialog 663 664 Usage: C{dialog.calendar_dialog(title="Choose a date", format="%Y-%m-%d", date="YYYY-MM-DD", **kwargs)} 665 666 @param title: window title for the dialog 667 @param format: format of date to be returned 668 @param date: initial date as YYYY-MM-DD, otherwise today 669 @return: a tuple containing the exit code and date 670 @rtype: C{tuple(int, str)} 671 """ 672 if re.match(r"[0-9]{4}-[0-9]{2}-[0-9]{2}", date): 673 year = date[0:4] 674 month = date[5:7] 675 day = date[8:10] 676 date_args = ["--year=" + year, "--month=" + month, "--day=" + day] 677 else: 678 date_args = [] 679 return self.__runZenity(title, ["--calendar", "--date-format=" + format] + date_args, kwargs)
680 681
682 -class QtClipboard:
683 """ 684 Read/write access to the X selection and clipboard - QT version 685 """ 686
687 - def __init__(self, app):
688 self.clipBoard = QApplication.clipboard() 689 self.app = app
690
691 - def fill_selection(self, contents):
692 """ 693 Copy text into the X selection 694 695 Usage: C{clipboard.fill_selection(contents)} 696 697 @param contents: string to be placed in the selection 698 """ 699 self.__execAsync(self.__fillSelection, contents)
700
701 - def __fillSelection(self, string):
702 self.clipBoard.setText(string, QClipboard.Selection) 703 self.sem.release()
704
705 - def get_selection(self):
706 """ 707 Read text from the X selection 708 709 Usage: C{clipboard.get_selection()} 710 711 @return: text contents of the mouse selection 712 @rtype: C{str} 713 """ 714 self.__execAsync(self.__getSelection) 715 return unicode(self.text)
716
717 - def __getSelection(self):
718 self.text = self.clipBoard.text(QClipboard.Selection) 719 self.sem.release()
720
721 - def fill_clipboard(self, contents):
722 """ 723 Copy text into the clipboard 724 725 Usage: C{clipboard.fill_clipboard(contents)} 726 727 @param contents: string to be placed in the selection 728 """ 729 self.__execAsync(self.__fillClipboard, contents)
730
731 - def __fillClipboard(self, string):
732 self.clipBoard.setText(string, QClipboard.Clipboard) 733 self.sem.release()
734
735 - def get_clipboard(self):
736 """ 737 Read text from the clipboard 738 739 Usage: C{clipboard.get_clipboard()} 740 741 @return: text contents of the clipboard 742 @rtype: C{str} 743 """ 744 self.__execAsync(self.__getClipboard) 745 return unicode(self.text)
746
747 - def __getClipboard(self):
748 self.text = self.clipBoard.text(QClipboard.Clipboard) 749 self.sem.release()
750
751 - def __execAsync(self, callback, *args):
752 self.sem = threading.Semaphore(0) 753 self.app.exec_in_main(callback, *args) 754 self.sem.acquire()
755 756
757 -class GtkClipboard:
758 """ 759 Read/write access to the X selection and clipboard - GTK version 760 """ 761
762 - def __init__(self, app):
763 self.clipBoard = gtk.Clipboard() 764 self.selection = gtk.Clipboard(selection="PRIMARY") 765 self.app = app
766
767 - def fill_selection(self, contents):
768 """ 769 Copy text into the X selection 770 771 Usage: C{clipboard.fill_selection(contents)} 772 773 @param contents: string to be placed in the selection 774 """ 775 #self.__execAsync(self.__fillSelection, contents) 776 self.__fillSelection(contents)
777
778 - def __fillSelection(self, string):
779 gtk.gdk.threads_enter() 780 self.selection.set_text(string.encode("utf-8")) 781 gtk.gdk.threads_leave()
782 #self.sem.release() 783
784 - def get_selection(self):
785 """ 786 Read text from the X selection 787 788 Usage: C{clipboard.get_selection()} 789 790 @return: text contents of the mouse selection 791 @rtype: C{str} 792 @raise Exception: if no text was found in the selection 793 """ 794 self.__execAsync(self.selection.request_text, self.__receive) 795 if self.text is not None: 796 return self.text.decode("utf-8") 797 else: 798 raise Exception("No text found in X selection")
799
800 - def __receive(self, cb, text, data=None):
801 self.text = text 802 self.sem.release()
803
804 - def fill_clipboard(self, contents):
805 """ 806 Copy text into the clipboard 807 808 Usage: C{clipboard.fill_clipboard(contents)} 809 810 @param contents: string to be placed in the selection 811 """ 812 self.__fillClipboard(contents)
813
814 - def __fillClipboard(self, string):
815 gtk.gdk.threads_enter() 816 self.clipBoard.set_text(string.encode("utf-8")) 817 gtk.gdk.threads_leave()
818 #self.sem.release() 819
820 - def get_clipboard(self):
821 """ 822 Read text from the clipboard 823 824 Usage: C{clipboard.get_clipboard()} 825 826 @return: text contents of the clipboard 827 @rtype: C{str} 828 @raise Exception: if no text was found on the clipboard 829 """ 830 self.__execAsync(self.clipBoard.request_text, self.__receive) 831 if self.text is not None: 832 return self.text.decode("utf-8") 833 else: 834 raise Exception("No text found on clipboard")
835
836 - def __execAsync(self, callback, *args):
837 self.sem = threading.Semaphore(0) 838 gtk.gdk.threads_enter() 839 callback(*args) 840 gtk.gdk.threads_leave() 841 self.sem.acquire()
842 843
844 -class Window:
845 """ 846 Basic window management using wmctrl 847 848 Note: in all cases where a window title is required (with the exception of wait_for_focus()), 849 two special values of window title are permitted: 850 851 :ACTIVE: - select the currently active window 852 :SELECT: - select the desired window by clicking on it 853 """ 854
855 - def __init__(self, mediator):
856 self.mediator = mediator
857
858 - def wait_for_focus(self, title, timeOut=5):
859 """ 860 Wait for window with the given title to have focus 861 862 Usage: C{window.wait_for_focus(title, timeOut=5)} 863 864 If the window becomes active, returns True. Otherwise, returns False if 865 the window has not become active by the time the timeout has elapsed. 866 867 @param title: title to match against (as a regular expression) 868 @param timeOut: period (seconds) to wait before giving up 869 @rtype: boolean 870 """ 871 regex = re.compile(title) 872 waited = 0 873 while waited <= timeOut: 874 if regex.match(self.mediator.interface.get_window_title()): 875 return True 876 877 if timeOut == 0: 878 break # zero length timeout, if not matched go straight to end 879 880 time.sleep(0.3) 881 waited += 0.3 882 883 return False
884
885 - def wait_for_exist(self, title, timeOut=5):
886 """ 887 Wait for window with the given title to be created 888 889 Usage: C{window.wait_for_exist(title, timeOut=5)} 890 891 If the window is in existence, returns True. Otherwise, returns False if 892 the window has not been created by the time the timeout has elapsed. 893 894 @param title: title to match against (as a regular expression) 895 @param timeOut: period (seconds) to wait before giving up 896 @rtype: boolean 897 """ 898 regex = re.compile(title) 899 waited = 0 900 while waited <= timeOut: 901 retCode, output = self.__runWmctrl(["-l"]) 902 for line in output.split('\n'): 903 if regex.match(line[14:].split(' ', 1)[-1]): 904 return True 905 906 if timeOut == 0: 907 break # zero length timeout, if not matched go straight to end 908 909 time.sleep(0.3) 910 waited += 0.3 911 912 return False
913
914 - def activate(self, title, switchDesktop=False, matchClass=False):
915 """ 916 Activate the specified window, giving it input focus 917 918 Usage: C{window.activate(title, switchDesktop=False, matchClass=False)} 919 920 If switchDesktop is False (default), the window will be moved to the current desktop 921 and activated. Otherwise, switch to the window's current desktop and activate it there. 922 923 @param title: window title to match against (as case-insensitive substring match) 924 @param switchDesktop: whether or not to switch to the window's current desktop 925 @param matchClass: if True, match on the window class instead of the title 926 """ 927 if switchDesktop: 928 args = ["-a", title] 929 else: 930 args = ["-R", title] 931 if matchClass: 932 args += ["-x"] 933 self.__runWmctrl(args)
934
935 - def close(self, title, matchClass=False):
936 """ 937 Close the specified window gracefully 938 939 Usage: C{window.close(title, matchClass=False)} 940 941 @param title: window title to match against (as case-insensitive substring match) 942 @param matchClass: if True, match on the window class instead of the title 943 """ 944 if matchClass: 945 self.__runWmctrl(["-c", title, "-x"]) 946 else: 947 self.__runWmctrl(["-c", title])
948
949 - def resize_move(self, title, xOrigin=-1, yOrigin=-1, width=-1, height=-1, matchClass=False):
950 """ 951 Resize and/or move the specified window 952 953 Usage: C{window.close(title, xOrigin=-1, yOrigin=-1, width=-1, height=-1, matchClass=False)} 954 955 Leaving and of the position/dimension values as the default (-1) will cause that 956 value to be left unmodified. 957 958 @param title: window title to match against (as case-insensitive substring match) 959 @param xOrigin: new x origin of the window (upper left corner) 960 @param yOrigin: new y origin of the window (upper left corner) 961 @param width: new width of the window 962 @param height: new height of the window 963 @param matchClass: if True, match on the window class instead of the title 964 """ 965 mvArgs = ["0", str(xOrigin), str(yOrigin), str(width), str(height)] 966 if matchClass: 967 xArgs = ["-x"] 968 else: 969 xArgs = [] 970 self.__runWmctrl(["-r", title, "-e", ','.join(mvArgs)] + xArgs)
971 972
973 - def move_to_desktop(self, title, deskNum, matchClass=False):
974 """ 975 Move the specified window to the given desktop 976 977 Usage: C{window.move_to_desktop(title, deskNum, matchClass=False)} 978 979 @param title: window title to match against (as case-insensitive substring match) 980 @param deskNum: desktop to move the window to (note: zero based) 981 @param matchClass: if True, match on the window class instead of the title 982 """ 983 if matchClass: 984 xArgs = ["-x"] 985 else: 986 xArgs = [] 987 self.__runWmctrl(["-r", title, "-t", str(deskNum)] + xArgs)
988 989
990 - def switch_desktop(self, deskNum):
991 """ 992 Switch to the specified desktop 993 994 Usage: C{window.switch_desktop(deskNum)} 995 996 @param deskNum: desktop to switch to (note: zero based) 997 """ 998 self.__runWmctrl(["-s", str(deskNum)])
999
1000 - def set_property(self, title, action, prop, matchClass=False):
1001 """ 1002 Set a property on the given window using the specified action 1003 1004 Usage: C{window.set_property(title, action, prop, matchClass=False)} 1005 1006 Allowable actions: C{add, remove, toggle} 1007 Allowable properties: C{modal, sticky, maximized_vert, maximized_horz, shaded, skip_taskbar, 1008 skip_pager, hidden, fullscreen, above} 1009 1010 @param title: window title to match against (as case-insensitive substring match) 1011 @param action: one of the actions listed above 1012 @param prop: one of the properties listed above 1013 @param matchClass: if True, match on the window class instead of the title 1014 """ 1015 if matchClass: 1016 xArgs = ["-x"] 1017 else: 1018 xArgs = [] 1019 self.__runWmctrl(["-r", title, "-b" + action + ',' + prop] + xArgs)
1020
1021 - def get_active_geometry(self):
1022 """ 1023 Get the geometry of the currently active window 1024 1025 Usage: C{window.get_active_geometry()} 1026 1027 @return: a 4-tuple containing the x-origin, y-origin, width and height of the window (in pixels) 1028 @rtype: C{tuple(int, int, int, int)} 1029 """ 1030 active = self.mediator.interface.get_window_title() 1031 result, output = self.__runWmctrl(["-l", "-G"]) 1032 matchingLine = None 1033 for line in output.split('\n'): 1034 if active in line[34:].split(' ', 1)[-1]: 1035 matchingLine = line 1036 1037 if matchingLine is not None: 1038 output = matchingLine.split()[2:6] 1039 return map(int, output) 1040 else: 1041 return None
1042
1043 - def get_active_title(self):
1044 """ 1045 Get the visible title of the currently active window 1046 1047 Usage: C{window.get_active_title()} 1048 1049 @return: the visible title of the currentle active window 1050 @rtype: C{str} 1051 """ 1052 return self.mediator.interface.get_window_title()
1053
1054 - def get_active_class(self):
1055 """ 1056 Get the class of the currently active window 1057 1058 Usage: C{window.get_active_class()} 1059 1060 @return: the class of the currentle active window 1061 @rtype: C{str} 1062 """ 1063 return self.mediator.interface.get_window_class()
1064
1065 - def __runWmctrl(self, args):
1066 p = subprocess.Popen(["wmctrl"] + args, stdout=subprocess.PIPE) 1067 retCode = p.wait() 1068 output = p.stdout.read()[:-1] # Drop trailing newline 1069 1070 return (retCode, output)
1071 1072
1073 -class Engine:
1074 """ 1075 Provides access to the internals of AutoKey. 1076 1077 Note that any configuration changes made using this API while the configuration window 1078 is open will not appear until it is closed and re-opened. 1079 """ 1080
1081 - def __init__(self, configManager, runner):
1082 self.configManager = configManager 1083 self.runner = runner 1084 self.monitor = configManager.app.monitor 1085 self.__returnValue = ''
1086
1087 - def get_folder(self, title):
1088 """ 1089 Retrieve a folder by its title 1090 1091 Usage: C{engine.get_folder(title)} 1092 1093 Note that if more than one folder has the same title, only the first match will be 1094 returned. 1095 """ 1096 for folder in self.configManager.allFolders: 1097 if folder.title == title: 1098 return folder 1099 return None
1100
1101 - def create_phrase(self, folder, description, contents):
1102 """ 1103 Create a text phrase 1104 1105 Usage: C{engine.create_phrase(folder, description, contents)} 1106 1107 A new phrase with no abbreviation or hotkey is created in the specified folder 1108 1109 @param folder: folder to place the abbreviation in, retrieved using C{engine.get_folder()} 1110 @param description: description for the phrase 1111 @param contents: the expansion text 1112 """ 1113 self.monitor.suspend() 1114 p = model.Phrase(description, contents) 1115 folder.add_item(p) 1116 p.persist() 1117 self.monitor.unsuspend() 1118 self.configManager.config_altered(False)
1119
1120 - def create_abbreviation(self, folder, description, abbr, contents):
1121 """ 1122 Create a text abbreviation 1123 1124 Usage: C{engine.create_abbreviation(folder, description, abbr, contents)} 1125 1126 When the given abbreviation is typed, it will be replaced with the given 1127 text. 1128 1129 @param folder: folder to place the abbreviation in, retrieved using C{engine.get_folder()} 1130 @param description: description for the phrase 1131 @param abbr: the abbreviation that will trigger the expansion 1132 @param contents: the expansion text 1133 @raise Exception: if the specified abbreviation is not unique 1134 """ 1135 if not self.configManager.check_abbreviation_unique(abbr, None, None): 1136 raise Exception("The specified abbreviation is already in use") 1137 1138 self.monitor.suspend() 1139 p = model.Phrase(description, contents) 1140 p.modes.append(model.TriggerMode.ABBREVIATION) 1141 p.abbreviations = [abbr] 1142 folder.add_item(p) 1143 p.persist() 1144 self.monitor.unsuspend() 1145 self.configManager.config_altered(False)
1146
1147 - def create_hotkey(self, folder, description, modifiers, key, contents):
1148 """ 1149 Create a text hotkey 1150 1151 Usage: C{engine.create_hotkey(folder, description, modifiers, key, contents)} 1152 1153 When the given hotkey is pressed, it will be replaced with the given 1154 text. Modifiers must be given as a list of strings, with the following 1155 values permitted: 1156 1157 <ctrl> 1158 <alt> 1159 <super> 1160 <hyper> 1161 <shift> 1162 1163 The key must be an unshifted character (i.e. lowercase) 1164 1165 @param folder: folder to place the abbreviation in, retrieved using C{engine.get_folder()} 1166 @param description: description for the phrase 1167 @param modifiers: modifiers to use with the hotkey (as a list) 1168 @param key: the hotkey 1169 @param contents: the expansion text 1170 @raise Exception: if the specified hotkey is not unique 1171 """ 1172 modifiers.sort() 1173 if not self.configManager.check_hotkey_unique(modifiers, key, None, None): 1174 raise Exception("The specified hotkey and modifier combination is already in use") 1175 1176 self.monitor.suspend() 1177 p = model.Phrase(description, contents) 1178 p.modes.append(model.TriggerMode.HOTKEY) 1179 p.set_hotkey(modifiers, key) 1180 folder.add_item(p) 1181 p.persist() 1182 self.monitor.unsuspend() 1183 self.configManager.config_altered(False)
1184
1185 - def run_script(self, description):
1186 """ 1187 Run an existing script using its description to look it up 1188 1189 Usage: C{engine.run_script(description)} 1190 1191 @param description: description of the script to run 1192 @raise Exception: if the specified script does not exist 1193 """ 1194 targetScript = None 1195 for item in self.configManager.allItems: 1196 if item.description == description and isinstance(item, model.Script): 1197 targetScript = item 1198 1199 if targetScript is not None: 1200 self.runner.run_subscript(targetScript) 1201 else: 1202 raise Exception("No script with description '%s' found" % description)
1203
1204 - def run_script_from_macro(self, args):
1205 """ 1206 Used internally by AutoKey for phrase macros 1207 """ 1208 self.__macroArgs = args["args"].split(',') 1209 1210 try: 1211 self.run_script(args["name"]) 1212 except Exception, e: 1213 self.set_return_value("{ERROR: %s}" % str(e))
1214
1215 - def get_macro_arguments(self):
1216 """ 1217 Get the arguments supplied to the current script via its macro 1218 1219 Usage: C{engine.get_macro_arguments()} 1220 1221 @return: the arguments 1222 @rtype: C{list(str())} 1223 """ 1224 return self.__macroArgs
1225
1226 - def set_return_value(self, val):
1227 """ 1228 Store a return value to be used by a phrase macro 1229 1230 Usage: C{engine.set_return_value(val)} 1231 1232 @param val: value to be stored 1233 """ 1234 self.__returnValue = val
1235
1236 - def get_return_value(self):
1237 """ 1238 Used internally by AutoKey for phrase macros 1239 """ 1240 ret = self.__returnValue 1241 self.__returnValue = '' 1242 return ret
1243