Skip to content

Commit

Permalink
more rules, recording, updated test example
Browse files Browse the repository at this point in the history
  • Loading branch information
verdverm committed May 11, 2019
1 parent 8bfd8a9 commit d8ee374
Show file tree
Hide file tree
Showing 8 changed files with 553 additions and 240 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
penv
__pycache__
build
dist
self_driving_desktop.egg-info/
108 changes: 44 additions & 64 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
# self-driving-desktop

Desktop Automation framework

_tested on linux/gnome3_

Desktop Automation Framework.
Drive your keyboard and mouse with text files.

```
python main.py --playlist path/to/file.txt
```
pip install self-driving-desktop
### Development Setup

```
virtualenv --python python3 penv
source penv/bin/activate
pip install -r requirements.txt
sdd playlist.txt [--record]
```

### Playlists
Expand Down Expand Up @@ -93,75 +85,63 @@ play main;

### Grammar

Top-level:
#### Top-level:

- file has steps and playlists
- steps are the only thing run
- play runs a playlist

Steps:
#### Steps:

- `play name+;`: run one or more playlists
- `play name nameB ...;`: run one or more playlists
- `delay x.y;`: set delay between steps to x.y seconds
- `sleep x.y;`: sleep for x.y seconds
- `mv x y s;`: move the mose to x,y in s seconds
- `click;`: click the left mouse button
- `wspace_down;`: move one workspace down
- `wspace_up;`: move one workspace up
- `hotkeys "quoted" "keys" ...;`: press some keys together
- `write "quoted string\n";`: type a string, "\n" is enter
- `shell "quoted strings"+;`: exec a command from the program
- `name_active someName;`: name the active window
- `focus someName;`: focus a named window


