Package lib ::
Module scripting
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import subprocess, threading, time, re
20
21
22
24 """
25 Provides access to the keyboard for event generation.
26 """
27
29 self.mediator = mediator
30
32 """
33 Send a sequence of keys via keyboard events
34
35 Usage: C{keyboard.send_keys(keyString)}
36
37 @param keyString: string of keys (including special keys) to send
38 """
39 self.mediator.send_string(keyString.decode("utf-8"))
40 self.mediator.flush()
41
43 """
44 Send a keyboard event
45
46 Usage: C{keyboard.send_key(key, repeat=1)}
47
48 @param key: they key to be sent (e.g. "s" or "<enter>")
49 @param repeat: number of times to repeat the key event
50 """
51 for x in range(repeat):
52 self.mediator.send_key(key.decode("utf-8"))
53 self.mediator.flush()
54
55
57 """
58 Provides access to send mouse clicks
59 """
61 self.mediator = mediator
62
64 """
65 Send a mouse click relative to the active window
66
67 Usage: C{mouse.click_relative(x, y, button)}
68
69 @param x: x-coordinate in pixels, relative to upper left corner of window
70 @param y: y-coordinate in pixels, relative to upper left corner of window
71 @param button: mouse button to simulate (left=1, middle=2, right=3)
72 """
73 self.mediator.send_mouse_click(x, y, button, True)
74
76 """
77 Send a mouse click relative to the screen (absolute)
78
79 Usage: C{mouse.click_absolute(x, y, button)}
80
81 @param x: x-coordinate in pixels, relative to upper left corner of window
82 @param y: y-coordinate in pixels, relative to upper left corner of window
83 @param button: mouse button to simulate (left=1, middle=2, right=3)
84 """
85 self.mediator.send_mouse_click(x, y, button, False)
86
87
89 """
90 Allows persistent storage of values between invocations of the script.
91 """
92
94 """
95 Store a value
96
97 Usage: C{store.set_value(key, value)}
98 """
99 self[key] = value
100
102 """
103 Get a value
104
105 Usage: C{store.get_value(key)}
106 """
107 return self[key]
108
110 """
111 Remove a value
112
113 Usage: C{store.remove_value(key)}
114 """
115 del self[key]
116
118 """
119 Provides a simple interface for the display of some basic dialogs to collect information from the user.
120 """
121
123 p = subprocess.Popen(["kdialog", "--title", title] + args, stdout=subprocess.PIPE)
124 retCode = p.wait()
125 output = p.stdout.read()[:-1]
126
127 return (retCode, output)
128
140
141 - def password_dialog(self, title="Enter password", message="Enter password"):
142 """
143 Show a password input dialog
144
145 Usage: C{dialog.password_dialog(title="Enter password", message="Enter password")}
146
147 @param title: window title for the dialog
148 @param message: message displayed above the password input box
149 """
150 return self.__runKdialog(title, ["--password", message])
151
153 """
154 Show a combobox menu
155
156 Usage: C{dialog.combo_menu(options, title="Choose an option", message="Choose an option")}
157
158 @param options: list of options (strings) for the dialog
159 @param title: window title for the dialog
160 @param message: message displayed above the combobox
161 """
162 return self.__runKdialog(title, ["--combobox", message] + options)
163
165 """
166 Show a single-selection list menu
167
168 Usage: C{dialog.list_menu(options, title="Choose a value", message="Choose a value", default=None)}
169
170 @param options: list of options (strings) for the dialog
171 @param title: window title for the dialog
172 @param message: message displayed above the list
173 @param default: default value to be selected
174 """
175
176 choices = []
177 optionNum = 0
178 for option in options:
179 choices.append(str(optionNum))
180 choices.append(option)
181 if option == default:
182 choices.append("on")
183 else:
184 choices.append("off")
185 optionNum += 1
186
187 retCode, result = self.__runKdialog(title, ["--radiolist", message] + choices)
188 choice = options[int(result)]
189
190 return retCode, choice
191
193 """
194 Show a multiple-selection list menu
195
196 Usage: C{dialog.list_menu_multi(options, title="Choose one or more values", message="Choose one or more values", defaults=[])}
197
198 @param options: list of options (strings) for the dialog
199 @param title: window title for the dialog
200 @param message: message displayed above the list
201 @param defaults: list of default values to be selected
202 """
203
204 choices = []
205 optionNum = 0
206 for option in options:
207 choices.append(str(optionNum))
208 choices.append(option)
209 if option in defaults:
210 choices.append("on")
211 else:
212 choices.append("off")
213 optionNum += 1
214
215 retCode, output = self.__runKdialog(title, ["--separate-output", "--checklist", message] + choices)
216 results = output.split()
217
218 choices = []
219 for index in results:
220 choices.append(options[int(index)])
221
222 return retCode, choices
223
224 - def open_file(self, title="Open File", initialDir="~", fileTypes="*|All Files", rememberAs=None):
225 """
226 Show an Open File dialog
227
228 Usage: C{dialog.open_file(title="Open File", initialDir="~", fileTypes="*|All Files", rememberAs=None)}
229
230 @param title: window title for the dialog
231 @param initialDir: starting directory for the file dialog
232 @param fileTypes: file type filter expression
233 @param rememberAs: gives an ID to this file dialog, allowing it to open at the last used path next time
234 """
235 if rememberAs is not None:
236 return self.__runKdialog(title, ["--getopenfilename", initialDir, fileTypes, ":" + rememberAs])
237 else:
238 return self.__runKdialog(title, ["--getopenfilename", initialDir, fileTypes])
239
240 - def save_file(self, title="Save As", initialDir="~", fileTypes="*|All Files", rememberAs=None):
241 """
242 Show a Save As dialog
243
244 Usage: C{dialog.save_file(title="Save As", initialDir="~", fileTypes="*|All Files", rememberAs=None)}
245
246 @param title: window title for the dialog
247 @param initialDir: starting directory for the file dialog
248 @param fileTypes: file type filter expression
249 @param rememberAs: gives an ID to this file dialog, allowing it to open at the last used path next time
250 """
251 if rememberAs is not None:
252 return self.__runKdialog(title, ["--getsavefilename", initialDir, fileTypes, ":" + rememberAs])
253 else:
254 return self.__runKdialog(title, ["--getsavefilename", initialDir, fileTypes])
255
256 - def choose_directory(self, title="Select Directory", initialDir="~", rememberAs=None):
257 """
258 Show a Directory Chooser dialog
259
260 Usage: C{dialog.choose_directory(title="Select Directory", initialDir="~", rememberAs=None)}
261
262 @param title: window title for the dialog
263 @param initialDir: starting directory for the directory chooser dialog
264 @param rememberAs: gives an ID to this file dialog, allowing it to open at the last used path next time
265 """
266 if rememberAs is not None:
267 return self.__runKdialog(title, ["--getexistingdirectory", initialDir, ":" + rememberAs])
268 else:
269 return self.__runKdialog(title, ["--getexistingdirectory", initialDir])
270
272 """
273 Show a Colour Chooser dialog
274
275 Usage: C{dialog.choose_colour(title="Select Colour")}
276
277 @param title: window title for the dialog
278 """
279 return self.__runKdialog(title, ["--getcolor"])
280
281
283 """
284 Simplified access to some system commands.
285 """
286
288 """
289 Execute a shell command
290
291 Usage: C{system.exec_command(command)}
292
293 @param command: command to be executed (including any arguments) - e.g. "ls -l"
294 @raises subprocess.CalledProcessError: if the command returns a non-zero exit code
295 """
296 p = subprocess.Popen(command, shell=True, bufsize=-1, stdout=subprocess.PIPE)
297 retCode = p.wait()
298 output = p.stdout.read()[:-1]
299 if retCode != 0:
300 raise subprocess.CalledProcessError(retCode, output)
301 else:
302 return output
303
305 """
306 Create a file with contents
307
308 Usage: C{system.create_file(fileName, contents="")}
309
310 @param fileName: full path to the file to be created
311 @param contents: contents to insert into the file
312 """
313 f = open(fileName, "w")
314 f.write(contents)
315 f.close()
316
317
319 """
320 Read/write access to the X selection and clipboard
321 """
322
324 self.clipBoard = QApplication.clipboard()
325 self.app = app
326
328 """
329 Copy text into the X selection
330
331 Usage: C{clipboard.fill_selection(contents)}
332
333 @param contents: string to be placed in the selection
334 """
335 self.__execAsync(self.__fillSelection, contents)
336
338 self.clipBoard.setText(string, QClipboard.Selection)
339 self.sem.release()
340
342 """
343 Read text from the X selection
344
345 Usage: C{clipboard.get_selection()}
346 """
347 self.__execAsync(self.__getSelection)
348 return str(self.text)
349
351 self.text = self.clipBoard.text(QClipboard.Selection)
352 self.sem.release()
353
355 """
356 Copy text into the clipboard
357
358 Usage: C{clipboard.fill_clipboard(contents)}
359
360 @param contents: string to be placed in the selection
361 """
362 self.__execAsync(self.__fillClipboard, contents)
363
365 self.clipBoard.setText(string, QClipboard.Clipboard)
366 self.sem.release()
367
369 """
370 Read text from the clipboard
371
372 Usage: C{clipboard.get_clipboard()}
373 """
374 self.__execAsync(self.__getClipboard)
375 return str(self.text)
376
378 self.text = self.clipBoard.text(QClipboard.Clipboard)
379 self.sem.release()
380
382 self.sem = threading.Semaphore(0)
383 self.app.exec_in_main(callback, *args)
384 self.sem.acquire()
385
386
388 """
389 Basic window management using wmctrl
390
391 Note: in all cases where a window title is required (with the exception of wait_for_focus()),
392 two special values of window title are permitted:
393
394 :ACTIVE: - select the currently active window
395 :SELECT: - select the desired window by clicking on it
396 """
397
399 self.mediator = mediator
400
402 """
403 Wait for window with the given title to have focus
404
405 Usage: C{window.wait_for_focus(title, timeOut=5)}
406
407 If the window becomes active, returns True. Otherwise, returns False if
408 the window has not become active by the time the timeout has elapsed.
409
410 @param title: title to match against (as a regular expression)
411 @param timeOut: period (seconds) to wait before giving up
412 """
413 regex = re.compile(title)
414 waited = 0
415 while waited < timeOut:
416 if regex.match(self.mediator.interface.get_window_title()):
417 return True
418 time.sleep(0.3)
419 waited += 0.3
420
421 return False
422
424 """
425 Wait for window with the given title to be created
426
427 Usage: C{window.wait_for_exist(title, timeOut=5)}
428
429 If the window is in existence, returns True. Otherwise, returns False if
430 the window has not been created by the time the timeout has elapsed.
431
432 @param title: title to match against (as a regular expression)
433 @param timeOut: period (seconds) to wait before giving up
434 """
435 regex = re.compile(title)
436 waited = 0
437 while waited < timeOut:
438 retCode, output = self.__runWmctrl(["-l"])
439 for line in output.split('\n'):
440 if regex.match(line[14:].split(' ', 1)[-1]):
441 return True
442
443 time.sleep(0.3)
444 waited += 0.3
445
446 return False
447
448 - def activate(self, title, switchDesktop=False):
449 """
450 Activate the specified window, giving it input focus
451
452 Usage: C{window.activate(title, switchDesktop=False)}
453
454 If switchDesktop is False (default), the window will be moved to the current desktop
455 and activated. Otherwise, switch to the window's current desktop and activate it there.
456
457 @param title: window title to match against (as case-insensitive substring match)
458 @param switchDesktop: whether or not to switch to the window's current desktop
459 """
460 if switchDesktop:
461 args = ["-a", title]
462 else:
463 args = ["-R", title]
464 self.__runWmctrl(args)
465
467 """
468 Close the specified window gracefully
469
470 Usage: C{window.close(title)}
471
472 @param title: window title to match against (as case-insensitive substring match)
473 """
474 self.__runWmctrl(["-c", title])
475
476 - def resize_move(self, title, xOrigin=-1, yOrigin=-1, width=-1, height=-1):
477 """
478 Resize and/or move the specified window
479
480 Usage: C{window.close(title, xOrigin=-1, yOrigin=-1, width=-1, height=-1)}
481
482 Leaving and of the position/dimension values as the default (-1) will cause that
483 value to be left unmodified.
484
485 @param title: window title to match against (as case-insensitive substring match)
486 @param xOrigin: new x origin of the window (upper left corner)
487 @param yOrigin: new y origin of the window (upper left corner)
488 @param width: new width of the window
489 @param height: new height of the window
490 """
491 mvArgs = ["0", str(xOrigin), str(yOrigin), str(width), str(height)]
492 self.__runWmctrl(["-r", title, "-e", ','.join(mvArgs)])
493
494
496 """
497 Move the specified window to the given desktop
498
499 Usage: C{window.move_to_desktop(title, deskNum)}
500
501 @param title: window title to match against (as case-insensitive substring match)
502 @param deskNum: desktop to move the window to (note: zero based)
503 """
504 self.__runWmctrl(["-r", title, "-t", str(deskNum)])
505
506
508 """
509 Switch to the specified desktop
510
511 Usage: C{window.switch_desktop(deskNum)}
512
513 @param deskNum: desktop to switch to (note: zero based)
514 """
515 self.__runWmctrl(["-s", str(deskNum)])
516
518 """
519 Set a property on the given window using the specified action
520
521 Usage: C{window.set_property(title, title, action, prop)}
522
523 Allowable actions: C{add, remove, toggle}
524 Allowable properties: C{modal, sticky, maximized_vert, maximized_horz, shaded, skip_taskbar,
525 skip_pager, hidden, fullscreen, above}
526
527 @param title: window title to match against (as case-insensitive substring match)
528 @param action: one of the actions listed above
529 @param prop: one of the properties listed above
530 """
531 self.__runWmctrl(["-r", title, "-b" + action + ',' + prop])
532
534 """
535 Get the geometry of the currently active window
536
537 Usage: C{window.get_active_geometry()}
538
539 Returns a 4-tuple containing the x-origin, y-origin, width and height of the window (in pixels)
540 """
541 active = self.mediator.interface.get_window_title()
542 result, output = self.__runWmctrl(["-l", "-G"])
543 matchingLine = None
544 for line in output.split('\n'):
545 if active in line[34:].split(' ', 1)[-1]:
546 matchingLine = line
547
548 if matchingLine is not None:
549 output = matchingLine[14:].split(' ')[0:3]
550 return map(int, output)
551 else:
552 return None
553
555 p = subprocess.Popen(["wmctrl"] + args, stdout=subprocess.PIPE)
556 retCode = p.wait()
557 output = p.stdout.read()[:-1]
558
559 return (retCode, output)
560
561
563 """
564 Provides access to the internals of AutoKey.
565
566 Note that any configuration changes made using this API while the configuration window
567 is open will not appear until it is closed and re-opened.
568 """
569
570 - def __init__(self, configManager, runner):
571 self.configManager = configManager
572 self.runner = runner
573
575 """
576 Retrieve a folder by its title
577
578 Usage: C{engine.get_folder(title)}
579
580 Note that if more than one folder has the same title, only the first match will be
581 returned.
582 """
583 for folder in self.configManager.allFolders:
584 if folder.title == title:
585 return folder
586 return None
587
589 """
590 Create a text phrase
591
592 Usage: C{engine.create_phrase(folder, description, contents)}
593
594 A new phrase with no abbreviation or hotkey is created in the specified folder
595
596 @param folder: folder to place the abbreviation in, retrieved using C{engine.get_folder()}
597 @param description: description for the phrase
598 @param contents: the expansion text
599 """
600 p = model.Phrase(description, contents)
601 folder.add_item(p)
602 self.configManager.config_altered()
603
605 """
606 Create a text abbreviation
607
608 Usage: C{engine.create_abbreviation(folder, description, abbr, contents)}
609
610 When the given abbreviation is typed, it will be replaced with the given
611 text.
612
613 @param folder: folder to place the abbreviation in, retrieved using C{engine.get_folder()}
614 @param description: description for the phrase
615 @param abbr: the abbreviation that will trigger the expansion
616 @param contents: the expansion text
617 @raises Exception: if the specified abbreviation is not unique
618 """
619 if not self.configManager.check_abbreviation_unique(abbr, None):
620 raise Exception("The specified abbreviation is already in use")
621
622 p = model.Phrase(description, contents)
623 p.modes.append(model.TriggerMode.ABBREVIATION)
624 p.abbreviation = abbr
625 folder.add_item(p)
626 self.configManager.config_altered()
627
628 - def create_hotkey(self, folder, description, modifiers, key, contents):
629 """
630 Create a text hotkey.
631
632 Usage: C{engine.create_hotkey(folder, description, modifiers, key, contents)}
633
634 When the given hotkey is pressed, it will be replaced with the given
635 text. Modifiers must be given as a list of strings, with the following
636 values permitted:
637
638 <control>
639 <alt>
640 <super>
641 <shift>
642
643 The key must be an unshifted character (i.e. lowercase)
644
645 @param folder: folder to place the abbreviation in, retrieved using C{engine.get_folder()}
646 @param description: description for the phrase
647 @param modifiers: modifiers to use with the hotkey (as a list)
648 @param key: the hotkey
649 @param contents: the expansion text
650 @raises Exception: if the specified hotkey is not unique
651 """
652 modifiers.sort()
653 if not self.configManager.check_hotkey_unique(modifiers, key, None):
654 raise Exception("The specified hotkey and modifier combination is already in use")
655
656 p = model.Phrase(description, contents)
657 p.modes.append(model.TriggerMode.HOTKEY)
658 p.set_hotkey(modifiers, key)
659 folder.add_item(p)
660 self.configManager.config_altered()
661
663 """
664 Run an existing script using its description to look it up
665
666 Usage: C{engine.run_script(description)}
667
668 @param description: description of the script to run
669 @raises Exception: if the specified script does not exist
670 """
671 targetScript = None
672 for item in self.configManager.allItems:
673 if item.description == description and isinstance(item, Script):
674 targetScript = item
675
676 if targetScript is not None:
677 self.runner.execute(targetScript, "")
678 else:
679 raise Exception("No script with description '%s' found" % description)
680