def __init__(self): self.language = unimacroutils.getLanguage() # here the grammar is not loaded yet, but the ini file is present self.startInifile() #print 'requireTimes: %s, simpleOrExtended: %s'% (self.requireTimes, self.doKeystrokesExtended) self.constructGramSpec(inInit=1) ancestor.__init__(self)
def test_Something_in_unimacro(self): self.log('testing something') actions.doAction("W") lang = unimacroutils.getLanguage() self.assert_equal( "enx", lang, "testing should be done from an English speech profile, not: %s" % lang)
def test_Something_in_unimacro(self): print('testing something') lang = unimacroutils.getLanguage() self.assert_equal( "enx", lang, "testing should be done from an English speech profile, not: %s" % lang) # no main statement, run from command in _unimacrotest.py.
moveRateChange = 2.0 fastSlowChange = 3.0 veryChange = 3.0 Counts = [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '25', '30', '35', '40' ] # flag for displaying all results through the DisplayMessage function # gives an error in the English version when saying "wait", change therefore # in "hold on" showAll = 1 language = unimacroutils.getLanguage() normalSet = ['startMoving', 'startMousing', 'startRepeating', 'startSearching'] ############################################################################ # # Here are some of our instisnce variables # # self.inTimer set when the timer callback in installed # self.curSpeed current movement speed (milliseconds for timer) # ancestor = natbj.IniGrammar class ThisGrammar(ancestor): # when we unload the grammar, we must make sure we clear the timer # callback so we keep a variable which is set when we currently own
class ThisGrammar(ancestor): language = unimacroutils.getLanguage() #Step 1, choose one of next three grammar rules: # the <integer> rule comes from these grammar rules try: number_rules = natbj.numberGrammarTill999[language] except KeyError: print('take number grammar from "enx"') number_rules = natbj.numberGrammarTill999['enx'] #number_rules = natbj.numberGrammarTill999999[language] # including thousands #number_rules = natbj.numberGrammar[language] # including millions name = "number extended" # do not use "To" instead of "Through", because "To" sounds the same as "2" gramSpec = [ """ <testnumber1> exported = [<spacing>] (number <number>)+; <testnumber2> exported = number <integer> Through <integer>; <pagenumber> exported = page <integer> [Through <integer>]; <filenamelastpage> exported = filename last page; <pair> exported = Pair <number> And <number>; <number> = <integer> | Minus <integer> | <float> | Minus <float>; <spacingnumber> exported = number (<spacing>|<number>)+; <spacing> = {spacing}; """ ] gramSpec.append( "<banknumber> exported = IBAN {n00-99} {bankheader} <integer>;") gramSpec.append(number_rules) try: integer999 = natbj.numberGrammarTill999[language] except KeyError: print('take number grammar from "enx"') integer999 = natbj.numberGrammarTill999['enx'] ## 345567;345567 ## these are special rules for making numbers of specific length. ## when they appear to be usefull, I will try to make them available in other languages ## and other cases too: amsterdamZuidRule = """<numberaztotal> exported = <numberazone> | <numberaztwo> | <numberazone><numberaztwo>; <numberazone> = zesentachtig <numberthreedigits><numberthreedigits>; <numberaztwo> = point (<__thousandslimited> | <numbertwodigits><numbertwodigits>); <numbertwodigits> = {n0-9}{n0-9} | {n10-99}; <numberthreedigits> = {n0-9}{n0-9}{n0-9} | <__hundredslimited> | {n0-9} <numbertwodigits>; <__hundredslimited> = hundred | <__2to9before> hundred | hundred <__1to99after> ; <__thousandslimited> = thousand | <__2to9before> thousand | thousand <__1to99after> | <__2to9before> thousand <__1to9after>; <numberfourdigits> = <__thousandslimited> | <numbertwodigits> <numbertwodigits> ; <numbersixdigits> = {n0-9}+ | {n0-9} <numbertwodigits> {n0-9} <numbertwodigits> ; <__thousands> = duizend | (<__1to99>|<__hundreds>) duizend | duizend (<__1to99>|<__hundreds>) | (<__1to99>|<__hundreds>) duizend (<__1to99>|<__hundreds>); <__2to9before> = {n2-9}; <__1to99after> = {n1-99}; <__1to9after> = {n1-9}; """ ##8600 8650 86 2008 def gotResults___hundredslimited(self, words, fullResults): """will return only three digits as _sofar goes together with <__2to9before> and <__1to99after> """ didBeforeRules = ['__2to9before'] doInAfterRules = ['__1to99after', '__1to9after'] print('__hundredslimited, prevRule: %s, nextRule: %s, words: %s' % (self.prevRule, self.nextRule, words)) lenw = len(words) for i in range(lenw): if i == 0 and self.prevRule in didBeforeRules: pass else: self._hundreds = 100 if self.nextRule in doInAfterRules and i == lenw - 1: # leave the "adding" action to nextRule pass else: self._sofar += str(self._hundreds) self._hundreds = 0 def gotResults___thousandslimited(self, words, fullResults): """will return only four digits as _sofar goes together with <__2to9before> and <__1to99after> and possibly <__1to9after> """ didBeforeRules = ['__2to9before'] doInAfterRules = ['__1to99after', '__1to9after'] # print '__hundredslimited, prevRule: %s, nextRule: %s, words: %s'% (self.prevRule, self.nextRule, words) lenw = len(words) for i in range(lenw): if i == 0 and self.prevRule in didBeforeRules: pass else: self._thousands = 1000 if self.nextRule in doInAfterRules and i == lenw - 1: # leave the "adding" action to nextRule pass else: self._sofar += str(self._thousands) self._thousands = 0 def gotResults___1to99after(self, words, fullResults): """should be after __hundredslimited or __thousandslimited must be defined in doInAfterRules ofter the corresponding rule """ didBeforeRules = ['__hundredslimited', '__thousandslimited'] print('__1to99after, prevRule: %s, nextRule: %s, words: %s' % (self.prevRule, self.nextRule, words)) if len(words) > 1: raise ValueError( "rule __1to99after, expect only one word, got %s: %s" % (len(words), words)) numWords = self.getNumbersFromSpoken(words) num = numWords[0] if self.prevRule == '__hundredslimited': self._hundreds += num self._sofar += str(self._hundreds) self._hundreds = 0 elif self.prevRule == '__thousandslimited': self._thousands += num self._sofar += str(self._thousands) self._thousands = 0 else: print('__1to99after, no valid rule, got: %s, expected one of: %s' % (self.prevRule, didBeforeRules)) self._sofar += str(num) print('_sofar: %s' % self._sofar) def gotResults___1to9after(self, words, fullResults): """should be after __hundredslimited or __thousandslimited must be defined in doInAfterRules ofter the corresponding rule Identical with gotResults___1to99after """ didBeforeRules = ['__hundredslimited', '__thousandslimited'] print('__1to99after, prevRule: %s, nextRule: %s, words: %s' % (self.prevRule, self.nextRule, words)) if len(words) > 1: raise ValueError( "rule __1to99after, expect only one word, got %s: %s" % (len(words), words)) numWords = self.getNumbersFromSpoken(words) num = numWords[0] if self.prevRule == '__hundredslimited': self._hundreds += num self._sofar += str(self._hundreds) self._hundreds = 0 elif self.prevRule == '__thousandslimited': self._thousands += num self._sofar += str(self._thousands) self._thousands = 0 else: print('__1to99after, no valid rule, got: %s, expected one of: %s' % (self.prevRule, didBeforeRules)) self._sofar += str(num) print('_sofar: %s' % self._sofar) def gotResults___2to9before(self, words, fullResults): """together with __hundredslimited or __thousandslimited expect one word only, and treat with respect to the next rule should be defined in didBeforeRules of the corresponding rule """ # print '__2to9before, prevRule: %s, nextRule: %s, words: %s'% (self.prevRule, self.nextRule, words) if len(words) > 1: raise ValueError( "rule __2to9before, expect only one word, got %s: %s" % (len(words), words)) numWords = self.getNumbersFromSpoken(words, asStr=1) numStr = numWords[0] num = int(numStr) if self.nextRule == '__hundredslimited': self._hundreds = num * 100 if self.nextRule == '__thousandslimited': self._thousands = num * 1000 else: print( '__2to9before, nextRule is NOT __hundredslimited of __thousandslimited' ) self._sofar += numStr print('_sofar: %s' % self._sofar) ##8640 8600 #failed to get this working: #<listtupleargument> exported = (Tuple|List|Argument) (Number <number> | (Variable|String) <dgndictation> | None)+; def __init__(self): """start the inifile and add to grammar if needed a special rule """ self.startInifile() #print 'enableSearchCommands: %s'% self.enableSearchCommands if self.specialAmsterdamZuid: self.gramSpec.append(self.amsterdamZuidRule) ancestor.__init__(self) def gotBegin(self, moduleInfo): if self.checkForChanges: self.checkInifile() self.progInfo = unimacroutils.getProgInfo(moduleInfo) def initialize(self): if not self.language: print("no valid language in grammar " + __name__ + " grammar not initialized") return self.load(self.gramSpec) # if switching on fillInstanceVariables also fill numbers lists like {n1-9} or {number1to99} self.switchOnOrOff() def gotResultsInit(self, words, fullResults): # Step 2: # initialise the variables you want to collect the numbers in: # defining them here makes testing in gotResults easier self.number = '' # for most rules self.page = '' # for page rule self.through = '' # only for testnumber2 and pagenumber self.pair = '' # only for the pair rule... self.minus = False self.ibanHeader = None # for banknumber self.ibanCheck = None # for banknumber self.totalNumber = '' # for multiple parts (Amsterdam Zuid, spacingnumber) #self.listtupleargument = '' #self.Items = [] # for listtupleargumenthallo # this line can be used to get the correct fullResults to be used in testIniGrammar.py for # for debugging the rules! # print 'fullResults of number extended: %s'% fullResults def fillInstanceVariables(self): """fills the necessary instance variables in this grammar a special rule for Loes Wijers, Amsterdam Zuid. """ # search commands (for Arnoud): self.specialAmsterdamZuid = self.ini.getBool('general', 'special amsterdam zuid') self.filenamePagenumbersPrefix = self.ini.get( 'general', 'filename page numbers prefix') self.filenamePagenumbersPostfix = self.ini.get( 'general', 'filename page numbers postfix') self.lastPage, self.lastThroug = None, None # filename page numbers def gotResults_testnumber1(self, words, fullResults): # step 3: setting the number to wait for # because more numbers can be collected, the previous ones be collected first # if you expect only one number, this function can be skipped (but it is safe to call): self.collectNumber() result = self.hasCommon(words, 'number') if self.hasCommon(words, 'number'): if self.number: # flush previous number self.number = self.doMinus('number', 'minus') # convert to int self.outputNumber(self.number) self.waitForNumber('number') else: raise ValueError('invalid user input in grammar %s: %s' % (__name__, words)) def gotResults_testnumber2(self, words, fullResults): # step 4 also: if more numbers are expected, # you have to collect the previous number before asking for the new self.collectNumber() # self.minus is not relevant here, as this rule is about integers only... # can ask for 'number' or for 'through': if self.hasCommon(words, 'number'): self.waitForNumber('number') elif self.hasCommon(words, 'through'): self.waitForNumber('through') else: raise NumberError('invalid user input in grammar %s: %s' % (__name__, words)) def gotResults_spacingnumber(self, words, fullResults): self.collectNumber() self.totalNumber += self.number self.waitForNumber('number') def gotResults_spacing(self, words, fullResults): self.collectNumber() self.totalNumber += self.number self.waitForNumber('number') for w in words: spacingchar = self.getFromInifile(w, 'spacing') self.totalNumber += spacingchar def gotResults_pagenumber(self, words, fullResults): # step 4 also: if more numbers are expected, # you have to collect the previous number before asking for the new self.collectNumber() # self.minus is not relevant here, as this rule is about integers only... # can ask for 'number' or for 'through': if self.hasCommon(words, 'page'): self.waitForNumber('page') elif self.hasCommon(words, 'through'): self.waitForNumber('through') else: raise NumberError( 'invalid words in pagenumber rule in grammar %s: %s' % (__name__, words)) def gotResults_filenamelastpage(self, words, fullResults): # additional command, compose filename with the last called page number(s). # need variables in inifile section [general]: filename pagenumber prefix and filename pagenumber postfix # if these are not set, of if no page numbers are "remembered", do nothing if self.lastPage: if self.lastThroug: lp, lt = int(self.lastPage), int(self.lastThroug) if lt > lp: pagePart = '%s-%s' % (lp, lt) else: pagePart = self.lastPage else: pagePart = self.lastPage else: print( 'numbers extended: no page numbers command issued yet, skip command' ) return if not self.filenamePagenumbersPrefix: print( '%s: command "%s", please specify "filename page numbers prefix" in section [general] of inifile' % (' '.join(words), self.name)) if not self.filenamePagenumbersPostfix: print( '%s: command "%s", please specify "filename page numbers postfix" in section [general] of inifile' % (' '.join(words), self.name)) if self.filenamePagenumbersPrefix and self.filenamePagenumbersPostfix: fName = self.filenamePagenumbersPrefix + pagePart + self.filenamePagenumbersPostfix action("SCLIP %s" % fName) def gotResults_pair(self, words, fullResults): # here a bit different logic for the place to collect the previous number. # self.pair = 'pair' if self.hasCommon(words, 'and'): self.collectNumber() self.number = self.doMinus('number', 'minus') self.waitForNumber('pair') else: self.waitForNumber('number') def gotResults_banknumber(self, words, fullResults): """get first 8 characters from bank name (list), rest from numbers grammar """ self.ibanHeader = self.getFromInifile(words[-1], 'bankheader') self.ibanCheck = "%.2d" % self.getNumberFromSpoken(words[-2]) self.waitForNumber('number') def gotResults_number(self, words, fullResults): # step: when in this rule, the word Minus (or synonym or translation) has been spoken.. self.minus = True def gotResults_numberazone(self, words, fullResults): """first part of the number""" if not self.totalNumber: self.totalNumber = '86' # print 'totalNumber: ', self.totalNumber self.collectNumber() self.totalNumber += self.number self.number = '' # wait for a number of 6 digits, if not, do NOT output!! self.waitForNumber('number', 6) def gotResults_numberaztwo(self, words, fullResults): """second part of the number""" self.collectNumber() self.totalNumber += self.number self.number = '' self.totalNumber += '.' # wait for a number of 6 digits, if not, do NOT output!! self.waitForNumber('number', 4) # # # # <numbertwodigits> = {n0-9}{n0-9} | {n10-99}; # # # # <numberthreedigits> = {n0-9}+ | <__hundreds> | {n0-9} <numbertwodigits>; def gotResults_numbertwodigits(self, words, fullResults): numWords = self.getNumbersFromSpoken(words, asStr=1) numberString = ''.join(numWords) self._sofar += numberString def gotResults_numberthreedigits(self, words, fullResults): numWords = self.getNumbersFromSpoken(words, asStr=1) numberString = ''.join(numWords) self._sofar += numberString def gotResults_numberfourdigits(self, words, fullResults): numWords = self.getNumbersFromSpoken(words, asStr=1) numberString = ''.join(numWords) self._sofar += numberString def gotResults_numbersixdigits(self, words, fullResults): numWords = self.getNumbersFromSpoken(words, asStr=1) numberString = ''.join(numWords) self._sofar += numberString def gotResults(self, words, fullResults): # step 5, in got results collect the number: self.collectNumber() if self.totalNumber: # multiple numbers, AmsterdamZuid self.totalNumber += self.number print('gotResults totalNumber: ', self.totalNumber) if self.message: print( 'Error collecting the number, do NOT output the number...') print(self.message) else: self.outputNumber(self.totalNumber) if len(self.totalNumber) == 13 and self.totalNumber[-5] == '.': keystroke("{tab}") elif len(self.totalNumber) == 5 and self.totalNumber[0] == '.': keystroke("{tab}") return if self.page: ## page numbers rule (for child windows, adobe [acrord32] of pdf24.) self.lastPage, self.lastThroug = self.page, self.through print('setting lastPage and lastThroug: %s, %s' % (self.lastPage, self.lastThroug)) isTop = (self.progInfo[2] == 'top') ma = getMetaAction('pagestart', progInfo=self.progInfo) if not ma: print('no metaactions defined for pagestart, stop command %s' % self.progInfo.prog) if isTop: if self.through: keystroke(" page %s-%s" % (self.page, self.through)) else: keystroke(" page %s" % self.page) else: print("cannot finish page command for child window: %s" % repr(self.progInfo)) return action("<<pagestart>>") keystroke(self.page) if self.progInfo.prog == 'pdf24-creator' and self.through == '': self.through = self.page if self.through: ## only child window here: action("<<pagethrough>>") keystroke(self.through) action("<<pagefinish>>") return # page command elif self.pair: self.pair = self.doMinus('pair', 'minus') print("(%s, %s) " % (self.number, self.pair)) #elif self.listtupleargument: # print 'self.listtupleargument in gotResults: %s'% self.listtupleargument # if self.number: # self.number = self.doMinus('number', 'minus') # self.Items.append(self.number) # # if self.dictated: # self.Items.append(''.join(self.dictated)) # self.dictated = None # # result = repr(self.Items) # print 'result: %s'% self.Items # if self.listtupleargument == 'list': # print 'list: %s'% result # elif self.listtupleargument == 'tuple': # result = repr(tuple(self.Items)) # print 'tuple: %s'% result # else: # result = repr(tuple(self.Items)).replace(', ', ',') # result = result.replace(', ',',') # print 'argument: %s'% result # elif self.ibanCheck and self.ibanHeader: try: # print 'ibanheader: %s, number: %s'% (self.ibanHeader, self.number) result = Iban = iban.create_iban(self.ibanHeader[:2], self.ibanHeader[2:], self.number) except iban.IBANError as err: print('invalid iban %s, %s (%s)' % (self.ibanHeader, self.number, err)) return if result[2:4] == str(self.ibanCheck): keystroke(result) else: print('invalid check: %s (%s) ' % (self.ibanCheck, result)) elif self.number: # last part if all others failed: self.number = self.doMinus('number', 'minus') self.outputNumber(self.number) def outputNumber(self, number): if type(number) in [int, float]: number = str(number) keystroke(number) prog = unimacroutils.getProgName() if prog in ['iexplore', 'firefox', 'chrome', 'safari']: keystroke('{tab}') elif prog in ['natspeak']: keystroke('{enter}') elif prog in ['excel', 'winword', 'soffice']: keystroke('{tab}') def doMinus(self, number, minus): """return the minus version of number, is self.minus is set pass in the names of the number variable and the name of the minus variable. return the wanted number. """ Nstring = getattr(self, number) Nstring = Nstring.strip() Minus = getattr(self, minus) if not Nstring: setattr(self, minus, False) return '' if Minus: if Nstring.startswith('-'): Nstring = Nstring[1:] else: Nstring = '-' + Nstring setattr(self, minus, False) return Nstring
class ThisGrammar(ancestor): language = unimacroutils.getLanguage() iniIgnoreGrammarLists = ['count', 'taskcount', 'taskapplication'] # are set in this module # taskcount and taskapplication only in very special # case, see Arnoud... if language == "nld": name = "regels" numGram = natbj.numberGrammarTill999['nld'] else: name = "lines" numGram = natbj.numberGrammarTill999['enx'] gramSpec = """ <before> = Here; <linesthat> exported = (THAT | <lines> | <paras>) <action>; <linesnum> exported = <linenum> | <linenum> <action>; <lines> = line | this line | <before> line | {count} lines| <before> {count} lines | these {count} lines| (previous|next) (line | {count} lines); <paras> = para | this para | <before> para | <before> this para | {count} paras | <before> {count} paras | these {count} paras| (previous|next) (para | {count} paras); <linenum> = line (<integer> | <integer>(through<integer> | plus {count}) | back); <action> = <column> | {simpleaction} | <movecopyaction>; <column> = column <integer>; <movecopyaction> = (move | copy) (((down|up) {count})|(to <integer>)); """ + numGram + """ <base> exported = 'line base' (<integer>|off); """ def initialize(self): if not self.language: return self.load(self.gramSpec) self.setNumbersList('count', counts) # only for coupling with tasks search (Arnoud) if self.enableSearchCommands: self.setNumbersList('taskcount', self.tasksGrammar.taskCounts) self.setList('taskapplication', self.tasksGrammar.ini.get('application')) self.switchOnOrOff() self.mode = 'inactive' self.lastDirection = 'down' def gotBegin(self, moduleInfo): if self.checkForChanges: self.checkInifile() if self.prevModInfo == moduleInfo: return # changing sets, as moduleInfo changed ## if not moduleInfo[0]: # no valid module info, do nothing ## print 'no module info, skipping' ## return # if window changes reset base: self.maxBase = 0 self.base = 0 progInfo = unimacroutils.getProgInfo(modInfo=moduleInfo) if unimacroutils.matchWindow(self.ignore, progInfo=progInfo): ## print 'progInfo in ignore, skipping: %s'% self.ignore return if self.windowPolicy(moduleInfo, progInfo): if self.mode != 'active': self.activateAll() self.mode = 'active' else: pass ## print '%s, remains active'% self.getName() else: if self.mode != 'inactive': ## print '%s: non matching window deactivate: %s'% \ ## (self.getName(), moduleInfo[1]) self.deactivateAll() self.mode = 'inactive' else: pass ## print '%s, remains inactive'% self.getName() self.prevModInfo = moduleInfo self.progInfo = progInfo def gotResultsInit(self, words, fullResults): self.currentLine = None if self.lineNumbersModuloHundred: self.app = actions.get_instance_from_progInfo(self.progInfo) prog = self.progInfo.prog if self.app: self.currentLine = self.app.getCurrentLineNumber() #if self.currentLine: # print 'current line number for prog: %s: %s'% (prog, self.currentLine) #else: # print 'currentLine not found in app: %s'% prog self.previousLines = self.nextLines = 0 self.firstPart = '' self.line = 0 self.through = 0 self.newbase = 0 # for collecting a new base number self.numlines = 1 # for selecting line number plus count self.movecopyto = 0 # line number or number of lines to move or copy to self.action = None # for simple actions the action string or list # for movecopy actions set to ['move','to'], ['copy', 'up'] etc self.paras = False # normally lines self.column = 0 self.resetWordVariables() def resetWordVariables(self): """for the processing of the word copy paste commands """ # self.count is set in the wordspec rule and # self.wordAction is set in the rules wordspecifyaction and afterwordoptional. # self.direction is default 'right', but can be adapted to 'left' in wordspec rule. # in doWordAction, the action is only performed if variable have been set. # special case: self.count = 0, only click, in afterwordoptional. self.wordAction = None self.searchAction = None self.count = None self.direction = 'right' ## words: # word-action handling: def rule_words(self, words): """# rules to manage words (copy/paste etc) # optional part after a cut/copy action, to move mouse # to a new location and do a paste there: <wordspecifyaction> | <before> <wordspecifyaction> | <wordspecifyaction> <afterwordoptional> | <before> <wordspecifyaction> <afterwordoptional> """ print("never comes here! %s" % words) def subrule_wordspec(self, words): """word | {n2-20} words | word (left|right) | {n2-20} words (left|right) """ lenwords = len(words) print("wordspec, got: %s (%s)" % (words, lenwords)) for i, w in enumerate(words): if self.hasCommon(w, 'word'): self.count = 1 if i < lenwords - 1: if self.hasCommon(words[i + 1], 'left'): direction = 'left' elif self.hasCommon(w, 'words'): if not self.count: print('error in grammar lines, wordspec, count should have a value by now: %s\nprocessing word: %s (words: %s)'% \ (self.count, w, words)) if i < lenwords - 1: if self.hasCommon(words[i + 1], 'left'): self.direction = 'left' elif self.hasCommon(w, ['left', 'right']): continue # should have been processed already else: self.count = self.getNumberFromSpoken(w) if not self.count: print('error in grammar lines, wordspec, count been caught here: %s\nprocessing word: %s (words: %s)'% \ (self.count, w, words)) continue # process the count: if self.wordAction: self.doWordAction() # and flush if self.searchAction: self.doSearchAction() # and flush def subrule_wordspecifyaction(self, words): """<wordspec> <wordaction> | <wordaction> <wordspec> """ print("should never come in subrule_wordspecifyaction %s" % words) def subrule_wordaction(self, words): """{wordaction}| (search ({taskcount}|{taskapplication}))""" # wordactions, simple actions from inifile # search, coupling with tasks grammar for search command after a word # was selected, for Arnoud van den Eerenbeemt, QH august 2011 self.wordAction = self.getFromInifile(words, 'wordaction') if self.wordAction: if self.count: self.doWordAction() # only if data else: if self.hasCommon(words, 'search'): self.searchAction = ['search', words[-1]] if self.count: self.doSearchAction() def subrule_afterwordoptional(self, words): """{afterwordaction} | {afterwordaction} <wordspec> | <wordspec> {afterwordaction} """ print("afterwordoptional, got: %s" % words) self.wordAction = self.getFromInifile(words, 'afterwordaction') if not self.doMouseMoveStopClick(): self.count = None self.wordAction = 0 print('aborted doMouseMoveStop action, stop this rule') return # because <wordspec> is optional, test here if it is yet to come (nextRule) if self.count: # had wordspec rule: self.doWordAction() elif self.count is None and self.nextRule != 'wordspec': print('no word specification in afterwordoptional') self.count = 0 # go with a single click if no wordspec has been given self.doWordAction() def doMouseMoveStopClick(self): """wait for mouse starting to move, stopping and then click """ action("ALERT") if not action("WAITMOUSEMOVE"): action("ALERT 2") return if not action("WAITMOUSESTOP"): return action("ButtonClick") action("ALERT") return 1 def doWordAction(self): """process count, direction and action if done, reset variables, if variables missing, do nothing """ if self.count is None or self.wordAction is None: print('not ready for word action: %s, %s, %s' % (self.count, self.direction, self.wordAction)) return if self.count == 0: #print 'doWordAction, single click: %s, %s, %s'% (self.count, self.direction, self.wordAction) pass else: #print 'doWordAction (select words): %s, %s, %s'% (self.count, self.direction, self.wordAction) wordSelect = "SELECTWORD %s, %s" % (self.count, self.direction) action(wordSelect) if self.wordAction: action(self.wordAction) self.resetWordVariables() def doSearchAction(self): """process count, direction and action, in this case a search into another window (coupling with tasks grammar) if done, reset variables, if variables missing, do nothing """ if self.count is None or self.searchAction is None: print('not ready for word action: %s, %s, %s' % (self.count, self.direction, self.wordAction)) return if self.count == 0: #print 'doWordAction, single click: %s, %s, %s'% (self.count, self.direction, self.wordAction) pass else: #print 'doWordAction (select words): %s, %s, %s'% (self.count, self.direction, self.wordAction) wordSelect = "SELECTWORD %s, %s" % (self.count, self.direction) action(wordSelect) if self.searchAction: print('now the search action in the tasks grammar: %s' % self.searchAction) self.tasksGrammar.rule_searchinothertask(self.searchAction) self.resetWordVariables() def gotResults_linenum(self, words, fullResults): """starting a linenum rule when the command is line back, this is intercepted immediately otherwise the waiting for a number is started """ if self.hasCommon(words, 'back'): self.firstPart = 'lineback' return self.firstPart = 'linesnum' self.lastDirection = 'down' self.collectNumber() if self.hasCommon(words, ['line']): self.waitForNumber('line') elif self.hasCommon(words, ['through']): self.waitForNumber('through') elif self.hasCommon(words, ['plus']): gotCounts = self.getNumbersFromSpoken(words, counts) if gotCounts: self.numlines = gotCounts[-1] + 1 def gotResults_that(self, words, fullResults): self.firstPart = 'that' def gotResults_lines(self, words, fullResults): self.lastDirection = 'down' self.firstPart = 'lines' if self.hasCommon(words, ['previous']): self.previousLines = 1 self.lastDirection = 'up' if self.hasCommon(words, ['next']): self.nextLines = 1 if self.hasCommon(words, ['line']): self.numlines = 1 if self.hasCommon(words, ['lines']): getCounts = self.getNumbersFromSpoken(words, counts) if getCounts: if len(getCounts) > 1: print('warning, more counts found: %s (take the first)' % getCounts) self.numlines = getCounts[0] else: print('should collect a count, nothing found, take 1') def gotResults_paras(self, words, fullResults): self.lastDirection = 'down' self.firstPart = 'paras' if self.hasCommon(words, ['previous']): self.previousLines = 1 self.lastDirection = 'up' if self.hasCommon(words, ['next']): self.nextLines = 1 if self.hasCommon(words, ['para']): self.numparas = 1 if self.hasCommon(words, ['paras']): getCounts = self.getNumbersFromSpoken(words, counts) if getCounts: if len(getCounts) > 1: print('warning, more counts found: %s (take the first)' % getCounts) self.numlines = getCounts[0] else: print('should collect a count, nothing found, take 1') def gotResults_action(self, words, fullResults): self.action = self.getFromInifile(words, 'simpleaction') def gotResults_column(self, words, fullResults): """wait for integer, action in gotResults """ self.collectNumber() self.waitForNumber('column') def gotResults_movecopyaction(self, words, fullResults): self.collectNumber() self.action = [None, None] if self.hasCommon(words, ['move']): self.action[0] = 'move' elif self.hasCommon(words, ['copy']): self.action[0] = 'copy' if self.hasCommon(words, ['down']): self.action[1] = 'down' self.movecopyto = self.getNumberFromSpoken(words[-1], counts) elif self.hasCommon(words, ['up']): self.action[1] = 'up' self.movecopyto = self.getNumberFromSpoken(words[-1], counts) elif self.hasCommon(words, ['to']): self.action[1] = 'to' self.waitForNumber('movecopyto') print('movecopyto: %s (%s)' % (self.movecopyto, type(self.movecopyto))) def gotResults_before(self, words, fullResults): if self.hasCommon(words, 'here'): natlinkutils.buttonClick('left', 1) def gotResults_base(self, words, fullResults): if self.hasCommon(words, ['off']): self.base = 0 self.DisplayMessage('resetting line base to 0') else: self.waitForNumber('newbase') def gotResults(self, words, fullResults): comment = 'command: %s' % ' '.join(words) self.prog = self.progInfo.prog self.collectNumber() #print 'lines command: %s (direction: %s)'% (comment, self.lastDirection) #print 'type line: %s'% type(self.line) #print 'base: %(base)s, line: %(line)s, through: %(through)s, ' \ # 'movecopyto: %(movecopyto)s, action: %(action)s'% self.__dict__ if self.movecopyto and self.action[1] == 'to': intLine = int(self.movecopyto) if intLine >= 100 or self.movecopyto.startswith('0'): self.movecopyto = intLine # always absolute elif self.currentLine: intLine = getLineRelativeTo(intLine, self.currentLine) self.movecopyto = intLine else: self.movecopyto = intLine if self.line: intLine = int(self.line) #print 'intLine: %s, currentLine: %s'% (intLine, self.currentLine) if len(self.line) > 2: self.line = intLine # always absolute elif self.currentLine: intLine = getLineRelativeTo(intLine, self.currentLine) print('getLineRelativeTo, old: %s new: %s (currentline: %s)' % (self.line, intLine, self.currentLine)) self.line = intLine else: self.line = intLine if self.through: intThrough = int(self.through) if intThrough > self.line: self.through = intThrough # always absolute else: if len(self.through) == 2: modulo = 100 else: modulo = 10 print('modulo for through: %s' % modulo) intThrough = getLineRelativeTo(intThrough, self.line, modulo=modulo, minLine=self.line) self.through = intThrough ## should not happen often: if self.line > self.through: # calculate the next higher number respective to self.line ndigits = len(repr(self.through)) steps = 10**ndigits leftPart = self.line // steps newThrough = leftPart * steps + self.through while newThrough < self.line: leftPart += 1 newThrough = leftPart * steps + self.through self.through = newThrough self.numlines = self.through - self.line + 1 #print 'line: "%(line)s", to: "%(through)s", movecopyto: "%(movecopyto)s"' \ # ', numlines "%(numlines)s", action: "%(action)s"'% self.__dict__ # doing the selection part: T = [] self.goingUp = 0 if self.previousLines: self.goingUp = 1 if self.nextLines: T.append('{extdown}') if self.firstPart == 'that': pass elif self.firstPart == 'lineback': T.append('<<lineback>>') elif self.firstPart == 'lines': if self.goingUp: T.append('<<selectpreviousline>>') if self.numlines > 1: T.append('<<selectup %s>>' % (self.numlines - 1, )) else: T.append('<<selectline>>') if self.numlines > 1: T.append('<<selectdown %s>>' % (self.numlines - 1, )) elif self.firstPart == 'paras': print('paragraphs!') if self.goingUp: print('vorige alinea') T.append('<<selectpreviouspara>>') if self.numlines > 1: T.append('<<selectparaup %s>>' % (self.numlines - 1, )) else: print('deze of volgende alinea') T.append('<<selectpara>>') if self.numlines > 1: T.append('<<selectparadown %s>>' % (self.numlines - 1, )) elif self.firstPart == 'linesnum': T.append('<<gotoline %s>>' % self.line) if self.numlines > 1: T.append('<<selectline>>') T.append('<<selectdown %s>>' % (self.numlines - 1, )) elif self.action != None: # only when you call for a single line without action, NO # selection is done T.append('<<selectline>>') t1 = time.time() action(''.join(T), comment=comment) t2 = time.time() ## print 'line select action: %s'% (t2-t1) T = [] # doing the action part: if self.column: keystroke("{right %s}" % self.column) return if not self.action: return if isinstance(self.action, str): action(self.action, comment=comment) elif type(self.action) == list: if self.action[0] == 'move': T.append('<<cut>>') elif self.action[0] == 'copy': T.append('<<copy>>') if not self.prog in ['excel']: if self.lastDirection == 'up': T.append('{extleft}') elif self.lastDirection == 'down': T.append('{extright}') else: raise LinesError('invalid movecopy action (first word): %s' % self.action) print('gotResult: movecopyto: %s (%s)' % (self.movecopyto, type(self.movecopyto))) print('gotResult: numlines: %s' % self.numlines) if self.action[1] == 'up': if self.prog in ['excel']: T.append("<<movetotopofselection>>") if self.prog in ['pycharm64', 'pycharm32']: print('pycharm, %s' % repr(T)) if self.action[0] == 'copy': T.append("{ctrl+d}") # duplicate T.append("{shift+ctrl+up %s}" % self.numlines) else: # all other applications T.append('{up %s}' % self.movecopyto) elif self.action[1] == 'down': if self.prog in ['excel']: T.append("<<movetobottomofselection>>") self.movecopyto += 1 if self.prog in ['pycharm64', 'pycharm32']: print('pycharm, %s' % repr(T)) if T and T[0] == "<<copy>>": T = ["{ctrl+d}"] ## duplicate else: T = [] T.append("{shift+ctrl+down %s}" % self.numlines) else: # all other applications: T.append('{down %s}' % self.movecopyto) elif self.action[1] == 'to': if self.action[0] == 'move' and not self.prog in ['excel']: # if new line below the current line, the cut lines have to be accounted for (not in excel though) currentLine = self.line or self.currentLine if currentLine: if self.movecopyto > currentLine + self.numlines: self.movecopyto -= self.numlines else: print( '_lines: movecopyaction: current line not known, move can be wrong if target is below current line' ) T.append('<<gotoline %s>>' % self.movecopyto) else: raise LinesError('invalid movecopy action (second word): %s' % self.action) T.append('<<movecopypaste>>') if self.numlines and not self.prog in ['excel']: T.append('{extup %s}{exthome}' % self.numlines) ## action('<<upafterpaste>>', comment=comment) T.append('<<afterlines>>') if T: t1 = time.time() action(''.join(T), comment=comment) t2 = time.time() ## print 'line action action: %s'% (t2-t1) def fillDefaultInifile(self, ini=None): """initialize as a starting example the ini file """ ini = ini or self.ini ancestor.fillDefaultInifile(self, ini) if self.language == 'nld': self.ini.set('simpleaction', 'selecteren', '') self.ini.set('simpleaction', 'verwijderen', '{del}') self.ini.set('simpleaction', 'knippen', '{ctrl+x}') self.ini.set('simpleaction', 'kopiejeren', '{ctrl+c}') self.ini.set('simpleaction', 'dupliceren', '{ctrl+c}{right}{ctrl+v}') self.ini.set('simpleaction', 'wissen', '{del}') self.ini.set('simpleaction', 'einde', '<<endafterselection>>') self.ini.set('simpleaction', 'over plakken', '{ctrl+v}') self.ini.set('simpleaction', 'plakken', '<<realhome>>{ctrl+v}') self.ini.set('simpleaction', 'over plakken', '{ctrl+v}') self.ini.set('simpleaction', 'over plakken', '{ctrl+v}') self.ini.set('general', 'deactivate', 'sol') self.ini.set('general', 'ignore', 'empty') elif self.language == 'enx': self.ini.set('simpleaction', 'select', '') self.ini.set('simpleaction', 'delete', '{del}') self.ini.set('simpleaction', 'cut', '{ctrl+x}') self.ini.set('simpleaction', 'copy', '{ctrl+c}') self.ini.set('simpleaction', 'duplicate', '{ctrl+c}{right}{ctrl+v}') self.ini.set('simpleaction', 'end', '<<endafterselection>>') self.ini.set('simpleaction', 'paste', '<<realhome>>{ctrl+v}') self.ini.set('simpleaction', 'paste over', '{ctrl+v}') self.ini.set('general', 'deactivate', 'sol') self.ini.set('general', 'ignore', 'empty') else: self.ini.set('simpleaction', 'select', '') self.ini.set('general', 'ignore', 'empty') def fillInstanceVariables(self, ini=None): """fills instance variables with data from inifile overload for grammar lines: get activate/deactivate windows """ try: #print 'fillInstantVariables for %s'% self ini = ini or self.ini self.lineNumbersModuloHundred = self.ini.getBool( 'general', 'line numbers modulo hundred') if self.lineNumbersModuloHundred: print( '_lines: do "line numbers modulo hundred" if the application allows this' ) self.activateRules = ini.getDict('general', 'activate') if not self.activateRules: self.activateRules = {'all': None} if '*' in self.activateRules: del self.activateRules['*'] self.activateRules['all'] = None for prog in self.activateRules: if self.activateRules[prog] == '*': self.activateRules[prog] = None ## print 'self.activateRules: %s'% self.activateRules self.deactivateRules = ini.getDict('general', 'deactivate') if not self.deactivateRules: self.deactivateRules = {'none': None} if '*' in self.deactivateRules: del self.deactivateRules['*'] self.deactivateRules['all'] = None for prog in self.deactivateRules: if self.deactivateRules[prog] == '*': self.deactivateRules[prog] = None ## print 'self.deactivateRules: %s'% self.deactivateRules self.ignore = ini.getDict('general', 'ignore') if not self.ignore: try: mgwn = {'nld': 'muisraster'}[self.language] except KeyError: mgwn = 'mouse grid' self.ignore = {'empty': None, 'natspeak': mgwn} for prog in self.ignore: if self.ignore[prog] == '*': self.ignore[prog] = None ## print 'self.ignore: %s'% self.ignore # Arnoud, same option needs to be set in _tasks self.tasksGrammarName = self.ini.get('general', 'enable search commands') self.tasksGrammar = self.enableSearchCommands = None # after a wordspec you can call "search number or task", numbers and tasks # corresponding with the _tasks grammar if self.tasksGrammarName: self.tasksGrammar = natbj.GetGrammarObject( self.tasksGrammarName) if self.tasksGrammar: self.enableSearchCommands = 1 print( '_lines, enable search commands, coupling grammar %s with %s' % (self.name, self.tasksGrammarName)) else: print( '_lines, enable search commands failed, invalid name: %s' % self.tasksGrammarName) self.enableSearchCommands = 0 except inivars.IniError: print('IniError while initialising ini variables in _lines') def windowPolicy(self, modInfo, progInfo=None): progInfo = progInfo or unimacroutils.getProgInfo(modInfo) ## print 'window policy------progInfo: ', `progInfo` if unimacroutils.matchWindow(self.activateRules, progInfo=progInfo): ## print 'matching activate: %s'% self.activateRules if not unimacroutils.matchWindow(self.deactivateRules, progInfo=progInfo): return 1
class ThisGrammar(ancestor): """grammar that makes html tags, as defined in an inifile """ language = unimacroutils.getLanguage() iniIgnoreGrammarLists = ['character'] name = "tags" gramSpec = """ <tags> exported = (Tag | HTML Tag | <prefix> Tag | <prefix> HTML Tag )({tagname}|{character}+); <prefix> = Open | Close | Begin | End | Empty; """ def initialize(self): if not self.language: print("no valid language in grammar "+__name__+" grammar not initialized") return self.load(self.gramSpec) self.setCharactersList('character') self.switchOnOrOff() def gotBegin(self,moduleInfo): if self.checkForChanges: self.checkInifile() def gotResultsInit(self,words,fullResults): self.letters = '' self.pleft = '' self.pright = '' self.dictated = '' self.onlyOpen = self.onlyClose = self.empty = 0 def gotResults_tags(self,words,fullResults): self.letters = self.getFromInifile(words, 'tagname', noWarning=1) if not self.letters: print('_tags, no valid tagname found: %s'% words) return for w in words: char = self.getCharacterFromSpoken(w) if char: self.letters += char #def gotResults_dgndictation(self,words,fullResults): # """return dictated text, put in between (or before or after the tags) # """ # self.dictated, dummy = nsformat.formatWords(words, state=-1) # no capping, no spacing # #print '-result of nsformat: |%s|'% repr(self.dictated) def gotResults_prefix(self,words,fullResults): self.empty = self.hasCommon(words, ['Empty', 'Lege']) self.onlyOpen = self.hasCommon(words, ['Begin', 'Open']) self.onlyClose = self.hasCommon(words, ['Sluit', 'Close', 'End', 'Eind']) def gotResults(self,words,fullResults): tag = self.letters.strip() ## print 'rule gotResults: %s'% tag pleft = pright = "" if not tag: return pleft = '<%s>' % tag if tag.find(' ') >= 0: endTag = ' '.split(tag)[0] else: endTag = tag pright = '</%s>' % endTag # see of something selected, leave clipboard intact cb = Clipboard(save_clear=True) keystroke('{ctrl+x}') # try to cut the selection contents = cb.Get_text() #.replace('\r','').strip() keystroke(pleft) if contents: #print 'contents: |%s|'% repr(contents) keystroke(contents) keystroke(pright) if not contents: # go back so you stand inside the brackets: nLeft = len(pright) keystroke('{left %s}'% nLeft) def fillDefaultInifile(self, ini): """filling entries for default ini file """ ancestor.fillDefaultInifile(self, ini) if self.language == 'nld': tagNames = { 'Hedder 1': 'h1', 'Hedder 2': 'h2', 'Hedder 3': 'h3', 'tabel': 'table', 'tabel honderd procent': 'table border="0" cellpadding="0" cellspacing="0" width="100%"', 'tabel data': 'td', 'tabel roo': 'tr', 'Java script': 'script language="JavaScript"', 'script': 'script' } elif self.language == 'enx': tagNames = { 'Header 1': 'h1', 'Header 2': 'h2', 'Header 3': 'h3', 'table': 'table', 'table data': 'td', 'table row': 'tr', 'Java script': 'script language="JavaScript"', 'script': 'script' } else: print('-----filling ini file %s , invalid language: "%s"! '% \ (self.__module__, self.language)) ini.set('general', 'error', 'invalid language') return for k, v in list(tagNames.items()): ini.set('tagname', k, v) # by default switch off initially: ini.set('general', 'initial on', '0')
class UnittestGrammar(natbj.IniGrammar): language = unimacroutils.getLanguage() name = 'unimacro test' iniIgnoreGrammarLists = ['tests'] # are set in this module gramSpec = """ <onetest> exported = unimacro test {tests}; <alltests> exported = unimacro test (all); """ def initialize(self): self.load(self.gramSpec) self.activateAll() self.switchOnOrOff() self.setList( 'tests', self.getTestNames()) # also fill self.testNames, self.allTests self.filesToSkipInResult = ["unittest.py", "TestCaseWithHelpers.py"] def gotBegin(self, moduleInfo): if self.checkForChanges: self.checkInifile() self.setList('tests', self.getTestNames()) def gotResults_onetest(self, words, fullResults): """do one of the unittests""" test = words[-1] self.doAll = 0 print("---------------------------------do unimacro unittest for: %s" % test) self.doUnitTests([test]) def gotResults_alltests(self, words, fullResults): """do all the unittests""" print("---------------------------------do all unimacro unittests") tests = list(self.allTests.keys()) tests.sort() self.doAll = 1 self.doUnitTests(tests) def getTestNames(self): """get the test names, with side effect builds a dict of all the tests the result (the testNames) can then be filled in the list {tests} self.allTests (dict) contains the names: files entries """ self.testFolder = os.path.join(unimacroutils.getUnimacroDirectory(), "unimacro_test") testFiles = glob.glob(os.path.join(self.testFolder, "*test.py")) ## print 'testFiles: %s'% testFiles testNames = list(map(self.extractTestName, testFiles)) print('testNames: %s' % testNames) self.allTests = dict(list(zip(testNames, testFiles))) return testNames def extractTestName(self, fullPath): """remove path, .py and "Test" from filename to get pronouncable test""" name = os.path.basename(fullPath).lower() name = utilsqh.removeFromEnd(name, ".py", ignoreCase=1) name = utilsqh.removeFromEnd(name, "test", ignoreCase=1) return name def showInifile(self): commandExplanation = "Names of tests: \n%s\n---------------" % '\n'.join( self.testNames) super(UnittestGrammar, self).showInifile(commandExplanation=commandExplanation) def doUnitTests(self, tests): # self.checkSysPath(self.testFolder) # append unimacro_test if needed suiteAll = None self.activeTests = [] for test in tests: print('do test %s (%s)' % (test, self.allTests[test])) suite = self.addUnitTest(test, self.allTests[test]) if suite: self.activeTests.append(test) if suiteAll: suiteAll.addTest(suite) else: suiteAll = suite if suiteAll: natlink.setMicState('off') result = unittest.TextTestRunner().run(suiteAll) print('after testing------------------------------------') self.dumpResult(result, filesToSkip=self.filesToSkipInResult) natlink.setMicState('on') else: print("nothing valid to test") def addUnitTest(self, test, fileName): """do one of the unittests""" ## actions.Message("starting test %s"% fileName) modName = os.path.basename(fileName) modName = utilsqh.removeFromEnd(modName, ".py", ignoreCase=1) testMod = __import__(modName) testClassName = modName try: testClass = getattr(testMod, testClassName) except AttributeError: print( '****cannot find test class in test module, skipping test: %s' % testClassName) return suite = unittest.makeSuite(testClass, 'test') return suite def checkSysPath(self, folder): """add to sys.path if not present yet""" if folder in sys.path: return print('adding to path: %s' % folder) sys.path.append(folder) def dumpResult(self, testResult, filesToSkip=None): """dump into txt file "testresult" in unimacro_test folder slightly different version in voice coder (test_defs.py) """ alert = len(self.activeTests) > 0 testResultFile = os.path.join(self.testFolder, "testresult.txt") f = open(testResultFile, 'w') if testResult.wasSuccessful(): if alert: mes = "all succesful: %s" % self.activeTests actions.Message(mes, "Unimacro Tests succesful") else: mes = "test passed: %s" % self.activeTests[0] self.DisplayMessage(mes) f.write(mes + '\n' * 2) f.close() return f.write("results of tests: %s\n" % self.activeTests) f.write('\n--------------- errors -----------------\n') for case, tb in testResult.errors: f.write('\n---------- %s --------\n' % case) cleanTb = utilsqh.cleanTraceback(tb, filesToSkip) f.write(cleanTb) f.write('\n--------------- failures -----------------\n') for case, tb in testResult.failures: f.write('\n---------- %s --------\n' % case) cleanTb = utilsqh.cleanTraceback(tb, filesToSkip) f.write(cleanTb) f.close() mes = "there were errors with testing unimacro: %s\n\nPlease consult the outputfile: %s"% \ (self.activeTests, testResultFile) actions.Message(mes, "Unimacro Tests failure", alert=alert) self.openFileDefault(testResultFile, mode="edit", name='test result')
class ThisGrammar(ancestor): language = unimacroutils.getLanguage() #Step 1, choose one of next three grammar rules: # the <integer> rule comes from these grammar rules number_rules = natbj.numberGrammarTill999[ language] # hundreds, including a long string of digits #number_rules = natbj.numberGrammarTill999999[language] # including thousands #number_rules = natbj.numberGrammar[language] # including millions # the rules <integer> and <float> are already defined. name = "number simple" gramSpec = """ <testnumber> exported = Number <number>; <number> = <integer> | Minus <integer> | <float> | Minus <float>; """ + number_rules + """ """ def __init__(self): self.minus = False self.number = False super().__init__() def initialize(self): if not self.language: print("no valid language in grammar " + __name__ + " grammar not initialized") return self.load(self.gramSpec) # if switching on fillInstanceVariables also fill numbers lists like {n1-9} or {number1to99} self.switchOnOrOff() def gotResultsInit(self, words, fullResults): # Step 2: # you can initialise variables here, eg self.minus = None, self.number = ''. Not necessary, but # in some grammars more appropriate than in the next function. pass def gotResults_testnumber(self, words, fullResults): # step 3: specify the number you are waiting for # check if the word Minus is present, and then wait for the number named "number" # note in this example self.number is non existent until collectNumber has been visited. # self.minus will only be set if the rule number is visited. self.waitForNumber('number') self.minus = False def gotResults_number(self, words, fullResults): # step 3a: when in this rule, the word Minus (or synonym or translation) has been spoken.. self.minus = True def gotResults(self, words, fullResults): # step 4, in gotResults collect the number (as a string): self.collectNumber( ) # setting self.number, see self.waitForNumber above print('number from the _number simple grammar: %s' % self.number) if self.minus: self.number = '-' + self.number # disable when testing through unittestIniGrammar.py (in unimacro_test directory): self.outputNumber(self.number) def outputNumber(self, number): #pylint:disable=R0201 keystroke(number) #Step 5: # Here some extra postprocessing for different programs: prog = unimacroutils.getProgName() if prog in ['iexplore', 'firefox', 'chrome', 'safari']: keystroke('{tab}') elif prog in ['natspeak']: # DragonPad keystroke('{enter}') elif prog in ['excel']: keystroke('{tab}')
class BracketsGrammar(ancestor): language = unimacroutils.getLanguage() name = "brackets" def initialize(self): if not self.language: print("no valid language in grammar "+__name__+" grammar not initialized") return self.load(self.gramSpec) self.switchOnOrOff() def gotBegin(self,moduleInfo): if self.checkForChanges: self.checkInifile() if self.mayBeSwitchedOn == 'exclusive': self.switchOnOrOff() def gotResultsInit(self, words, fullResults): self.dictated = '' # analysis of dgndictation or dgnletters self.pleft = self.pright = '' # the left and right parts of the brackets if self.mayBeSwitchedOn == 'exclusive': print('recog brackets, switch off mic: %s'% words) natbj.SetMic('off') def importedrule_dgndictation(self, words): #do with nsformat functions: self.dictated, dummy = nsformat.formatWords(words, state=-1) # no capping, no spacing #print '-result of nsformat: |%s|'% repr(self.dictated) def rule_brackets(self, words): "<before> {brackets}+ [<dgndictation>]" for w in words: p = self.getFromInifile(w, 'brackets') if not p: print('no valid brackets found for word: "%s"'% w) continue #print 'brackets, found: %s, %s'% (w, p) if len(p) > 2 and p.find("|") > 0: pList = p.split("|") newpleft = pList[0] newpright = pList[1] else: lenph = int(len(p)/2) newpleft, newpright = p[:lenph], p[lenph:] # make more brackets together, from outer to inner: self.pleft = self.pleft + newpleft self.pright = newpright + self.pright #print 'pleft: "%s", pright: "%s"'% (repr(self.pleft), repr(self.pright)) def subrule_before(self, words): "(here|between|empty)+" self.here, self.between = False, False for w in words: if self.hasCommon(w, 'between'): # this is the trigger word, ignore self.between = True if self.hasCommon(w, 'here'): # this is the trigger word, ignore self.here = True if self.hasCommon(w, 'empty'): # this is the trigger word, ignore self.between = False def gotResults(self, words, fullResults): # see if something selected, leaving the clipboard intact # keystroke('{ctrl+x}') # try to cut the selection if self.between: cb = Clipboard(save_clear=True) action('<<cut>>') text = cb.get_text(waiting_interval=0.1, waiting_iterations=3) else: text = "" if self.here: unimacroutils.buttonClick('left', 1) unimacroutils.visibleWait() leftText = rightText = leftTextDict = rightTextDict = "" if text: # strip from clipboard text: text, leftText, rightText = stripFromBothSides((text)) if self.dictated.strip(): text, leftTextDict, rightTextDict = stripFromBothSides(self.dictated) elif self.dictated: # the case of only a space-bar: leftTextDict = self.dictated lSpacing = leftText + leftTextDict rSpacing = rightTextDict + rightText if lSpacing: keystroke(lSpacing) action(self.pleft) unimacroutils.visibleWait() if text: #print 'text: |%s|'% repr(text) keystroke(text) unimacroutils.visibleWait() action(self.pright) unimacroutils.visibleWait() if rSpacing: keystroke(rSpacing) if not text: # go back so you stand inside the (brackets): nLeft = len(self.pright) + len(rSpacing) keystroke('{left %s}'% nLeft) # [(hello)] def fillDefaultInifile(self, ini): """filling entries for default ini file complex """ if self.language == 'nld': ini.set('brackets', 'aanhalingstekens', '""') ini.set('brackets', 'kwoots', "''") ini.set('brackets', 'brekkits', '[]') ini.set('brackets', 'haakjes', '()') ini.set('brackets', 'punt haakjes', '<>') ini.set('brackets', 'driedubbele aanhalingstekens', '""""""') ini.set('brackets', 'accolades', '{}') ini.set('brackets', 'html punt haakjes', "<|>") ini.set('brackets', 'html brekkits', "[|]") else: ini.set('brackets', 'double quotes', '""') ini.set('brackets', 'quotes', "''") ini.set('brackets', 'single quotes', "''") ini.set('brackets', 'square brackets', '[]') ini.set('brackets', 'brackets', '()') ini.set('brackets', 'parenthesis', '()') ini.set('brackets', 'backticks', '``') ini.set('brackets', 'parens', '()') ini.set('brackets', 'angle brackets', '<>') ini.set('brackets', 'triple quotes', '""""""') ini.set('brackets', 'html angle brackets', "<|>") ini.set('brackets', 'html square brackets', "[|]") ini.set('brackets', 'braces', '{}')
class ThisGrammar(ancestor): language = unimacroutils.getLanguage() name = "latex" gramSpec = """ <dgndictation> imported; <structure> exported = begin {floating} [<dgndictation>]; <reference> exported = reference ({floating} | {label}) <dgndictation>; <namereference> exported = name reference ({floating} | {label}) <dgndictation>; <label> exported = label ({floating} | {label}) <dgndictation>; <command> exported = {commands} [with] [ (arguments [{arguments}]) | (that | ([this] line)) ] [(and | with) label]; <options> exported = add option [{options}]; <replace> exported = replace {placeholders} [with <dgndictation>]; """ def initialize(self): if not self.language: print("no valid language in grammar " + __name__ + " grammar not initialized") return self.load(self.gramSpec) self.switchOnOrOff() def gotBegin(self, moduleInfo): if self.checkForChanges: self.checkInifile() def gotResultsInit(self, words, fullResults): self.dictation = '' self.floating = '' self.label = 0 self.reference = '' self.namereference = '' self.label_text = '' self.Cmd = '' self.replace_text = 0 def gotResults_replace(self, words, fullResults): place = self.getFromInifile(words, 'placeholders', noWarning=1) action('<<startsearch>>') keystroke(place) action('<<searchgo>>') action('<<cancel>>') if self.hasCommon(words, ['with']): self.replace_text = 1 def gotResults_command(self, words, fullResults): initial = self.getFromInifile(words, 'commands', noWarning=1) pos = initial.find('{') print(pos) contents = '' if self.hasCommon(words, ['arguments']): if pos > 0: Cmd = initial[:pos + 1] else: Cmd = initial + '{' args = self.getFromInifile(words, 'arguments', noWarning=1) Cmd = Cmd + args + '}' print(Cmd) else: Cmd = initial if self.hasCommon(words, ['that']): contents = self.get_selection_that(line=0) if self.hasCommon(words, ['line']): contents = self.get_selection_that(line=1) stringpaste(Cmd) if pos > 0 or self.hasCommon(words, ['arguments']): keystroke('{left}') if contents: if pos == -1: stringpaste('{') stringpaste(contents) if pos == -1: stringpaste('}') else: keystroke('{right}') if self.hasCommon(words, ['label']): label = self.getFromInifile(words, 'label', noWarning=1) if label: ## workaround for keystrokes: {{} keystroke('{enter}') stringpaste(r'\label{}%s}' % (self.makes_label(label, contents))) keystroke('{enter}') def gotResults_options(self, words, fullResults): selection = self.view_selection_current_line() options = self.getFromInifile(words, 'options', noWarning=1) present = 1 squared = selection.find(']') only = 1 if squared == -1: squared = selection.find('{') present = 0 else: squared = squared + 1 start = selection.find('[') if start != squared - 2: only = 0 if squared == -1: keystroke('{end}') else: keystroke('{home}') for i in range(0, squared): keystroke('{right}') if present == 0: keystroke('[') if options: if present == 1: keystroke('{left}') if only == 0: keystroke(',') keystroke(options) if present == 0: keystroke(']') if not options: keystroke('{left}') def gotResults_reference(self, words, fullResults): self.reference = self.getFromInifile(words, 'floating', noWarning=1) or \ self.getFromInifile(words, 'label', noWarning = 1) def gotResults_namereference(self, words, fullResults): self.namereference = self.getFromInifile(words, 'floating', noWarning=1) or \ self.getFromInifile(words, 'label', noWarning = 1) def gotResults_label(self, words, fullResults): self.label_text = self.getFromInifile(words, 'floating', noWarning=1) or \ self.getFromInifile(words, 'label', noWarning = 1) def gotResults_structure(self, words, fullResults): self.floating = self.getFromInifile(words, 'floating', noWarning=1) def gotResults(self, words, fullResults): if self.floating: floatingString = r'\begin{%s}' % self.floating stringpaste(floatingString) keystroke('{enter}') if self.dictation: labelString = self.makes_label(self.floating, self.dictation) dictationString = r'\label{%s}' % labelString stringpaste(dictationString) keystroke('{enter}') floatingString = '\\end{%s}' % self.floating stringpaste(floatingString) keystroke('{up}') print('floating: %s' % self.floating) if self.reference: stringpaste('\\ref{%s}' % (self.makes_label(self.reference, self.dictation))) if self.namereference: stringpaste('\\nameref{%s}' % (self.makes_label(self.namereference, self.dictation))) if self.label_text: stringpaste('\\label{%s}' % (self.makes_label(self.label_text, self.dictation))) if self.replace_text: stringpaste(self.dictation) def gotResults_dgndictation(self, words, fullResults): """do with nsformat functions""" print('got dgndictation: %s' % words) self.dictation, dummy = nsformat.formatWords( words) # state not needed in call print(' result of nsformat: %s' % repr(self.dictation)) def get_selection_that(self, line=0): unimacroutils.saveClipboard() if line: action('<<selectline>><<cut>>') else: action('<<cut>>') contents = natlink.getClipboard().strip().replace('\r', '') if len(contents) == 0: if line: print('_latex, empty line') return "" action('HW select that') action('<<cut>>') contents = natlink.getClipboard().strip().replace('\r', '') if len(contents) == 0: print( '_latex, empty contents, no last dicatate utterance available' ) unimacroutils.restoreClipboard() return contents def view_selection_current_line(self): unimacroutils.saveClipboard() keystroke('{ctrl+c}') contents = natlink.getClipboard() if len(contents) == 0: print('no_space_by_existing selection') keystroke('{end}{shift+home}') keystroke('{ctrl+c}') contents = natlink.getClipboard() unimacroutils.restoreClipboard() return contents def makes_label(self, type, text): return type + ':' + ''.join(text.split()).lower()
class ThisGrammar(ancestor): """grammar recent folder dialog only to be switched on if dialog is there, if dialog is gone, the grammar rules set is deactivated """ language = unimacroutils.getLanguage() name = "natspeak dialog" setRecentFolderDialog = ["chooserfd", "okcancelrfd"] gramSpec = """ <chooserfd> exported = choose {number}; <okcancelrfd> exported = OK | Cancel; """ def initialize(self): if not self.language: print("no valid language in grammar " + __name__ + " grammar not initialized") return self.load(self.gramSpec) print('loaded: %s' % self.gramSpec) self.prevHndle = None def gotBegin(self, moduleInfo): """switch rfd (recent folders dialog) on if title matches what is provided in _folders grammar. """ hndle = moduleInfo[2] if hndle == self.prevHndle: return self.prevHndle = hndle if unimacroutils.matchModule('natspeak'): # try to reach data from _folders grammar: self.foldersGram = natbj.GetGrammarObject('folders') #print 'self.foldersGram: %s'% self.foldersGram if self.foldersGram is None: return dTitle = self.foldersGram.dialogWindowTitle dRange = self.foldersGram.dialogNumberRange if dTitle and unimacroutils.matchModule( 'natspeak', wantedTitle=dTitle, titleExact=1, caseExact=1): self.activateSet(self.setRecentFolderDialog) self.setNumbersList('number', dRange) return # other situations: self.deactivateAll() def gotResults_chooserfd(self, words, fullResults): """result from the recent folders dialog extract the number, then close the dialog and then call the relevant function from the folders grammar """ wNumList = self.getNumbersFromSpoken(words) # returns a string or None if len(wNumList) != 1: print('natspeak dialog: error in command, no number found: %s' % wNumList) return chooseNum = wNumList[0] self.exitDialog() self.foldersGram.gotoRecentFolder( chooseNum - 1) # remember choose is 1 based, the list is 0 based def gotResults_okcancelrfd(self, words, fullResults): print('dialog ok cancel: %s, just close the dialog' % words) self.exitDialog() def exitDialog(self): """finish the open dialog, normally by pressing enter""" dTitle = self.foldersGram.dialogWindowTitle if dTitle and unimacroutils.matchModule( 'natspeak', wantedTitle=dTitle, titleExact=1, caseExact=1): unimacroutils.rememberWindow() keystroke("{enter}") # exit dialog. unimacroutils.waitForNewWindow()
class UtilGrammar(ancestor): language = unimacroutils.getLanguage() iniIgnoreGrammarLists = ['gramnames', 'tracecount', 'message'] # are set in this module name = 'control' ## normalSet = ['show', 'edit', 'trace', 'switch', 'message'] ## exclusiveSet = ['showexclusive', 'message'] # commands for controlling module actions if actions: actionName = "| actions" else: actionName = "" gramSpec = """ <show> exported = show ('all grammars' | ([grammar|inifile] {gramnames}) """ + actionName + """); <edit> exported = edit ([grammar|inifile] {gramnames}""" + actionName + """); <trace> exported = trace [on|off| {tracecount}] ('all grammars' | [grammar] {gramnames}""" + actionName + """) [on|off| {tracecount}] ; <switch> exported = switch [on|off] ('all grammars' | [grammar] {gramnames})[on|off]; <voicecode> exported = 'switch on voicecode'; <showexclusive> exported = show exclusive [grammars]; <resetexclusive> exported = reset exclusive [grammars]; <message> exported = {message}; """ Mode = Normal LastMode = Normal CurrentWord = 0 Repeat = 0 def initialize(self): global loadedNames if not self.load(self.gramSpec, allResults=1): return None natbj.RegisterControlObject(self) self.emptyList('message') self.emptyList('gramnames') self.setList('tracecount', tracecount) self.activateAll() self.setMode(Normal) self.doMessages = None self.startExclusive = self.exclusive # exclusive state at start of recognition! ## if unimacroutils.getUser() == 'martijn': ## print 'martijn, set exclusive %s'% self.name ## self.setExclusive(1) def unload(self): natbj.UnRegisterControlObject(self) ancestor.unload(self) def gotBegin(self, moduleInfo): #Now is the time to get the names of the grammar objects and # activate the list for the <ShowTrainGrammar> rule if natbj.grammarsChanged: ## print 'new list for control: %s'% natbj.loadedGrammars.keys() self.setList('gramnames', list(natbj.loadedGrammars.keys())) natbj.ClearGrammarsChangedFlag() if self.checkForChanges: self.checkInifile() self.startExclusive = self.exclusive # exclusive state at start of recognition! def gotResultsInit(self, words, fullResults): if self.mayBeSwitchedOn == 'exclusive': print('recog controle, switch off mic: %s' % words) natbj.SetMic('off') if self.exclusive and self.doMessages: self.DisplayMessage('<%s>' % ' '.join(words)) def resetExclusiveMode(self): """no activateAll, do nothing, this grammar follows the last unexclusive grammar """ pass def setExclusiveMode(self): """no nothing, control follows other exclusive grammars """ pass def gotResultsObject(self, recogType, resObj): if natbj.IsDisplayingMessage: return if self.doMessages: mes = natbj.GetPendingMessage() if mes: mes = '\n\n'.join(mes) natbj.ClearPendingMessage() self.DisplayMessage(mes) return unimacroutils.doPendingBringUps() # if there were if self.startExclusive and self.exclusive and showAll: # display recognition results for exclusive grammars # with showAll = None (in top of this file), you can disable this feature if recogType == 'reject': URName = '' exclGram = natbj.exclusiveGrammars exclGramKeys = list(exclGram.keys()) if len(exclGramKeys) > 1 and self.name in exclGramKeys: exclGramKeys.remove(self.name) if len(exclGramKeys) > 1: URName = ', '.join(exclGramKeys) k = exclGramKeys[0] v = exclGram[k] # UE stands for unrecognised exclusive: # if more exclusive grammars, # the last one is taken URName = URName or v[ 2] # combined name or last rule of excl grammar if self.doMessages: self.DisplayMessage('<' + URName + ': ???>', alsoPrint=0) #Utilities for Filter Modes and other special modes def setMode(self, NewMode): self.LastMode = self.Mode self.Mode = NewMode def restoreMode(self): self.Mode = self.LastMode def gotResults_trace(self, words, fullResults): print('control, trace: %s' % words) if self.hasCommon(words, 'actions'): if self.hasCommon(words, 'show'): actions.debugActionsShow() elif self.hasCommon(words, 'off'): actions.debugActions(0) elif self.hasCommon(words, 'on'): actions.debugActions(1) elif words[-1] in tracecount: actions.debugActions(int(words[-1])) else: actions.debugActions(1) def gotResults_voicecode(self, words, fullResults): """switch on if requirements are fulfilled voicecodeHome must exist emacs must be in foreground """ wxmed = os.path.join(voicecodeHome, 'mediator', 'wxmediator.py') if os.path.isfile(wxmed): commandLine = r"%spython.exe %s > D:\foo1.txt >> D:\foo2.txt" % ( sys.prefix, wxmed) os.system(commandLine) else: print('not a file: %s' % wxmed) def gotResults_switch(self, words, fullResults): print('control, switch: %s' % words) if self.hasCommon(words, 'on'): func = 'switchOn' elif self.hasCommon(words, 'off'): func = 'switchOff' else: try: t = { 'nld': '<%s: ongeldig schakel-commando>' % self.GetName() }[self.language] except: t = '<%s: invalid switch command>' % self.GetName() if self.doMessages: self.DisplayMessage(t) return if self.hasCommon(words, 'all grammars'): print('%s all grammars:' % func) natbj.CallAllGrammarObjects(func, ()) print("-" * 10) else: gramname = self.hasCommon(words, list(natbj.loadedGrammars.keys())) if gramname: gram = natbj.loadedGrammars[gramname] gram.callIfExists(func, ()) else: print('no grammar name found: %s' % gramname) def gotResults_showexclusive(self, words, fullResults): if natbj.exclusiveGrammars: T = ['exclusive grammars:'] for e in natbj.exclusiveGrammars: T.append(e) T.append(self.name) if self.doMessages: self.DisplayMessage(' '.join(T)) else: if self.doMessages: self.DisplayMessage('no exclusive grammars') def gotResults_resetexclusive(self, words, fullResults): print('reset exclusive') if natbj.exclusiveGrammars: T = ['exclusive grammars:'] for e in natbj.exclusiveGrammars: T.append(e) T.append(self.name) T.append('... reset exclusive mode') if self.doMessages: self.DisplayMessage(' '.join(T)) natbj.GlobalResetExclusiveMode() unimacroutils.Wait(1) if self.doMessages: self.DisplayMessage('reset exclusive mode OK') else: if self.doMessages: self.DisplayMessage('no exclusive grammars') ## def setExclusive(self, state): ## """control grammar, do NOT register, set and maintain state ## ## special position because of ControlGrammar ## """ ## print 'control set exclusive: %s'% state ## if state == None: ## return ## if state == self.exclusive: ## return ## print 'control, (re)set exclusive: %s'% state ## self.gramObj.setExclusive(state) ## self.exclusive = state def gotResults_show(self, words, fullResults): # special case for actions: if self.hasCommon(words, 'actions'): actions.showActions() return if natbj.exclusiveGrammars: print('exclusive (+ control) are: %s' % ' '.join(list(natbj.exclusiveGrammars.keys()))) grammars = natbj.loadedGrammars gramNames = list(grammars.keys()) gramName = self.hasCommon(words, gramNames) if gramName: grammar = grammars[gramName] if not grammar.onOrOff: # off, show message: self.offInfo(grammar) return if not self.hasCommon(words, 'grammar'): try: grammar.showInifile() return except AttributeError: try: grammar.showGrammar() return except AttributeError: pass # now show the grammar in the browser application: if gramName: name = [gramName] else: name = words[1:-1] if (len(name) > 0) and ( name[0] == 'previous'): #shortcut: show previously assembled grammar pypath = '.' for x in sys.path: pypath = pypath + ';' + x os.environ['PYTHONPATH'] = pypath AppBringUp('Browser', Exec=PythonwinExe, Args='/app BrowseGrammarApp.py') else: All = 1 if len(name) > 0: All = self.hasCommon(words, 'all grammars') if self.hasCommon(words, ['all', 'active']): name = name[1:] if len(name) > 0: Start = (' '.join(name), []) else: Start = () print('start browsing with: %s' % All) self.Browse(Start, All) def gotResults_edit(self, words, fullResults): # special case for actions: if self.hasCommon(words, 'actions'): actions.editActions() return grammars = natbj.loadedGrammars gramNames = list(grammars.keys()) gramName = self.hasCommon(words[-1:], gramNames) if gramName: grammar = grammars[gramName] if self.hasCommon(words, 'grammar'): module = grammar.__module__ filename = unimacroutils.getModuleFilename(module) #print 'open for edit file: %s'% filename self.openFileDefault(filename, mode="edit", name='edit grammar %s' % gramName) unimacroutils.setCheckForGrammarChanges(1) else: # edit the inifile try: grammar.switchOn() grammar.editInifile() except AttributeError: if self.doMessages: self.DisplayMessage( 'grammar "%s" has no method "editInifile"' % gramName) return else: print('no grammar name found') def switchOff(self, **kw): """overload, this grammar never switches off """ print('remains switched on: %s' % self) def switchOn(self, **kw): """overload, just switch on """ self.activateAll() def offInfo(self, grammar): """gives a nice message that the grammar is switched off Gives also information on how to switch on. """ name = grammar.getName() try: t = { 'nld': [ 'Grammatica "%s" is uitgeschakeld' % name, '', '"Schakel grammatica %s in" om te activeren' % name ] }[self.language] title = {'nld': 'Grammatica %s' % name}[self.language] except KeyError: t = [ 'Grammar "%s" is switched off' % name, '"Switch On Grammar %s" to activate' % name ] title = 'Grammar %s' % name if natbj.loadedMessageGrammar: t = '; '.join(t) if self.doMessages: self.DisplayMessage(t) else: actions.Message(t)
class ThisGrammar(ancestor): language = unimacroutils.getLanguage() normalRules = ['calcnormal', 'cancel'] calcRules = ['calccalc', 'cancel'] # exclusive continueRules = ['calccontinue', 'cancel'] # exclusive #see grammar _numbers.py (or _numbers_simple.py or _numbers_extended.py) #for instructions on the (quite complicated) number rules number_rules = natbj.numberGrammarTill999[ language] # hundreds, including a long string of digits name = "calculator" gramSpec = """ <calcnormal> exported = calculate (<number>|<operator>)+ [equals]; <calccalc> exported = [calculate] (<number>|<operator>)+ [equals]; <calccontinue> exported = equals | (<number>|<operator>)+; <number> = <integer> | <float>; <operator> = {operator}; <cancel> exported = Cancel [Calculator] | Calculator Cancel; """ + number_rules + """ """ def initialize(self): if not self.language: print("no valid language in grammar " + __name__ + " grammar not initialized") return self.load(self.gramSpec) # if switching on fillInstanceVariables also fill numbers lists like {n1-9} or {number1to99} self.switchOnOrOff() self.inCalculation = False self.activeSet = None self.minus = '' self.number = '' self.prevModInfo = None self.prog = None self.exclusive = 0 self.lastResult = None self.startwithLastResult = False def gotBegin(self, moduleInfo): if self.checkForChanges: self.checkInifile() if self.prevModInfo != moduleInfo: progInfo = unimacroutils.getProgInfo(modInfo=moduleInfo) self.prevModInfo = moduleInfo self.cancelMode() self.prog = progInfo.prog if self.prog == 'calc': self.activeSet = None else: if self.exclusive: self.activeSet = None # reinitialize non exclusive if self.inCalculation: if self.prog != 'calc': if not self.activeSet == self.continueRules: self.activeSet = copy.copy(self.continueRules) self.exclusive = 1 self.activateSet(self.activeSet, exclusive=self.exclusive) print("calculation, exclusiveRules activated") else: if self.prog == 'calc': if not self.activeSet == self.calcRules: self.activeSet = copy.copy(self.calcRules) self.exclusive = 1 self.activateSet(self.activeSet, exclusive=self.exclusive) print("calc, exclusive rules activated") else: if not self.activeSet == self.normalRules: if self.activeSet: print("calculation, activate normalRules again") self.activeSet = copy.copy(self.normalRules) self.exclusive = 0 self.activateSet(self.activeSet, exclusive=self.exclusive) def gotResultsInit(self, words, fullResults): # Step 2: # you can initialise variables here, eg self.minus = None, self.number = ''. Not necessary, but # in some grammars more appropriate than in the next function. if self.inCalculation: pass # print 'continue calculation %s'% words else: ## reset, to be sure: self.calculation = [] self.hadNumber = self.hadOperator = False if self.exclusive: print('calculation exclusive, wait for number') self.waitForNumber('number') # starting with no lastresult: self.startwithLastResult = False def fillInstanceVariables(self): """fills the necessary instance variables records the unary or possibly unary operators """ self.unaryLeftOperators = self.ini.getList('general', 'unary left operators') self.unaryRightOperators = self.ini.getList('general', 'unary right operators') self.dualOperators = self.ini.getList( 'general', 'dual operators' ) ## may be incomplete, but for double functions, like minus def reset(self): pass def gotResults_calcnormal(self, words, fullResults): # step 3: specify the number you are waiting for # check if the word Minus is present, and then wait for the number named "number" # note in this example self.number is non existent until collectNumber has been visited. # self.minus will only be set if the rule number is visited. # print 'calcnormal or calccalc' if self.hasCommon(words, 'calculate'): if self.prog == 'calc': print('calculate, clear previous calculation') keystroke("{esc}") self.waitForNumber('number') self.minus = False self.inCalculation = True elif self.hasCommon(words, 'equals'): self.collectNumberAddToCalculation() self.calculation.append("=") self.doCalculation() return gotResults_calccalc = gotResults_calcnormal def gotResults_calccontinue(self, words, fullResults): # step 3: specify the number you are waiting for # check if the word Minus is present, and then wait for the number named "number" # note in this example self.number is non existent until collectNumber has been visited. # self.minus will only be set if the rule number is visited. if not self.inCalculation: raise Exception("_calculator, shoud be in a calculation right now") if self.hasCommon(words, 'equals'): self.collectNumberAddToCalculation() self.calculation.append("=") self.doCalculation() self.inCalculation = False def gotResults_number(self, words, fullResults): # this rule only catches with the word "minus" print('calculator: rule number, %s' % words) # self.waitForNumber('number') self.minus = not self.minus def gotResults_operator(self, words, fullResults): # step 3: specify the number you are waiting for # check if the word Minus is present, and then wait for the number named "number" # note in this example self.number is non existent until collectNumber has been visited. # self.minus will only be set if the rule number is visited. for w in words: self.collectNumberAddToCalculation() print('rule operator, hadOperator: %s, hadNumber %s' % (self.hadOperator, self.hadNumber)) operator = self.getFromInifile(w, 'operator') operatorStripped = operator.strip() unaryLeft = w in self.unaryLeftOperators unaryRight = w in self.unaryRightOperators possiblyDual = w in self.dualOperators if self.hadNumber: if unaryRight: pass elif possiblyDual or not unaryLeft: self.hadNumber = False # just set now else: print('no unaryLeft operator expected: %s' % w) continue else: if unaryLeft and not possiblyDual: pass elif w == words[0]: self.startwithLastResult = True if self.hadOperator: operator = operator.strip() self.calculation.append(operator) if not unaryRight: self.hadOperator = True def gotResults_cancel(self, words, fullResults): self.cancelMode() def gotResults(self, words, fullResults): # step 4, in gotResults collect the number (as a string): self.collectNumberAddToCalculation( ) # setting self.number, see self.waitForNumber above if self.calculation: if self.prog == 'calc': print('calculation sofar (calc): %s' % ''.join(self.calculation)) self.doCalculation() else: print( 'continue calculation or finish with "equals" (or "cancel")' ) print('calculation sofar: %s' % ''.join(self.calculation)) def collectNumberAddToCalculation(self): """collect the number, and add to self.calculation also wait again for new number... """ self.collectNumber() self.hadNumber = False if self.number: # if not self.hadOperator: # print 'no operator found, for new number: %s'% self.number self.hadNumber = True self.hadOperator = False if self.minus: self.number = '-' + self.number self.calculation.append(self.number) self.number = '' self.waitForNumber('number') def doCalculation(self): if not self.calculation: print('doCalculation, no calculation ready') return if not self.inCalculation and self.prog != 'calc': print('not in a calculation') return if self.startwithLastResult: self.calculation.insert(0, str(self.lastResult)) calculationpython = ''.join([c for c in self.calculation if c != "="]) # place for python specific tricks: if calculationpython.find(',') >= 0: calculationpython = calculationpython.replace(',', '.') result = eval(calculationpython) self.lastResult = result if self.prog in ['calc']: if self.startwithLastResult: del self.calculation[ 0] # take memory from calculator, not from this list... print( 'in calc, continuation of calculation, expect lastResult: %s' % self.lastResult) for c in self.calculation: keystroke(c.strip()) elif self.prog in ['natspeak']: # DragonPad print('%s = %s' % (calculationpython, result)) elif self.prog in ['excel']: calculationExcel = [ c.strip() for c in self.calculation if c != "=" ] calculationExcel = '=' + ''.join(calculationExcel) + "{tab}" keystroke(calculationExcel) else: print("calc: %s = %s" % (calculationpython, result)) keystroke('%s' % result) self.inCalculation = False self.calculation = [] def cancelMode(self): #print "end of oops exclusive mode", also called when microphone is toggled. ## print 'resetting oops grammar' if self.inCalculation: print('%s, cancel exclusive mode' % self.name) self.activeSet = None self.deactivateAll() self.inCalculation = False self.lastResult = None self.calculation = []