```
start: item+
item: step ";" | playlist ";"
playlist : "playlist" WORD playlist_body
playlist_body : "{" (step ";")* "}"
step : play
| sleep
| mv
| wspace_down
| wspace_up
| hotkeys
| write
| shell
| name_active
| focus
windows:

play: "play" WORD+
name_active: "name_active" WORD
focus: "focus" WORD
- `active someName;`: name the active window
- `focus someName;`: focus a named window

sleep: "sleep" number
mv: "mv" number number number
mouse:

wspace_down: "wspace_down"
wspace_up: "wspace_up"
- `mouse x y s;`: move the mose to x,y in s seconds
- `click;`: click the left mouse button
- `btnclick [left,middle,right];`
- `btndown [left,middle,right];`
- `btnup [left,middle,right];`
- `draw [left,middle,right] x y s;`: move the mose to x,y in s seconds
- `scroll n;`: scroll n lines, negative is up
- `hscroll n;`: horizontal scroll n "clicks", negative is left

keyboard:

- `keypress "key";`
- `keydown "key";`
- `keyup "key";`
- `hotkeys "quoted" "keys" ...;`: press some keys together
- `write "quoted string\n";`: type a string, "\n" is enter

hotkeys: "hotkeys" string+
write: "write" string number?
shell: "shell" string+
clipboard:

- `copy;`, just `ctrl-c`
- `paste;`, just `ctrl-v`
- `save_clipboard "name";` save the clipboard contents to "name"
- `load_clipboard "name";` load the clipboard contents from "name"
- `copy_clipboard "name";` copy && save the clipboard contents to "name"
- `paste_clipboard "name";` load the clipboard contents from "name" && paste

number: SIGNED_NUMBER
string: ESCAPED_STRING
all keys are from pyautogui

COMMENT: /#[^\n]*/
WORD: LETTER+
[Grammer Definition](./self_driving_desktop/grammar.py)

%import common.LETTER
%import common.ESCAPED_STRING
%import common.INT
%import common.SIGNED_NUMBER
%import common.WS
### Development Setup

%ignore COMMENT
%ignore WS
```
virtualenv --python python3 penv
source penv/bin/activate
pip install -r requirements.txt
export PYTHONPATH=.
python self_driving_desktop/__main__.py ...
```

138 changes: 18 additions & 120 deletions self_driving_desktop/__init__.py
Original file line number Diff line number Diff line change
@@ -1,133 +1,31 @@
import time
import click
import pyautogui
import subprocess
from lark import Lark
from Xlib import display, X

from self_driving_desktop import parser as ourlang
from self_driving_desktop import parser as P
from self_driving_desktop import grammar as G
from self_driving_desktop import recorder as R

d = display.Display()

playlists = {}
wins = {}

def do(t):
if t.data == "item":
do(t.children[0])
return

if t.data == "play":
for playname in t.children:
playlist = playlists[playname]
do(playlist)

return

if t.data == "name_active":
name = t.children[0]
w = d.get_input_focus().focus
wins[name] = w
return

if t.data == "focus":
name = t.children[0]
w = wins[name]
w.set_input_focus(X.RevertToNone, X.CurrentTime)
w.configure(stack_mode=X.Above)
d.sync()
return

if t.data == "comment":
return

if t.data == "string":
text = t.children[0][1:-1]
text = text.replace("\\n", "\n")
return text

if t.data == "int":
return int(t.children[0])

if t.data == "number":
return float(t.children[0])

if t.data == "playlist":
name, pl = t.children
playlists[name] = pl
return

if t.data == "playlist_body":
for c in t.children:
do(c)

return

if t.data == "step":
step = t.children[0]
do(step)
return

if t.data == "sleep":
sec = do(t.children[0])
time.sleep(sec)
return

if t.data == "mv":
cs = []
for c in t.children:
cs.append(do(c))

pyautogui.moveTo(*cs, pyautogui.easeOutQuad)
return

if t.data == "click":
pyautogui.click()
return

if t.data == "hotkeys":
cs = []
for c in t.children:
cs.append(do(c))

pyautogui.hotkey(*cs)
return

if t.data == "shell":
cs = []
for c in t.children:
cs.append(do(c))

subprocess.Popen(cs)
return

if t.data == "write":
text = do(t.children[0])
interval=0.1
if len(t.children) == 2:
interval = do(t.children[1])

pyautogui.typewrite(text, interval=interval)
return

if t.data == "wspace_up":
pyautogui.hotkey('ctrl', 'alt', 'up')
return
@click.command()
@click.argument('playlist')
@click.option('--record', is_flag=True, help='Record to a playlist.')
def drive(playlist, record):
if record is True:
doRecord(playlist)
else:
doPlay(playlist)

if t.data == "wspace_down":
pyautogui.hotkey('ctrl', 'alt', 'down')
return

raise SyntaxError('Unknown instruction: %s' % t.data)

@click.command()
@click.option('--playlist', default="test.txt", help='Playlist to run.')
def drive(playlist):
parser = Lark(ourlang.grammar, parser='lalr')
def doPlay(playlist):
parser = Lark(G.grammar, parser='lalr')

with open(playlist) as f:
tree = parser.parse(f.read())
# print(tree)
# print("="*16)
for t in tree.children:
do(t)
P.do(t)

def doRecord(playlist):
R.do(playlist)

79 changes: 79 additions & 0 deletions self_driving_desktop/grammar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
grammar = r"""
start: (item ";")+
item: playlist | step
playlist : "playlist" WORD playlist_body
playlist_body : "{" (step ";")* "}"
step : play
| active
| focus
| delay
| sleep
| shell
| drag
| mouse
| click
| btndown
| btnup
| scroll
| hscroll
| keydown
| keyup
| hotkeys
| write
| copy
| paste
| save_clipboard
| load_clipboard
| copy_clipboard
| paste_clipboard
play: "play" WORD+
active: "active" WORD
focus: "focus" WORD
delay: "delay" number
sleep: "sleep" number
shell: ("shell"|"sh") string+
mouse: ("mouse"|"mv"|"mm") number number number
drag: ("drag"|"md") string number number number
click: "click"
btnclick: ("btnclick"|"bc") string
btndown: ("btndown"|"bd") string
btnup: ("btnup"|"bu") string
scroll: "scroll" int
hscroll: "hscroll" int
keypress: ("keypress"|"kp") string
keydown: ("keydown"|"kd") string
keyup: ("keyup"|"ku") string
hotkeys: ("hotkeys"|"hk") string+
write: ("write"|"w"|"type"|"t") string number?
copy: "copy"
paste: "paste"
save_clipboard: ("save_clipboard"|"scb") string
load_clipboard: ("load_clipboard"|"lcb") string
copy_clipboard: ("copy_clipboard"|"ccb") string
paste_clipboard: ("paste_clipboard"|"pcb") string
int: INT
number: SIGNED_NUMBER
string: ESCAPED_STRING
COMMENT: /#[^\n]*/
WORD: LETTER+
%import common.LETTER
%import common.ESCAPED_STRING
%import common.INT
%import common.SIGNED_NUMBER
%import common.WS
%ignore COMMENT
%ignore WS
"""
Loading

0 comments on commit d8ee374

Please sign in to comment.