3 import os |
3 import os |
4 import sys |
4 import sys |
5 import subprocess |
5 import subprocess |
6 import traceback |
6 import traceback |
7 import signal |
7 import signal |
|
8 import re |
8 from threading import Thread, Event, Lock |
9 from threading import Thread, Event, Lock |
9 from time import time as timesec |
10 from time import time as timesec |
|
11 from xml.sax.saxutils import escape as escape_xml |
10 |
12 |
11 import sikuli |
13 import sikuli |
12 |
14 |
13 beremiz_path = os.environ["BEREMIZPATH"] |
15 beremiz_path = os.environ["BEREMIZPATH"] |
14 python_bin = os.environ.get("BEREMIZPYTHONPATH", "/usr/bin/python") |
16 python_bin = os.environ.get("BEREMIZPYTHONPATH", "/usr/bin/python") |
15 opj = os.path.join |
17 opj = os.path.join |
16 |
18 |
17 tessdata_path = os.environ["TESSDATAPATH"] |
19 tessdata_path = os.environ["TESSDATAPATH"] |
|
20 |
|
21 ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]') |
|
22 def escape_ansi(line): |
|
23 return ansi_escape.sub('', line) |
|
24 |
|
25 def escape(txt): |
|
26 return escape_xml(escape_ansi(txt)) |
18 |
27 |
19 class KBDShortcut: |
28 class KBDShortcut: |
20 """Send shortut to app by calling corresponding methods. |
29 """Send shortut to app by calling corresponding methods. |
21 |
30 |
22 example: |
31 example: |
197 exemple (str): path relative to exemples directory |
207 exemple (str): path relative to exemples directory |
198 |
208 |
199 Returns: |
209 Returns: |
200 Sikuli App class instance |
210 Sikuli App class instance |
201 """ |
211 """ |
202 sikuli.OCR.Options().dataPath(tessdata_path) |
212 self.ocropts = sikuli.OCR.globalOptions() |
203 sikuli.OCR.Options().oem(0) |
213 self.ocropts.dataPath(tessdata_path) |
204 |
214 self.ocropts.oem(0) |
205 self.screenshotnum = 0 |
215 self.ocropts.smallFont() |
|
216 |
|
217 self.imgnum = 0 |
206 self.starttime = timesec() |
218 self.starttime = timesec() |
207 self.screen = sikuli.Screen() |
219 self.screen = sikuli.Screen() |
208 |
220 |
209 self.report = open("report.html", "w") |
221 self.report = open("report.xhtml", "w") |
210 self.report.write("""<!doctype html> |
222 self.report.write("""\ |
211 <html> |
223 <html xmlns="http://www.w3.org/1999/xhtml"> |
212 <head> |
224 <head> |
213 <meta charset="utf-8"> |
225 <meta charset="utf-8"/> |
214 <meta name="color-scheme" content="light dark"> |
226 <meta name="color-scheme" content="light dark"/> |
215 <title>Test report</title> |
227 <title>Test report</title> |
216 </head> |
228 </head> |
217 <body> |
229 <body> |
218 """) |
230 """) |
219 |
231 |
271 |
283 |
272 IDEIdleObserver.__init__(self) |
284 IDEIdleObserver.__init__(self) |
273 stdoutIdleObserver.__init__(self) |
285 stdoutIdleObserver.__init__(self) |
274 |
286 |
275 # stubs for common sikuli calls to allow adding hooks later |
287 # stubs for common sikuli calls to allow adding hooks later |
276 for name in ["click","doubleClick","type","rightClick","wait"]: |
288 for name, takes_matches in [ |
277 def makeMyMeth(n): |
289 ("click", True), |
|
290 ("doubleClick", True), |
|
291 ("type", False), |
|
292 ("rightClick", True), |
|
293 ("wait", False)]: |
|
294 def makeMyMeth(n,m): |
278 def myMeth(*args, **kwargs): |
295 def myMeth(*args, **kwargs): |
279 self.ReportScreenShot("Begin: " + n + "(" + repr(args) + "," + repr(kwargs) + ")") |
296 self.ReportScreenShot("Begin: " + n + "(" + repr(args) + "," + repr(kwargs) + ")") |
|
297 if m: |
|
298 args = map(self.handle_PFRML_arg, args) |
|
299 kwargs = dict(map(lambda k,v:(k,self.handle_PFRML_arg(v)), kwargs.items())) |
280 try: |
300 try: |
281 getattr(sikuli, n)(*args, **kwargs) |
301 getattr(sikuli, n)(*args, **kwargs) |
282 finally: |
302 finally: |
283 self.ReportScreenShot("end: " + n + "(" + repr(args) + "," + repr(kwargs) + ")") |
303 self.ReportScreenShot("end: " + n + "(" + repr(args) + "," + repr(kwargs) + ")") |
284 return myMeth |
304 return myMeth |
285 setattr(self, name, makeMyMeth(name)) |
305 setattr(self, name, makeMyMeth(name,takes_matches)) |
|
306 |
|
307 def handle_PFRML_arg(self, arg): |
|
308 if type(arg)==list: |
|
309 return self.findBest(*arg) |
|
310 if type(arg)==str and not arg.endswith(".png"): |
|
311 return self.findBest(arg) |
|
312 return arg |
|
313 |
|
314 def findBest(self, *args): |
|
315 #match = self.r.findBest(*args) |
|
316 match = None |
|
317 matches = sikuli.OCR.readWords(self.r) + sikuli.OCR.readLines(self.r) |
|
318 for m in matches: |
|
319 mText = m.getText().encode('ascii', 'ignore') |
|
320 for arg in args: |
|
321 if arg in mText: |
|
322 if match is None: |
|
323 match = m |
|
324 if mText == arg: |
|
325 match = m |
|
326 break |
|
327 if match is None: |
|
328 self.ReportText("Not found: " + repr(args) + " OCR content: ") |
|
329 for m in matches: |
|
330 self.ReportText(repr(m) + ": " + m.getText().encode('ascii', 'ignore')) |
|
331 raise Exception("Not Found: " + repr(args)) |
|
332 |
|
333 # translate match to screen ref |
|
334 #match.setTargetOffset(self.targetOffset) |
|
335 match.setTopLeft(match.getTopLeft().offset(self.targetOffset)) |
|
336 |
|
337 self.ReportTextImage("Found for " + repr(args) + ": " + |
|
338 " ".join([repr(match), repr(match.getTarget()), repr(match.getTargetOffset())]), |
|
339 self.screen.capture(match)) |
|
340 return match.getTarget() |
286 |
341 |
287 def dragNdrop(self, src, dst): |
342 def dragNdrop(self, src, dst): |
288 sikuli.drag(src) |
343 self.ReportScreenShot("Drag: (" + repr(src) + ")") |
|
344 sikuli.drag(self.handle_PFRML_arg(src)) |
289 sikuli.mouseMove(5,0) |
345 sikuli.mouseMove(5,0) |
290 sikuli.dropAt(dst) |
346 sikuli.dropAt(self.handle_PFRML_arg(dst)) |
|
347 self.ReportScreenShot("Drop: (" + repr(dst) + ")") |
291 |
348 |
292 def close(self): |
349 def close(self): |
293 self.sikuliapp.close() |
350 self.sikuliapp.close() |
294 self.sikuliapp = None |
351 self.sikuliapp = None |
295 self.report.write(""" |
352 self.report.write(""" |
302 self.sikuliapp.close() |
359 self.sikuliapp.close() |
303 IDEIdleObserver.__del__(self) |
360 IDEIdleObserver.__del__(self) |
304 stdoutIdleObserver.__del__(self) |
361 stdoutIdleObserver.__del__(self) |
305 |
362 |
306 def ReportScreenShot(self, msg): |
363 def ReportScreenShot(self, msg): |
|
364 cap = self.screen.capture(self.r) |
|
365 self.ReportTextImage(msg, cap) |
|
366 |
|
367 def ReportTextImage(self, msg, img): |
307 elapsed = "%.3fs: "%(timesec() - self.starttime) |
368 elapsed = "%.3fs: "%(timesec() - self.starttime) |
308 fname = "capture"+str(self.screenshotnum)+".png" |
369 fname = "capture"+str(self.imgnum)+".png" |
309 cap = self.screen.capture(self.r) |
370 img.save(".", fname) |
310 cap.save(".", fname) |
371 self.imgnum = self.imgnum + 1 |
311 self.screenshotnum = self.screenshotnum + 1 |
372 self.report.write( "<p>" + escape(elapsed + msg) + "<br/><img src=\""+ fname + "\"/>" + "</p>") |
312 self.report.write( "<p>" + elapsed + msg + "<br/><img src=\""+ fname + "\">" + "</p>") |
|
313 |
373 |
314 def ReportText(self, text): |
374 def ReportText(self, text): |
315 elapsed = "%.3fs: "%(timesec() - self.starttime) |
375 elapsed = "%.3fs: "%(timesec() - self.starttime) |
316 self.report.write("<p>" + elapsed + text + "</p>") |
376 #res = u"<p><![CDATA[" + elapsed + text + "]]></p>" |
|
377 res = u"<p>" + escape(elapsed + text) + "</p>" |
|
378 self.report.write(res) |
317 |
379 |
318 def ReportOutput(self, text): |
380 def ReportOutput(self, text): |
319 elapsed = "%.3fs: "%(timesec() - self.starttime) |
381 elapsed = "%.3fs: "%(timesec() - self.starttime) |
320 sys.stdout.write(elapsed + text) |
382 sys.stdout.write(elapsed + text) |
321 self.report.write("<pre>" + elapsed + text + "</pre>") |
383 self.report.write("<pre>" + escape(elapsed + text) + "</pre>") |
322 |
384 |
323 |
385 |
324 class AuxiliaryProcess(stdoutIdleObserver): |
386 class AuxiliaryProcess(stdoutIdleObserver): |
325 def __init__(self, beremiz_app, command): |
387 def __init__(self, beremiz_app, command): |
326 self.app = beremiz_app |
388 self.app = beremiz_app |