From 3387fd35b79aaf0999e816d4cf8f1963e61eaaf2 Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Wed, 31 Jan 2024 16:32:45 +0100 Subject: [PATCH 01/26] a new test, and a few fixes, so making a brand new inifile passes without errors... --- src/unimacro/natlinkutilsbj.py | 20 ++++++---- tests/test_new_inifile_directory.py | 61 +++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 tests/test_new_inifile_directory.py diff --git a/src/unimacro/natlinkutilsbj.py b/src/unimacro/natlinkutilsbj.py index 43b630e..9900f80 100644 --- a/src/unimacro/natlinkutilsbj.py +++ b/src/unimacro/natlinkutilsbj.py @@ -27,7 +27,7 @@ # See the class BrowsableGrammar for documentation on the use of # the Grammar browser. # revised many times by Quintijn Hoogenboom -#pylint:disable=C0302, C0116, W0702, W0201, W0703, R0915, R0913, W0613, R0912, R0914, R0902, C0209, W0602 +#pylint:disable=C0302, C0116, W0702, W0201, W0703, R0915, R0913, W0613, R0912, R0914, R0902, C0209, W0602, W0212 #pylint:disable=E1101 """subclasses classes for natlink grammar files and utility functions @@ -1024,9 +1024,9 @@ def __init__(self, inifile_stem=None): self.module_name = self.__module__.rsplit('.', maxsplit=1)[-1] self.inifile_stem = inifile_stem or self.module_name self.language = status.get_language() - try: - self.ini - except AttributeError: + + + if not hasattr(self, 'ini'): self.startInifile() self.name = self.checkName() try: @@ -2385,7 +2385,10 @@ def makeDefaultInifile(self, inifile=None, enxVersion=None): self.ini.set('an instruction', 'note 3', 'This section can be deleted after reading') self.fillDefaultInifile(self.ini) + self.ini.writeIfChanged() self.ini.close() + name = self.getName() + print(f'Please edit this new inifile: {self.ini._file}, possibly by calling "edit {name}') def fillDefaultInifile(self, ini): """set initial settings for ini file, overload! @@ -2396,9 +2399,12 @@ def fillDefaultInifile(self, ini): name = self.__module__.strip('_') name = name.replace("_", " ") ini.set('grammar name', 'name', name) - for l in self.validLists: - if not('iniIgnoreGrammarLists' in dir(self) and l in self.iniIgnoreGrammarLists): - ini.set(l) + + if hasattr(self, "validLists") and hasattr(self, 'iniIgnoreGrammarLists'): + for l in self.validLists: + if not l in self.iniIgnoreGrammarLists: + ini.set(l) + ini.write() def error(self, message): """gives an error message, and leaves variable Error diff --git a/tests/test_new_inifile_directory.py b/tests/test_new_inifile_directory.py new file mode 100644 index 0000000..cb5f3f2 --- /dev/null +++ b/tests/test_new_inifile_directory.py @@ -0,0 +1,61 @@ +"""this one tests if an new .ini file is automatically made, when no examples are available... + +Not very nice programmed, but for the moment it works (January 2024, Quintijn) + +""" +import os +from pathlib import Path +import pytest +from unimacro import natlinkutilsbj as natbj +from natlinkcore import natlinkstatus +status = natlinkstatus.NatlinkStatus() +#boilerplate to copy the grammar ini file into the correct location +#just chagne grammar_ini to the name of the grammar ini file you want copied into +#unimacro user directory +# from conftest import make_copy_grammar_ini_fixture +grammar_ini= "_newbie.ini" +#grammar_ini_fixture will depend on unimacro_setup, so you dont' need to specfiy unimacro_setup +#as a fixture in your tests. Though you can +# grammar_ini_fixture=make_copy_grammar_ini_fixture(grammar_ini) +#end of boilderplate to copy the gramar ini file into correct location + +# #leave these first two samples in as documentation in test_brackets. confests docs say to look in this file for examples. +# def test_sample_grammar_ini_fixture1(grammar_ini_fixture): +# assert True +# +# def test_sample_grammar_ini_fixture2(grammar_ini_fixture,unimacro_setup): +# print(f"Unimacro setup sample: {unimacro_setup} grammar_ini_fixutre {grammar_ini_fixture} (should be the same)") +# assert True + + +class Newbie(natbj.IniGrammar): + """simple grammar which is initially on + """ + gramSpec = """ exported = 'grammar on'; + """ + def initialize(self): + self.ini.set('general', 'initial on', "True") + self.switchOnOrOff() + def gotResults_gramon(self, words, fullResults): + print(f'got gramon: {words}') + + +def test_get_newbie_without_demo_inifile(unimacro_setup): + """see if we can get all the grammars + """ + # this works apparently in the normal unimacro_user_directory, first delete _newbie.ini + inifile_stem = "_newbie" + userDir = status.getUnimacroUserDirectory() + commandDir = os.path.join(userDir, status.language +"_inifiles") + inifile = Path(commandDir)/ f'{inifile_stem}.ini' + if inifile.is_file(): + inifile.unlink() + + newbie = Newbie(inifile_stem="_newbie") + newbie.initialize() + assert newbie.ini + + +if __name__ == "__main__": + pytest.main(['test_new_inifile_directory.py']) + \ No newline at end of file From 2549515e65567c441c8b0d48e6e85dbaf1649e49 Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Thu, 1 Feb 2024 14:33:23 +0100 Subject: [PATCH 02/26] checking getTopOrChild function... --- src/unimacro/UnimacroGrammars/_general.py | 111 +++------------------- tests/test_grammar_folders.py | 4 +- tests/test_grammar_general.py | 3 + 3 files changed, 21 insertions(+), 97 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_general.py b/src/unimacro/UnimacroGrammars/_general.py index 222bc4d..0186f8b 100644 --- a/src/unimacro/UnimacroGrammars/_general.py +++ b/src/unimacro/UnimacroGrammars/_general.py @@ -1,22 +1,18 @@ +# This was the old "general" intro for all unimacro grammar files: # This file is part of a SourceForge project called "unimacro" see # http://unimacro.SourceForge.net and http://qh.antenna.nl/unimacro # (c) copyright 2003 see http://qh.antenna.nl/unimacro/aboutunimacro.html # or the file COPYRIGHT.txt in the natlink\natlink directory # -# _general.PY +# _general.py # # written by Quintijn Hoogenboom (QH softwaretraining & advies), -# developed during the past few years. # # #pylint:disable=C0302, R0904, C0209, C0321, R0912, R0914, R0915, R0911 #pylint:disable=E1101 -"""do a set of general commands, with language versions possible, version 7 - -a lot of commands from the previous version removed, inserted a search and dictate -mode, that only works when spell mode or command mode is on. - +"""do a set of general commands """ import re @@ -97,7 +93,7 @@ "{Right}": "{Left}"} modes = ['spell', 'command', 'numbers', 'normal', 'dictation', 'dictate'] -normalSet = ['test', 'reload', 'info', 'undo', 'redo', 'namephrase', 'batch', +normalSet = ['test', 'reload', 'info', 'undo', 'redo', 'namephrase', 'comment', 'documentation', 'modes', 'variable', 'search', 'highlight', # for Shane, enable, because Vocola did not fix _anything yet 'browsewith', 'hyphenatephrase', 'pastepart', @@ -105,7 +101,7 @@ #normalSet = ['hyphenatephrase'] # skip 'openuser' commandSet = normalSet[:] + ['dictate'] - +thisGrammar = None ancestor=natbj.IniGrammar class ThisGrammar(ancestor): @@ -127,7 +123,6 @@ class ThisGrammar(ancestor): imported; # imported; exported = Make documentation; - exported = do batch words; exported = test micstate; exported = (press|address) (|); exported = choose {n1-10}; @@ -274,83 +269,6 @@ def gotResults_before(self,words,fullResults): if self.hasCommon(words, 'here'): natut.buttonClick('left', 1) - def gotResults_batch(self,words,fullResults): - - _files = [f[:-4] for f in os.listdir(wordsFolder)] - if _files: - print('in folder: %s, files: %s'% (wordsFolder, _files)) - else: - print('in folder: %s, no files found'% wordsFolder) - return - - for f in _files: - F = f + '.txt' - if f == 'deleted words': - print('delete words!') - for l in open(os.path.join(wordsFolder, F)): - w = l.strip() - if w.find('\\\\') > 0: - w, p = w.split('\\\\') - print(f, ', word to delete :', w) - unimacroutils.deleteWordIfNecessary(w) - continue - - if f in FORMATS: - formatting = FORMATS[f] - print('to formatting for file: %s: %x'% (f, formatting)) - else: - print('no formatting information for file: %s'% f) - formatting = 0 - - for l in open(os.path.join(wordsFolder, F)): - p = 0 # possibly user defined properties - w = l.strip() - print(f, ', word:', w) - if w.find('\\\\') > 0: - w, p = w.split('\\\\') - exec("p = %s"%p) -## pList = unimacroutils.ListOfProperties(p) -## for pp in pList: -## print pp - newFormat = p or formatting - unimacroutils.addWordIfNecessary(w) - formatOld = natlink.getWordInfo(w) - if formatOld == newFormat: - print('format already okay: %s (%x)'% (w, newFormat)) - else: - natlink.setWordInfo(w, newFormat) - print('format set for %s: %x'% (w, newFormat)) - -## def gotResults_datetime(self,words,fullResults): -## """print copy or playback date, time or date and time -## """ -## Print = self.hasCommon(words, 'print') -## Speak = self.hasCommon(words, 'give') -## Date = self.hasCommon(words, 'date') -## Time = self.hasCommon(words, 'time') -## if Date and Time: -## DateTime = 1 -## result = [] -## if Date: -## dateformat = "%m/%d/%Y" -## cdate = datetime.date.today() -## fdate = cdate.strftime(dateformat) -## result.append(fdate) -## if Time: -## timeformat = "%H:%M" -## ctime = datetime.datetime.now().time() -## ftime = ctime.strftime(timeformat) -## result.append(ftime) -## if result: -## result = ' '.join(result) -## else: -## print 'no date or time in result' -## return -## if Print: -## keystroke(result) -## elif Speak: -## natlink.execScript('TTSPlayString "%s"'% result) - def gotResults_highlight(self,words,fullResults): # for Shane self.highlight = 1 @@ -777,15 +695,16 @@ def gotResults_info(self,words,fullResults): T.append(f' .toporchild\t{p.toporchild}') T.append(f' .classname\t{p.classname}') T.append(f' .hndle:\t{hndle}') - childClass = "#32770" - overruleIsTop = self.getTopOrChild(self.progInfo, childClass=childClass) - - if p.toporchild != overruleIsTop: - T.append('') - if overruleIsTop: - T.append("**** treat as TOP window although it is a child window") - else: - T.append("**** treat as CHILD window although it is a top window") + # # for special behaviour: + # childClass = "#32770" + # overruleIsTop = self.getTopOrChild(self.progInfo, childClass=childClass) + # + # if p.toporchild != overruleIsTop: + # T.append('') + # if overruleIsTop: + # T.append("**** treat as TOP window although it is a child window") + # else: + # T.append(f'**** treat as CHILD window although it is a top window (classname: {classname})') elif self.hasCommon(words,'user'): diff --git a/tests/test_grammar_folders.py b/tests/test_grammar_folders.py index edc3324..8e46b02 100644 --- a/tests/test_grammar_folders.py +++ b/tests/test_grammar_folders.py @@ -12,6 +12,8 @@ def test_fill_folders_list(unimacro_setup): thisGrammar = ThisGrammar() thisGrammar.startInifile() #modName = '_folders') thisGrammar.initialize() + # minor assertion, mainly for interactive use + assert thisGrammar.foldersDict def test_folder_remember(unimacro_setup): """go through folder remember functions @@ -26,7 +28,7 @@ def test_folder_remember(unimacro_setup): thisGrammar.wantedFolder = r'C:\Windows' thisGrammar.gotResults_remember(words, FR) - + # no assertion, for interactive use if __name__ == "__main__": diff --git a/tests/test_grammar_general.py b/tests/test_grammar_general.py index 3297008..70cac8b 100644 --- a/tests/test_grammar_general.py +++ b/tests/test_grammar_general.py @@ -20,9 +20,12 @@ def test_getTopOrChild(unimacro_setup,monkeypatch): thisGrammar.startInifile() #modName = '_general') thisGrammar.initialize() hndle = 10 + ##(progpath, prog, title, toporchild, classname, hndle) ## ProgInfo(progpath, prog, title, toporchild, classname, HNDLE) + progInfo = unimacroutils.ProgInfo('path/to/program.exe', 'program', 'window title', 'top', 'classname', hndle) + # thisGrammar.gotBegin(modInfo) thisGrammar.progInfo = progInfo assert thisGrammar.getTopOrChild(progInfo=progInfo, childClass=None) is True From 11b84990af10715da54dd9806a1bd7f1d35c845e Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Wed, 7 Feb 2024 12:16:59 +0100 Subject: [PATCH 03/26] fixing, at lasts, switch on a unimacro grammar, doing a reload (by the loader), with the force option. --- src/unimacro/_control.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/unimacro/_control.py b/src/unimacro/_control.py index d2a339b..feaa2ac 100644 --- a/src/unimacro/_control.py +++ b/src/unimacro/_control.py @@ -275,6 +275,7 @@ def gotResults_switch(self,words,fullResults): for gname, gram in G.items(): if gram == self: continue + gram.checkForChanges = 1 self.switch(gram, gname, switchOn) else: gname = self.hasCommon(words, Gkeys) @@ -290,19 +291,28 @@ def switch(self, gram, gname, switchOn): """switch on or off grammar, and set in inifile, gram is the grammar object gname is the grammar name - switchOn is True or Fals + switchOn is True or False """ if gram == self: print(f'should not be here, do not switch on of off _control {gram}') return None if switchOn: - self.checkInifile() + gram.checkInifile() gram.ini.set('general', 'initial on', 1) gram.ini.write() unimacroutils.Wait(0.1) + gramName = gram.getName() + unimacro_grammars_paths = self.getUnimacroGrammarNamesPaths() + try: + filepath = Path(unimacro_grammars_paths[gramName]) + except KeyError: + print(f'_control, grammar not in unimacro_grammars_paths dict: {gramName}, cannot switchOn') + return None + # now reload with force option. + print(f'_control, now reload grammar "{gramName}":') + natlinkmain.seen.clear() + natlinkmain.load_or_reload_module(filepath, force_load=True) - # gram.unload() - gram.initialize() return 1 # switch off: From 49ed8d0d074d124721c9bb91207a398c7bf1543c Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Wed, 7 Feb 2024 12:17:47 +0100 Subject: [PATCH 04/26] several details in boilerplate code. added rememberDialog to .gitignore --- .gitignore | 1 + src/unimacro/OtherUnimacroGrammars/_latex.py | 4 +- src/unimacro/UnimacroGrammars/_brackets.py | 11 +- src/unimacro/UnimacroGrammars/_folders.py | 265 +------------------ src/unimacro/UnimacroGrammars/_lines.py | 4 +- 5 files changed, 23 insertions(+), 262 deletions(-) diff --git a/.gitignore b/.gitignore index 89cdb74..5302b09 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ __pycache__/ /src/unimacro/grammar.bin README.html /src/unimacro/UnimacroGrammars/rememberdialog.py +/tests/rememberdialog.py diff --git a/src/unimacro/OtherUnimacroGrammars/_latex.py b/src/unimacro/OtherUnimacroGrammars/_latex.py index 08fbf82..c231d19 100644 --- a/src/unimacro/OtherUnimacroGrammars/_latex.py +++ b/src/unimacro/OtherUnimacroGrammars/_latex.py @@ -263,8 +263,8 @@ def unload(): # here code to interactive run this module natlink.natConnect() try: - thisGrammar = ThisGrammar() - thisGrammar.startInifile(modName = '_latex') + thisGrammar = ThisGrammar(inifile_stem='_latex') + thisGrammar.startInifile() thisGrammar.initialize() Words = ['add', 'option', 'draft'] FR = {} diff --git a/src/unimacro/UnimacroGrammars/_brackets.py b/src/unimacro/UnimacroGrammars/_brackets.py index 65d86c1..b9e4f86 100644 --- a/src/unimacro/UnimacroGrammars/_brackets.py +++ b/src/unimacro/UnimacroGrammars/_brackets.py @@ -113,8 +113,11 @@ def gotResults(self, words, fullResults): text = "" if self.here: + print('do a left buttonClick') unimacroutils.buttonClick('left', 1) + print('do a "visibleWait') unimacroutils.visibleWait() + print('after a visibleWait') leftText = rightText = leftTextDict = rightTextDict = "" if text: @@ -199,9 +202,9 @@ def stripFromBothSides(text): text = text.rstrip() return text, leftText, rightText -# standard stuff Joel (adapted for possible empty gramSpec, QH, unimacro) +# standard stuff Joel (adapted in course of time, QH) def unload(): - #pylint:disable=W0603 + #pylint:disable=W0603, E0601 global bracketsGrammar if bracketsGrammar: bracketsGrammar.unload() @@ -211,8 +214,8 @@ def unload(): if __name__ == "__main__": natlink.natConnect() try: - bracketsGrammar = BracketsGrammar() - bracketsGrammar.startInifile(modName = '_brackets') + bracketsGrammar = BracketsGrammar(inifile_stem='_brackets') + bracketsGrammar.startInifile() bracketsGrammar.initialize() finally: natlink.natDisconnect() diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index 1c90ef4..c07ff9e 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -7,7 +7,7 @@ # Written by: Quintijn Hoogenboom (QH softwaretraining & advies) # starting 2003, revised QH march 2011 # moved to the GitHub/Dictation-toolbox April 2020 -#pylint:disable=C0302, W0613, W0702, R0911, R0912, R0913, R0914, R0915 +#pylint:disable=C0302, W0613, W0702, R0911, R0912, R0913, R0914, R0915, W0212 #pylint:disable=E1101, C0209 r"""with this grammar, you can reach folders, files and websites from any window. From some windows (my computer and most dialog windows) the folders and files @@ -47,7 +47,6 @@ """ import re -import copy import pickle #recentfoldersDict import os import sys @@ -57,7 +56,6 @@ import urllib.parse import urllib.error import ctypes # get window text -import traceback from pathlib import Path # from pprint import pprint import win32gui @@ -72,7 +70,7 @@ from dtactions import natlinkclipboard from dtactions.unimacro.unimacroactions import doAction as action from dtactions.unimacro.unimacroactions import doKeystroke as keystroke -from dtactions.unimacro.unimacroactions import do_YESNO as YesNo +# from dtactions.unimacro.unimacroactions import do_YESNO as YesNo from dtactions.unimacro.unimacroactions import UnimacroBringUp from dtactions.unimacro import unimacroutils # from dtactions.unimacro.unimacroactions import Message @@ -126,8 +124,6 @@ class ThisGrammar(ancestor): optionalfoldercommands = ['new', 'explorer', 'paste', 'copy', 'remote', 'git'] optionalfilecommands = ['copy', 'paste', 'edit', 'paste', 'remote', 'openwith', 'git'] - # only used if siteRoot is a valid folder - optionalsitecommands = ['input', 'output', 'local', 'online'] gramSpec = """ exported = folder ({folders}[]); @@ -152,8 +148,6 @@ class ThisGrammar(ancestor): ; = remember; = (copy (name|path)) | ((name|path) copy); -## set all environment variables into the folders list... - exported = set environment folders; """ if doRecentFolderCommand: @@ -175,7 +169,6 @@ def initialize(self): print("no valid language in grammar "+__name__+" grammar not initialized") return self.load(self.gramSpec) - self.lastSite = None self.switchOnOrOff() # initialises lists from inifile, and switches on def gotBegin(self,moduleInfo): @@ -367,7 +360,8 @@ def fillInstanceVariables(self): foldersList = self.ini.get('folders') self.foldersDict = {} for f in foldersList: - folder = self.substituteFolder(self.ini.get('folders', f)) + raw_folder = self.ini.get('folders', f) + folder = self.substituteFolder(raw_folder) if not os.path.isdir(folder): print(f'warning _folders, folder "{f}" does not exist: "{folder}"') # self.ini.delete('folders', f) @@ -854,94 +848,6 @@ def displayRecentFolders(self): # wantedFolder = self.recentfoldersList[chooseNum] # self.gotoFolder(wantedFolder) - def gotResults_siteshort(self,words,fullResults): - """switch to last mentioned site in the list - mainly for private use, a lot of folders reside in the root folder, - siteRoot. They all have an input folder and a output folder. - - - """ - if self.lastSite: - words.insert(1, self.lastSite) - print('lastSite: %s'% words) - self.gotResults_site(words, fullResults) - else: - self.DisplayMessage('no "lastSite" available yet') - - - def gotResults_setenvironmentfolders(self,words,fullResults): - """switch to last mentioned site in the list - mainly for private use, a lot of folders reside in the root folder, - siteRoot. They all have an input folder and a output folder. - - """ - reverseOldValues = {'ignore': []} - for k in self.ini.get('folders'): - val = self.ini.get('folders', k) - if val: - reverseOldValues.setdefault(val, []).append(k) - else: - reverseOldValues['ignore'].append(k) - reverseVirtualDrives = {} - for k in self.ini.get('virtualdrives'): - val = self.ini.get('virtualdrives', k) - reverseVirtualDrives.setdefault(val, []).append(k) - -## print reverseOldValues - allFolders = copy.copy(self.envDict) # natlinkcorefunctions.getAllFolderEnvironmentVariables() - kandidates = {} - ignore = reverseOldValues['ignore'] - for (k,v) in list(allFolders.items()): - kSpeakable = k.replace("_", " ") - if k in ignore or kSpeakable in ignore: - continue - oldV = self.ini.get('folders', k, "") or self.ini.get('folders', kSpeakable) - if oldV: - vPercented = "%" + k + "%" - if oldV == v: - continue - if oldV == vPercented: - kPrevious = reverseOldValues[vPercented] -## print 'vPercented: %s, kPrevious: %s'% (vPercented, kPrevious) - if vPercented in reverseOldValues: - if k in kPrevious or kSpeakable in kPrevious: - continue - print('already in there: %s (%s), but spoken form changed to %s'% \ - (k, v, kPrevious)) - continue - else: - print('different for %s: old: %s, new: %s'% (k, oldV, v)) - kandidates[k] = v - count = len(kandidates) - - if not kandidates: - self.DisplayMessage("no new environment variables to put into the folders section") - return - mes = ["%s new environment variables for your folders section of the grammar _folders"% count] - - Keys = list(kandidates.keys()) - Keys.sort() - for k in Keys: - mes.append("%s\t\t%s"% (k, kandidates[k])) - - mes.append('\n\nDo you want these new environment variables in your folders section?') - - - - if YesNo('\n'.join(mes)): - for (k,v) in list(kandidates.items()): - if k.find('_') > 0: - kSpeakable = k.replace("_", " ") - if self.ini.get('folders', k): - self.ini.delete('folders', k) - else: - kSpeakable = k - self.ini.set('folders', kSpeakable, "%" + k + "%") - self.ini.write() - self.DisplayMessage('added %s entries, say "Show|Edit folders" to browse'% count) - else: - self.DisplayMessage('nothing added, command canceled') - def gotResults_website(self,words,fullResults): """start webbrowser, websites in inifile unders [websites] @@ -1143,127 +1049,6 @@ def gotResults_recentfolder(self,words,fullResults): folder = self.recentfoldersDict[name] print("recentfolder, name: %s, folder: %s"% (name, folder)) self.wantedFolder = folder - - def gotResults_site(self,words,fullResults): - """switch to one of the sites in the list - mainly for private use, a lot of folders reside in the root folder, - siteRoot. They all have an input folder and a output folder. - - """ - print('site: %s'% words) - siteSpoken = words[1] - self.lastSite = None # name of site - if siteSpoken in self.sitesDict: - siteName = self.sitesDict[siteSpoken] - self.lastSite = siteName - else: - raise ValueError("no siteName for %s"% siteSpoken) - - self.site = self.getSiteInstance(siteName) - - if siteName in self.sitesInstances: - self.site = self.sitesInstances[siteName] - else: - site = self.getSiteInstance(siteName) - if site: - self.sitesInstances[siteName] = site - self.lastSite = siteName - self.site = site - else: - self.site = None - print('could not get site: %s'% siteName) - # - #if site is None: - # print 'invalid site: %s, marking in ini file'% site - # self.ini.set('sites', siteName, '') - # self.ini.write() - # return - if self.nextRule == 'sitecommands': - print('site %s, waiting for sitecommands'% self.site) - else: - if self.site: - self.wantedFolder = self.site.rootDir - else: - print("_folders, site command (private QH), no site specified") - - def gotResults_sitecommands(self, words, fullResults): - """do the various options for sites (QH special). - Assume lastSite is set - """ - if not self.site: - print("sitecommands, no last or current site set") - return - print('sitecommands for "%s": %s (site: %s)'% (self.lastSite, words, self.site)) - site = self.site - website, folder = None, None - for command in words: - command = self.getFromInifile(words[0], 'sitecommands') - - if command == 'input': - print('input: %s'% words) - folder = str(site.sAbs) - elif command == 'output': - folder = str(site.hAbs) - elif command == 'local': - website = os.path.join(str(site.hAbs), 'index.html') - elif command == 'online': - sitePrefix = site.sitePrefix - if isinstance(sitePrefix, dict): - for v in sitePrefix.values(): - sitePrefix = v - break - - website = os.path.join(str(sitePrefix), 'index.html') - elif command == 'testsite': - if 'sg' in self.sitesInstances: - testsite = self.sitesInstances['sg'] - else: - testsite = self.getSiteInstance('sg') - if testsite: - self.sitesInstances['sg'] = testsite - - if testsite: - # site at sitegen site: - website = os.path.join(str(testsite.sitePrefix['nl']), self.lastSite, 'index.html') - - if self.nextRule: - if folder: - self.wantedFolder = folder - return - if website: - self.wantedWebsite = website - return - print('no valid folder or website for nextRule') - return - if folder: - self.gotoFolder(folder) - self.wantedFolder = None - elif website: - self.gotoWebsite(website) - self.wantedWebsite = None - - - def getSiteInstance(self, siteName): - """return pageopen function of site instance, or None - """ - try: - site = __import__(siteName) - except ImportError: - print('cannot import module %s'% siteName) - print(traceback.print_exc()) - currentDir = '.' in sys.path - print('currentDir in sys.path: %s'% currentDir) - print('sys.path: %s'% sys.path) - return None - if 'pagesopen' in dir(site): - try: - po = site.pagesopen() - return po - except: - print('"pagesopen" failed for site %s'% siteName) - return None - print('no function "pagesopen" in module: %s'% siteName) - return None def findFolderWithIndex(self, root, allowed, ignore=None): """get the first folder with a file index.html""" @@ -1784,9 +1569,9 @@ def substituteFolder(self, folder): """ folder = folder.replace('/', '\\') folder = self.substituteEnvVariable(folder) - if not self.virtualDriveDict: - #print 'no virtualDriveDict, return %s'% folder - return folder + # if not self.virtualDriveDict: + # #print 'no virtualDriveDict, return %s'% folder + # return folder if folder in self.virtualDriveDict: drive, rest = folder, "" elif folder.find(':\\') > 0: @@ -2329,38 +2114,6 @@ def getValidFile(self, f, remote): fparts.pop(0) print('_folders, no valid remote file found for %s and remote: %s'% (f, remote)) - - def getListOfSites(self, root): - """return list of sitenames, to be found as python files in root - - """ - pyfiles = [f for f in os.listdir(root) if f.endswith('.py')] - #print 'pyfiles for sites: %s'% pyfiles - D = {} - entries = self.ini.get('sites') - for p in pyfiles: - trunk = p[:-3] - if not reOnlyLowerCase.match(trunk): - continue # only lowercase items can be a sites item, so __init__ and HTMLgen etc are skipped - if trunk in entries: - spokenList = self.ini.getList('sites', trunk) - if not spokenList: - #print 'empty item in siteslist: %s'% trunk - continue - for spoken in spokenList: - spoken = self.spokenforms.correctLettersForDragonVersion(spoken) - D[spoken] = trunk - else: - # make new entry in sites section - if len(trunk) <= 3: - spoken = '. '.join(list(trunk.upper()))+'.' - else: - spoken = trunk - spoken = self.spokenforms.correctLettersForDragonVersion(spoken) - D[spoken] = trunk - #print 'set in sites: %s -> %s'% (trunk, spoken) - self.ini.set('sites', trunk, spoken) - return D def gotResults(self, words,fullResults): """at last do most of the actions, depending on the variables collected in the rules. @@ -2730,6 +2483,10 @@ def unload(): thisGrammar = ThisGrammar(inifile_stem="_folders") thisGrammar.startInifile() thisGrammar.initialize() + Words = ['folder', 'dtactions'] + Fr = {} + thisGrammar.gotResultsInit(Words, Fr) + thisGrammar.gotResults_folder(Words, Fr) finally: natlink.natDisconnect() elif __name__.find('.') == -1: diff --git a/src/unimacro/UnimacroGrammars/_lines.py b/src/unimacro/UnimacroGrammars/_lines.py index 839727d..8cc066b 100644 --- a/src/unimacro/UnimacroGrammars/_lines.py +++ b/src/unimacro/UnimacroGrammars/_lines.py @@ -780,8 +780,8 @@ def unload(): # here code to interactive run this module natlink.natConnect() try: - thisGrammar = ThisGrammar() - thisGrammar.startInifile(modName = '_lines') + thisGrammar = ThisGrammar(inifile_stem='_lines') + thisGrammar.startInifile() thisGrammar.initialize() thisGrammar.progInfo = unimacroutils.getProgInfo() seqsAndRules = [(['line'], 'linenum'), (['seven', 'two', 'three'], '__0to9')] From 32ddae122d831d2cfda547e143e93d21cadd55e5 Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Thu, 8 Feb 2024 12:06:03 +0100 Subject: [PATCH 05/26] _general.py fixed several dgndictation things (also in nsformat.py of natlinkcore) --- src/unimacro/UnimacroGrammars/_general.py | 49 +++++++---------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_general.py b/src/unimacro/UnimacroGrammars/_general.py index 0186f8b..ffbf0ad 100644 --- a/src/unimacro/UnimacroGrammars/_general.py +++ b/src/unimacro/UnimacroGrammars/_general.py @@ -97,7 +97,7 @@ 'comment', 'documentation', 'modes', 'variable', 'search', 'highlight', # for Shane, enable, because Vocola did not fix _anything yet 'browsewith', 'hyphenatephrase', 'pastepart', - 'password', 'presscode', 'choose'] + 'password', 'choose'] #normalSet = ['hyphenatephrase'] # skip 'openuser' commandSet = normalSet[:] + ['dictate'] @@ -124,7 +124,6 @@ class ThisGrammar(ancestor): # imported; exported = Make documentation; exported = test micstate; - exported = (press|address) (|); exported = choose {n1-10}; exported = reload Natlink; exported = give (user | prog |window |unimacro| path) (info|information) ; @@ -145,8 +144,12 @@ class ThisGrammar(ancestor): exported = ('browse with') {browsers}; exported = 'open user' {users}; exported = 'password' ; + exported = (press letter) ; """] +# A\determinerNormalTestThisNowI\pronounTest.\period\full stop +#NormalTestNormalI\pronounTestNormallyTestNormalAttestNormal.\period\periodSigns +#HelloTesting def initialize(self): if self.language: @@ -160,20 +163,9 @@ def initialize(self): # print('specialSearchWords: %s'% self.specialSearchWords) self.setNumbersList('count', Counts) self.setList('modes', modes) -## self.testlist = ['11\\Canon fiftyfive two fifty', -## '12\\Canon', -## '15\\C. Z. J.', -## '19\\Helios fourtyfour M.', -## '38\\Vivitar seventy one fifty', -## '32\\Contax twentyeight millimeter', -## '33\\C. Z. J. one thirtyfive number one', -## 'Canon 2870', -## '09\\Canon 1022', -## '31\\Tamron 2875'] -## #self.setList('testlist', self.testlist) -## self.emptyList('testlist') self.gotPassword = 0 - # self.gotPresscode = 0 + self.passwordEnding = None + self.gotPresscode = 0 # print "%s, activateSet: %s"% (self.name, normalSet) # self.deactivateAll() # why is this necessary? The activateAll in switchOn is definitly now Ok... self.title = 'Unimacro grammar "'+__name__+'" (language: '+self.language+')' @@ -216,8 +208,11 @@ def gotResults_password(self,words,fullResults): if number precedes @ """ - self.gotPassword = 1 - print('gotPassword: ', self.gotPassword) + if not self.gotPassword: + self.gotPassword = 1 + print('gotPassword: ', self.gotPassword) + else: + self.passwordEnding = words[-1] def gotResults_pastepart(self,words,fullResults): """paste part of clipboard, parts are separated by ";" @@ -372,7 +367,6 @@ def gotResults_dgndictation(self,words,fullResults): text = nsformat.formatPassword(words) keystroke(text) self.gotPassword = 0 - print('reset gotPassword, ', self.gotPassword) return if self.gotVariable: print('do variable trick %s on %s'% (self.gotVariable, words)) @@ -592,21 +586,6 @@ def gotResults_stopwatch(self,words,fullResults): action(f'MSG {elapsed:.2f} seconds') self.startTime = t - -# sstarting message - def gotResults_presscode(self,words,fullResults): - """pressing letters or dictation in for example explorer - - """ - self.gotPresscode = 1 - print(f'got presscode: {words}, presscode: {self.gotPresscode}') - if self.hasCommon(words, 'address'): - action('{alt+d}; VW') - # search maybe useful for safari, but does not make sense otherwise - # if self.hasCommon(words, 'search'): - # action('{ctrl+k}; VW') - - def gotResults_choose(self,words,fullResults): """choose alternative, via actions @@ -1128,7 +1107,9 @@ def unload(): # if __name__ == "__main__": # here code to interactive run this module - pass + thisGrammar = ThisGrammar(inifile_stem='_general') + thisGrammar.startInifile() + thisGrammar.initialize() elif __name__.find('.') == -1: # this is caught when this module is imported by the loader (when Dragon/Natlink starts) thisGrammar = ThisGrammar() From 2a3a9924dfe97f738e1bd80aa6555740f505a9b6 Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Thu, 8 Feb 2024 15:51:01 +0100 Subject: [PATCH 06/26] fixed a few flaws in _brackets, started test_grammar_brackets.py --- src/unimacro/UnimacroGrammars/_brackets.py | 8 +-- tests/test_grammar_brackets.py | 63 ++++++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 tests/test_grammar_brackets.py diff --git a/src/unimacro/UnimacroGrammars/_brackets.py b/src/unimacro/UnimacroGrammars/_brackets.py index b9e4f86..83adde0 100644 --- a/src/unimacro/UnimacroGrammars/_brackets.py +++ b/src/unimacro/UnimacroGrammars/_brackets.py @@ -81,12 +81,12 @@ def rule_brackets(self, words): newpleft = pList[0] newpright = pList[1] else: - lenph = int(len(p)/2) + lenph = 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)) + # print(f'result rule_brackets: |{self.pleft}|, pright: |{self.pright}|') def subrule_before(self, words): "(here|between|empty)+" @@ -137,13 +137,13 @@ def gotResults(self, words, fullResults): if lSpacing: keystroke(lSpacing) - action(self.pleft) + keystroke(self.pleft) unimacroutils.visibleWait() if text: #print 'text: |%s|'% repr(text) keystroke(text) unimacroutils.visibleWait() - action(self.pright) + keystroke(self.pright) unimacroutils.visibleWait() if rSpacing: diff --git a/tests/test_grammar_brackets.py b/tests/test_grammar_brackets.py new file mode 100644 index 0000000..26a27b1 --- /dev/null +++ b/tests/test_grammar_brackets.py @@ -0,0 +1,63 @@ +#pylint:disable=E1101 +from pathlib import Path +import pytest +import natlink +from unimacro.UnimacroGrammars import _brackets +from natlinkcore import natlinkstatus +status = natlinkstatus.NatlinkStatus + +thisDir = Path(__file__).parent + + +def test_testpleftpright(unimacro_setup): + """check the split of a brackets string in left and right + + "" should return " and " + asymmetric strings like f'|' (with a | as split) should return f' and ' + + """ + gram = _brackets.BracketsGrammar() + gram.startInifile() #modName = '_tasks') + gram.initialize() + + # test one + words = ['brackets', 'quotes'] + fr = {} + gram.gotResultsInit(words, fr) + # print(f'natbj.loadedGrammars: {natbj.loadedGrammars}') + gram.rule_brackets(words) + assert gram.pleft == '("' + assert gram.pright == '")' + + # test two: + words = ['quotes'] + gram.gotResultsInit(words, fr) + # print(f'natbj.loadedGrammars: {natbj.loadedGrammars}') + gram.rule_brackets(words) + assert gram.pleft == '"' + assert gram.pright == '"' + +def test_testactivebracketrules(unimacro_setup): + """do all the active words in the bracket rules accordin to the inifile + + """ + gram = _brackets.BracketsGrammar() + gram.startInifile() #modName = '_tasks') + gram.initialize() + + # test one + all_words = gram.ini.get('brackets') + for i, word in enumerate(all_words): + n = i + 1 + words = [word] + result = gram.ini.get('brackets', word) + print(f'test {word}, expect {result}') + fr = {} + gram.gotResultsInit(words, fr) + # print(f'natbj.loadedGrammars: {natbj.loadedGrammars}') + gram.rule_brackets(words) + assert n == len(all_words) + + +if __name__ == "__main__": + pytest.main(['test_grammar_brackets.py']) From 99b33ce827f091d1a1716b6a76edf0d96340b075 Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Thu, 8 Feb 2024 15:51:41 +0100 Subject: [PATCH 07/26] brought win32clipboard under try finally --- src/unimacro/UnimacroGrammars/_folders.py | 43 ++++++++++++----------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index c07ff9e..9716a73 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -2358,27 +2358,28 @@ def get_clipboard_files(folders=False): Enumerate clipboard content and return files either directly copied or highlighted path copied ''' - files = None - win32clipboard.OpenClipboard() - f = get_clipboard_formats() - if win32clipboard.CF_HDROP in f: - files = win32clipboard.GetClipboardData(win32clipboard.CF_HDROP) - else: - # print 'get_clipboard_files, not expected clipboard format CF_HDROP, but %s'% f - if win32clipboard.CF_UNICODETEXT in f: - files = [win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)] - elif win32clipboard.CF_TEXT in f: - files = [win32clipboard.GetClipboardData(win32clipboard.CF_TEXT)] - elif win32clipboard.CF_OEMTEXT in f: - files = [win32clipboard.GetClipboardData(win32clipboard.CF_OEMTEXT)] - if not files: - # print "get_clipboard_files, no files found from clipboard" - return None - if folders: - files = [f for f in files if os.path.isdir(f)] if files else None - else: - files = [f for f in files if os.path.isfile(f)] if files else None - win32clipboard.CloseClipboard() + try: + win32clipboard.OpenClipboard() + f = get_clipboard_formats() + if win32clipboard.CF_HDROP in f: + files = win32clipboard.GetClipboardData(win32clipboard.CF_HDROP) + else: + # print 'get_clipboard_files, not expected clipboard format CF_HDROP, but %s'% f + if win32clipboard.CF_UNICODETEXT in f: + files = [win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)] + elif win32clipboard.CF_TEXT in f: + files = [win32clipboard.GetClipboardData(win32clipboard.CF_TEXT)] + elif win32clipboard.CF_OEMTEXT in f: + files = [win32clipboard.GetClipboardData(win32clipboard.CF_OEMTEXT)] + if not files: + # print "get_clipboard_files, no files found from clipboard" + return None + if folders: + files = [f for f in files if os.path.isdir(f)] if files else None + else: + files = [f for f in files if os.path.isfile(f)] if files else None + finally: + win32clipboard.CloseClipboard() return files def makeFromTemplateAndExecute(unimacrofolder, templatefile, unimacrogrammarsfolder, exefile, prompt, text, default, inifile, section, value, pausetime=0): From 353ccb8fcb8bc5c7a2468281a09c306c28b9add5 Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Thu, 8 Feb 2024 15:52:05 +0100 Subject: [PATCH 08/26] tidy up _general --- src/unimacro/UnimacroGrammars/_general.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_general.py b/src/unimacro/UnimacroGrammars/_general.py index ffbf0ad..15c6a34 100644 --- a/src/unimacro/UnimacroGrammars/_general.py +++ b/src/unimacro/UnimacroGrammars/_general.py @@ -200,19 +200,13 @@ def gotResultsInit(self,words,fullResults): natut.buttonClick() unimacroutils.Wait() self.progInfo = unimacroutils.getProgInfo() - print(f'progInfo _general: {self.progInfo.prog}, {self.progInfo.title}') - def gotResults_password(self,words,fullResults): """interpret password as dictate Cap dictation words if number precedes @ """ - if not self.gotPassword: - self.gotPassword = 1 - print('gotPassword: ', self.gotPassword) - else: - self.passwordEnding = words[-1] + self.gotPassword = 1 def gotResults_pastepart(self,words,fullResults): """paste part of clipboard, parts are separated by ";" From b33efe370f04fa97bfbbcf56df056a239e1ab183 Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Thu, 8 Feb 2024 17:35:25 +0100 Subject: [PATCH 09/26] few small things _clickbyvoice.py, mostly tyding up things --- src/unimacro/UnimacroGrammars/_clickbyvoice.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_clickbyvoice.py b/src/unimacro/UnimacroGrammars/_clickbyvoice.py index 3f1271c..2d677a1 100644 --- a/src/unimacro/UnimacroGrammars/_clickbyvoice.py +++ b/src/unimacro/UnimacroGrammars/_clickbyvoice.py @@ -18,6 +18,8 @@ # # the lists {pagecommands} and {tabcommands} in the inifile (edit chrome hah) # +#pylint:disable=C0209 + """ This commands grammar allows clicking by voice @@ -53,7 +55,7 @@ class ThisGrammar(ancestor): gramSpec = """ exported = ((show) (numbers) [{additionalonoroff}]+) | ((numbers) {additionalonoroff}+) ; - exported = (hide) (numbers) [after {n1-20}]; + exported = (hide) (numbers); exported = (pick) [{navigateoptions}]; # is already in _tasks grammar: @@ -67,6 +69,7 @@ class ThisGrammar(ancestor): (page (back|forward) [{n1-20}]) | page {pagecommands} | (next|previous) page {pagecommands}; + #and the numbers grammar (0,...,999 or chain of digits): """+numberGram @@ -162,10 +165,6 @@ def gotResults_hidenumbers(self, words, fullResults): """hide the numbers """ - wordFound, _position = self.hasCommon(words, "after", withIndex=True) - if wordFound: - print("setting timer later") - self.getInputcontrol() self.doOption(self.hideNumbers) self.finishInputControl() From 2ac87ed6b28c81c406c44b95f24c40da3613b9b5 Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Mon, 12 Feb 2024 12:01:27 +0100 Subject: [PATCH 10/26] work in progress _folders, getting natlinktimer again at work... --- src/unimacro/UnimacroGrammars/_folders.py | 198 +++++----------------- src/unimacro/natlinkutilsbj.py | 2 +- 2 files changed, 45 insertions(+), 155 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index 9716a73..9d5bb59 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -31,20 +31,13 @@ In the inifile also the commands for start this computer or start windows explorer must be given. Correct these commands ("Edit Folders"/"Bewerk folders") if they do not work correct. -New feature: if you want to use xxexplorer (can be used hands-free very -easy, look in https://www.netez.com/xxExplorer), in section [general] -you can put a variable -xxexplorer = path to exe or false ('') This explorer is then taken if you are in or if Explorer is explicitly asked for. The strategy for "New" and "Explorer" (when you say "new", "nieuw", "explorer" in the folder command, are complicated, look below -The git additional commands are only valid if you specify a valid git client in the ini file general section -(git executable) (To be implemented) (##TODO) - """ import re import pickle #recentfoldersDict @@ -121,8 +114,8 @@ class ThisGrammar(ancestor): # commands with special status, must correspond to a right hand side # of a ini file entry (section foldercommands or filecommands) # remote, openwith have hardcoded details. - optionalfoldercommands = ['new', 'explorer', 'paste', 'copy', 'remote', 'git'] - optionalfilecommands = ['copy', 'paste', 'edit', 'paste', 'remote', 'openwith', 'git'] + optionalfoldercommands = ['new', 'explorer', 'paste', 'copy', 'remote'] + optionalfilecommands = ['copy', 'paste', 'edit', 'paste', 'remote', 'openwith'] gramSpec = """ @@ -131,22 +124,19 @@ class ThisGrammar(ancestor): exported = drive {letters} []; exported = ((this|here) folder) (|); = new | here | paste | on ({letters}|{virtualdrivesspoken}) | - (git) {gitfoldercommands}| | {foldercommands}; exported = folder up|folder up {n1-10}; - exported = file ({files}|{subfiles})[|]; # add dot {extensions} later again + exported = file ({files}|{subfiles})[|]; exported = ((here|this) file) (|); = {filecommands}| on ({letters}|{virtualdrivesspoken}) | - ('open with') {fileopenprograms}| - (git) {gitfilecommands}| - ; + ('open with') {fileopenprograms}|; exported = website {websites} []; exported = (this website) (|); = ('open with') {websiteopenprograms}| ; - = remember; + = remember; = (copy (name|path)) | ((name|path) copy); """ @@ -186,11 +176,10 @@ def gotBegin(self,moduleInfo): hndle = self.progInfo.hndle classname = self.progInfo.classname - activeFolder = self.getActiveFolder(hndle, classname) + # activeFolder = self.getActiveFolder(hndle, classname) if self.trackAutoFiles or self.trackAutoFolders: activeFolder = self.getActiveFolder(hndle, classname) - if activeFolder: - self.handleTrackFilesAndFolders(activeFolder) + self.handleTrackFilesAndFolders(activeFolder) if hndle and self.trackFoldersHistory: self.catchTimerRecentFolders(hndle) @@ -206,7 +195,7 @@ def gotResultsInit(self,words,fullResults): # folder options: # CopyName and PasteName refers to the folder, file or website name # Cut, Copy Paste of file or folder is not implemented - self.New = self.Here = self.Remote = self.Git = self.Cut = self.Copy = self.Paste = self.CopyNamePath = self.PastePath = False + self.New = self.Here = self.Remote = self.Cut = self.Copy = self.Paste = self.CopyNamePath = self.PastePath = False # file options: # OpenWith goes via Open. self.Open = self.Edit = None @@ -230,7 +219,8 @@ def handleTrackFilesAndFolders(self, activeFolder): if activeFolder and os.path.isdir(activeFolder): self.fillListsForActiveFolder(activeFolder) - print('set %s (sub)files and %s subfolders'% (len(self.subfilesDict), len(self.subfoldersDict))) + nFiles, nFolders = len(self.subfilesDict), len(self.subfoldersDict) + print(f'set {nFiles} (sub)files and {nFolders} subfolders') self.activeFolder = activeFolder return print(f'_folders, handleTrackFilesAndFolders, invalid activeFolder: {activeFolder}') @@ -265,17 +255,12 @@ def fillList(self, listName): self.setList('files', items) return items elif listName == 'recentfolders': + self.loadRecentFoldersDict() if self.recentfoldersDict: items = list(self.recentfoldersDict.keys()) self.setList('recentfolders', items) return items - print('no recentfolders in iniChangingData.ini') self.emptyList('recentfolders') - - elif listName in ['gitfilecommands', 'gitfoldercommands']: - if self.doGit: - return ancestor.fillList(self, listName) - self.emptyList(listName) else: return ancestor.fillList(self, listName) return None @@ -299,10 +284,6 @@ def fillInstanceVariables(self): """ #pylint:disable=R0914 - self.citrixApps = self.ini.getList('general', 'citrix apps') - if self.citrixApps: - print('_folders does special action for citrixApps: %s'% self.citrixApps) - self.xxExplorer = self.ini.get('general', '2xexplorer') self.useOtherExplorer = self.ini.get('general', 'use other explorer') if self.useOtherExplorer: if os.path.isfile(self.useOtherExplorer): @@ -315,7 +296,7 @@ def fillInstanceVariables(self): if interval: self.trackFoldersInterval = int(interval*1000) # give in seconds else: - self.trackFoldersInterval = 4000 # default 5 seconds + self.trackFoldersInterval = 4000 # default 4 seconds self.recentfoldersDict = {} inipath = self.ini.getFilename() @@ -373,8 +354,8 @@ def fillInstanceVariables(self): self.trackFolders = self.ini.getList('general', 'track folders virtualdrives') self.trackFiles = self.ini.getList('general', 'track files virtualdrives') # below this threshold, the getting of subfolders and files in a directory is not printed in the messages window - self.notifyThresholdMilliseconds = self.ini.getInt("general", "notify threshold milliseconds", 10) - print("_folders, notify threshold milliseconds: %s"% self.notifyThresholdMilliseconds) + self.notifyThresholdMilliseconds = self.ini.getInt("general", "notify threshold milliseconds", 50) + # print("_folders, notify threshold milliseconds: %s"% self.notifyThresholdMilliseconds) # in order to accept .py but it should be (for fnmatch) *.py etc.: self.acceptFileExtensions = self.ini.getList('general', 'track file extensions') self.ignoreFilePatterns = self.ini.getList('general', 'ignore file patterns') @@ -383,14 +364,6 @@ def fillInstanceVariables(self): self.trackAutoFolders = self.ini.getBool('general', 'automatic track folders') self.trackAutoFiles = self.ini.getBool('general', 'automatic track files') - self.doGit = self.ini.get('general', 'git executable') - if self.doGit: - if not os.path.isfile(self.doGit): - print('not a valid path to git executable: %s, ignore'% self.doGit) - self.doGit = None - if not self.doGit: - self.iniIgnoreGrammarLists.extend(['gitfoldercommands', 'gitfilecommands']) - self.foldersSections = ['folders'] # track folders: for trf in self.trackFolders: @@ -609,16 +582,24 @@ def fillListsForActiveFolder(self, activeFolder): this is for the automatic filling of the active window (either explorer, CabinetWClass, or child #32770. - - Seems to fail in windows XP and before. - + """ subs = os.listdir(activeFolder) # print 'subs: %s'% subs subfolders = [s for s in subs if os.path.isdir(os.path.join(activeFolder, s))] subfiles = [s for s in subs if os.path.isfile(os.path.join(activeFolder, s))] - self.subfoldersDict = self.getSpokenFormsDict(subfolders) - self.subfilesDict = self.getSpokenFormsDict(subfiles, extensions=1) + if len(subfiles) <= 20: + self.subfilesDict = self.getSpokenFormsDict(subfiles, extensions=1) + else: + print(f'_folders, do not set sub files, too many: {len(subfiles)}') + self.subfilesDict = {} + + if len(subfolders) < 50: + self.subfoldersDict = self.getSpokenFormsDict(subfolders) + else: + print(f'_folders, do not set sub folders, too many: {len(subfolders)}') + self.subfoldersDict = {} + # print 'activeFolder, %s, subfolders: %s'% (activeFolder, self.subfoldersDict.keys()) # print 'activeFolder, %s, subfiles: %s'% (activeFolder, self.subfilesDict.keys()) # print 'activeFolder, %s, subfiles: %s'% (activeFolder, self.subfilesDict) @@ -636,20 +617,12 @@ def fillListsForActiveFolder(self, activeFolder): def emptyListsForActiveFolder(self): """no sublists, empty """ - n0 = time.time() - lenSubFolders = len(self.subfoldersDict) - lenSubFiles = len(self.subfilesDict) if self.trackAutoFiles: self.emptyList('subfiles') self.subfilesDict.clear() if self.trackAutoFolders: self.emptyList('subfolders') self.subfoldersDict.clear() - n1 = time.time() - elapsed = int((n1 - n0)*1000) - if elapsed: - # if elapsed > self.notifyThresholdMilliseconds: - print('emptyListsForActiveFolder: emptied %s subfolders and %s (sub)files in %s milliseconds'% (lenSubFolders, lenSubFiles, elapsed)) self.activeFolder = None @@ -775,28 +748,21 @@ def manageRecentFolders(self, Spoken, Folder): # print("refilling recentfolders list with %s items'"% len(self.recentfoldersDict)) self.setList('recentfolders', list(self.recentfoldersDict.keys())) self.dumpRecentFoldersDict() - # self.pickleChangingData.delete('recentfolders') - # for key, value in self.recentfoldersDict.items(): - # # self.pickleChangingData.set("recentfolders", key, value) - # self.pickleChangingData.writeIfChanged() if not Spoken: return if Spoken in self.recentfoldersDict: spokenFolder = self.recentfoldersDict[Spoken] if spokenFolder == Folder: + print(f'readd {Spoken} to recentfoldersDict') del self.recentfoldersDict[Spoken] self.recentfoldersDict[Spoken] = Folder self.dumpRecentFoldersDict() - # self.pickleChangingData.set("recentfolders", Spoken, Folder) # print('re-enter Folder in recent folders: %s (%s)'% (Spoken, Folder)) elif Folder not in self.foldersSet: print('-- "recent [folder] %s": %s\nNote: "folder %s", points to: %s'% (Spoken, Folder, Spoken, spokenFolder)) del self.recentfoldersDict[Spoken] self.recentfoldersDict[Spoken] = Folder self.dumpRecentFoldersDict() - ## try to maintain order: - # self.pickleChangingDatahangingData.delete('recentfolders', Spoken) - # self.pickleChangingData.set("recentfolders", Spoken, Folder) else: # print('adding Folder in recent folders: %s (%s)'% (Spoken, Folder)) self.recentfoldersDict[Spoken] = Folder @@ -829,16 +795,21 @@ def resetRecentFolders(self): def displayRecentFolders(self): """display the list of recent folders """ - message = ["_folders, recent folders:"] - for name, value in self.recentfoldersDict.items(): - message.append('- %s: %s'% (name, value)) - message.append('-'*20) - message = '\n'.join(mess) + mess_list = ["--- recent folders:"] + if not self.recentfoldersDict: + message = 'recent folders list is empty at the moment' + self.prevDisplayRecentFolders = message + print(message) + return + for name, value in reversed(self.recentfoldersDict.items()): + mess_list.append('- %s: %s'% (name, value)) + mess_list.append('-'*20) + message = '\n'.join(mess_list) if message == self.prevDisplayRecentFolders: print("recent folders, no change") - return - self.prevDisplayRecentFolders = message - print(message) + else: + self.prevDisplayRecentFolders = message + print(message) # def gotoRecentFolder(self, chooseNum): @@ -1097,7 +1068,7 @@ def gotResults_foldercommands(self, words, fullResults): if not self.wantedFolder: print('rule foldercommands, no wantedFolder, return') return - nextGit = nextRemote = False + nextRemote = False for w in words: if self.hasCommon(w, 'here'): print("got Here: ", w) @@ -1121,14 +1092,6 @@ def gotResults_foldercommands(self, words, fullResults): self.Remote = self.virtualDriveDict[remoteVirtualDrive] print('remoteVirtualDrive: %s, resolves to: %s'% (remoteVirtualDrive, self.Remote)) nextRemote = False - elif self.hasCommon(w, 'git'): - print("got Git: ", w) - nextGit = True - elif nextGit: - print("got gitCommand: ", w) - gitCommand = self.getFromInifile(w, 'gitfoldercommands') - self.Git = gitCommand - nextGit = False # again else: opt = self.getFromInifile(w, 'foldercommands') print("got FolderOptions: ", opt) @@ -1421,14 +1384,6 @@ def gotResults_filecommands(self, words, fullResults): self.Remote = self.virtualDriveDict[remoteVirtualDrive] print('remoteVirtualDrive: %s, resolves to: %s'% (remoteVirtualDrive, self.Remote)) Remote = False - elif self.hasCommon(w, 'git'): - print("got Git: ", w) - Git = True - elif Git: - print("got gitCommand: ", w) - gitCommand = self.getFromInifile(w, 'gitfilecommands') - self.Git = gitCommand - Git = False # again else: act = self.getFromInifile(w, 'foldercommands') print("got FileCommand: ", act) @@ -1459,6 +1414,7 @@ def gotResults_thisfolder(self,words,fullResults): keystroke("{ctrl+c}") unimacroutils.Wait() paths1 = natlinkclipboard.Clipboard.Get_folderinfo() + unimacroutils.restoreClipboard() if paths1: paths1 = [p for p in paths1 if os.path.isdir(p)] paths2 = get_selected_files(folders=True) @@ -1691,12 +1647,10 @@ def gotoWebsite(self, f): """goto the file f, options in instance variables FileOptions: list - Git (with gitfileoptions), False or the git action to be taken Remote, False or the virtual drive to be inserted Open, False or app to Open with (default) Edit, False or app to Edit with, if fails, take Notepad - ##special case for citrix """ if self.Open: print("gotoWebsite %s with: %s", (f, self.Open)) @@ -1708,30 +1662,13 @@ def gotoFile(self, f): """goto the file f, options in instance variables FileOptions: list - Git (with gitfileoptions), False or the git action to be taken Remote, False or the virtual drive to be inserted Open, False or app to Open with (default) Edit, False or app to Edit with, if fails, take Notepad - - ##special case for citrix """ - if self.citrixApps: - prog = self.progInfo.prog - - print('citrixApps: %s app: %s'% (self.citrixApps, prog)) - if prog in self.citrixApps: - print('doing action gotoFolder for citrixApp: %s'% prog) - action("<>") - keystroke(f) - - # keystroke("{enter}") - return - if not os.path.isfile(f): self.DisplayMessage('file does not exist: %s'% f) return - prog = self.progInfo.prog - # istop logic, with functions from action.py module, settings from: # child behaves like top = natspeak: dragon-balk # top behaves like child = komodo: find, komodo; thunderbird: bericht opslaan @@ -1745,10 +1682,6 @@ def gotoFile(self, f): if not f: return - if self.Git: - print('git command "%s" for file "%s"'% (self.Git, f)) - self.doGitCommand(self.Git, f) - return mode = 'edit' if self.Open: mode = 'open' @@ -1822,7 +1755,7 @@ def openFolderDefault(self, foldername, mode=None, windowStyle=None, openWith=No def gotoFolder(self, f): """go to the specified folder - all the options are via instance variables, New, Here, Copy, Paste, Remote, Git (all False by default) + all the options are via instance variables, New, Here, Copy, Paste, Remote (all False by default) and FolderOptions (a list, initially empty). f = the (local) folder to go to @@ -1835,7 +1768,6 @@ def gotoFolder(self, f): --Remote: pass the (virtual) drive where the folder is wanted --Copy: investigate --Paste: only paste the path at the place where you are. - --Git: do a git command on the folder. To be done. this is the central routine, with complicated strategy for getting it, in pseudocode: @@ -1879,11 +1811,6 @@ def gotoFolder(self, f): else: if part of path is common, switch to that and goto folder - ## remove subversion support, - ## git support if git executable isdefined in section [general] - - ##special if citrixApps is set, just open the folder. - """ ## undoncitionally if folderoption New is used: if self.New: @@ -1891,15 +1818,6 @@ def gotoFolder(self, f): return prog = self.progInfo.prog - if self.citrixApps: - - print('citrixApps: %s app: %s'% (self.citrixApps, prog)) - if prog in self.citrixApps: - print('doing action gotoFolder for citrixApp: %s'% prog) - action("<>") - keystroke(f) - keystroke("{enter}") - return f = os.path.normpath(f) if not os.path.isdir(f): self.DisplayMessage('folder does not exist: %s'% f) @@ -1911,10 +1829,6 @@ def gotoFolder(self, f): action('SCLIP(%s)'% f) return - if self.Git: - self.doGitCommand(self.Git, f) - - # xx = self.xxExplorer if self.Remote: print('Remote: %s'% self.Remote) f = self.getValidDirectory(f, self.Remote) @@ -2124,30 +2038,6 @@ def gotResults(self, words,fullResults): self.gotoFile(self.wantedFile) if self.wantedWebsite: self.gotoWebsite(self.wantedWebsite) - - - def doGitCommand(self, command, path): - """launch git with command and path - """ - args = '/command:%s /path:""%s""'% (command, path) - - # Construct arguments and spawn TortoiseSVN. - name = "git %s %s"% (command, path) - print('future git %s, %s'% (name, args)) - ## does not work (yet)... - # unimacroutils.AppBringUp(name, self.doGit, args) - -# return 1 - - def doStart2xExplorer(self): - """starting the 2xExplorer, obsolete - - """ - command = 'AppBringUp "%s"'% self.xxExplorer -## print 'starting 2xExplorer: %s'% command - natlink.execScript(command) - unimacroutils.Wait(1.0) - keystroke("{alt+space}{extdown 4}{enter}") def gotoInThisComputer(self, f): """perform the keystrokes to go to a folder in this computer diff --git a/src/unimacro/natlinkutilsbj.py b/src/unimacro/natlinkutilsbj.py index 9900f80..3eff56b 100644 --- a/src/unimacro/natlinkutilsbj.py +++ b/src/unimacro/natlinkutilsbj.py @@ -1527,7 +1527,7 @@ def switchOn(self, **kw): # try: self.fillGrammarLists() - print(f'IniGrammar switched on: {self.getName()}') + # print(f'IniGrammar switched on: {self.getName()}') def showInifile(self, body=None, grammarLists=None, ini=None, showGramspec=1, prefix=None, commandExplanation=None, From bea933baf7d23b82d7746763d22cc5e0eae2d36a Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Fri, 16 Feb 2024 15:35:08 +0100 Subject: [PATCH 11/26] logging _folders settings --- .../UnimacroGrammars/settings folders grammar.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/unimacro/UnimacroGrammars/settings folders grammar.txt diff --git a/src/unimacro/UnimacroGrammars/settings folders grammar.txt b/src/unimacro/UnimacroGrammars/settings folders grammar.txt new file mode 100644 index 0000000..b80c31e --- /dev/null +++ b/src/unimacro/UnimacroGrammars/settings folders grammar.txt @@ -0,0 +1,12 @@ + +old settings (until feb 2024) +[general] +automatic track files = T +automatic track folders = T +child behaves like top = natspeak: dragonbar, dictation box, messages +ignore file patterns = ~*; .* +initial on = 1 +track file extensions = .py; .txt; .ini; .docx; .xlsx; .pdf +track files virtualdrives = +track folders virtualdrives = + From 24ce0c8d23b4561deb4f4f558c4b902a84b2ba6e Mon Sep 17 00:00:00 2001 From: Quintijn Date: Fri, 16 Feb 2024 15:37:15 +0100 Subject: [PATCH 12/26] working on the _folders grammar option and checking automatic tracking of recent folders and files en subfolders --- src/unimacro/UnimacroGrammars/_folders.py | 47 ++++++++++++----------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index 9d5bb59..666d138 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -151,6 +151,7 @@ def initialize(self): self.dialogNumberRange = [] # ditto self.catchRemember = "" self.activeFolder = None + self.inTimerRecentFolders = False self.prevDisplayRecentFolders = None # displaying recent folders list self.subfoldersDict = {} self.subfilesDict = {} @@ -292,22 +293,16 @@ def fillInstanceVariables(self): print('_folders, variable "use other explorer" set to: "%s" (use data from "actions.ini")'% self.useOtherExplorer) ## callback time in seconds: - interval = self.ini.getFloat('general', 'track folders interval') - if interval: - self.trackFoldersInterval = int(interval*1000) # give in seconds - else: - self.trackFoldersInterval = 4000 # default 4 seconds - + interval = self.ini.getFloat('general', 'timer track folders interval') + self.trackFoldersInterval = int(interval*1000) # give in seconds + if self.trackFoldersInterval: + print(f'track active folder every {interval:f.1} seconds') self.recentfoldersDict = {} inipath = self.ini.getFilename() - if inipath.endswith('.ini'): - _changingDataIniPath = inipath.replace(".ini", "changingdata.pickle") - self.pickleChangingData = inipath.replace(".ini", "changingdata.pickle") - else: - self.pickleChangingData = "" + self.pickleChangingData = Path(status.getUnimacroDataDirectory())/"recentfoldersdata.pickle") ## automatic tracking of recent folders : - self.trackFoldersHistory = self.ini.getInt('general', 'track folders history') + self.trackFoldersHistory = self.ini.getInt('general', 'timer track folders interval') if self.trackFoldersHistory: if self.pickleChangingData: self.recentfoldersDict = self.loadRecentFoldersDict() @@ -718,18 +713,24 @@ def catchTimerRecentFolders(self, hndle=None, className=None): (QH, March 2020) """ # print('_folders, catchTimerRecentFolders') - activeFolder = self.getActiveFolder(hndle, className) - if not activeFolder: - return - if activeFolder == self.activeFolder: - return - self.activeFolder = activeFolder - # print(f'get new folders for {activeFolder}') - # activeFolder = os.path.normcase(activeFolder) - if self.recentfoldersDict and activeFolder == list(self.recentfoldersDict.values())[-1]: + if self.inTimerRecentFolders: return - spokenName = self.getFolderBasenameRemember(activeFolder) - self.manageRecentFolders(spokenName, activeFolder) + self.inTimerRecentFolders = True + try: + activeFolder = self.getActiveFolder(hndle, className) + if not activeFolder: + return + if activeFolder == self.activeFolder: + return + self.activeFolder = activeFolder + print(f'get new folders for {activeFolder}') + # activeFolder = os.path.normcase(activeFolder) + if self.recentfoldersDict and activeFolder == list(self.recentfoldersDict.values())[-1]: + return + spokenName = self.getFolderBasenameRemember(activeFolder) + self.manageRecentFolders(spokenName, activeFolder) + finally: + self.inTimerRecentFolders = False def manageRecentFolders(self, Spoken, Folder): """manage the internals of the recent folders dict From 743f6bb268c85b225f66d1227a19df4d0dc77be7 Mon Sep 17 00:00:00 2001 From: Quintijn Date: Fri, 16 Feb 2024 15:46:43 +0100 Subject: [PATCH 13/26] work in progress _folders.py --- src/unimacro/UnimacroGrammars/_folders.py | 2 +- .../settings folders grammar.txt | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index 666d138..9b5a477 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -299,7 +299,7 @@ def fillInstanceVariables(self): print(f'track active folder every {interval:f.1} seconds') self.recentfoldersDict = {} inipath = self.ini.getFilename() - self.pickleChangingData = Path(status.getUnimacroDataDirectory())/"recentfoldersdata.pickle") + self.pickleChangingData = Path(status.getUnimacroDataDirectory())/"recentfoldersdata.pickle" ## automatic tracking of recent folders : self.trackFoldersHistory = self.ini.getInt('general', 'timer track folders interval') diff --git a/src/unimacro/UnimacroGrammars/settings folders grammar.txt b/src/unimacro/UnimacroGrammars/settings folders grammar.txt index b80c31e..f5907c5 100644 --- a/src/unimacro/UnimacroGrammars/settings folders grammar.txt +++ b/src/unimacro/UnimacroGrammars/settings folders grammar.txt @@ -10,3 +10,19 @@ track file extensions = .py; .txt; .ini; .docx; .xlsx; .pdf track files virtualdrives = track folders virtualdrives = +newer settings febr 2024 +[general] +ignore file patterns = ~*; .* +initial on = 1 +max files in folder = 40 +max subfolders in folder = 75 +notify threshold milliseconds = 50 +start this computer = HW start, My Computer +start windows explorer = HW start, Windows explorer +track file extensions = .py; .txt; .ini; .jpg; .jpeg; .png; .docx; .xlsx +track files at begin utterance = T +track files virtualdrives = +track folders at begin utterance = T +track folders history = 75 +track folders virtualdrives = md; dr; pr; fb +track recent folders timer interval = 4 From 233b6ffe2d096789163d27b3a4243a7539d6fc8b Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Fri, 16 Feb 2024 15:50:18 +0100 Subject: [PATCH 14/26] isolate fillinstancevariables (old) of _folders.py (txt) --- .../_folders fillinstancevariables.txt | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 src/unimacro/UnimacroGrammars/_folders fillinstancevariables.txt diff --git a/src/unimacro/UnimacroGrammars/_folders fillinstancevariables.txt b/src/unimacro/UnimacroGrammars/_folders fillinstancevariables.txt new file mode 100644 index 0000000..b089af3 --- /dev/null +++ b/src/unimacro/UnimacroGrammars/_folders fillinstancevariables.txt @@ -0,0 +1,151 @@ +fillinstancevariables old: + def fillInstanceVariables(self): + """fills the necessary instance variables + take the lists of folders, virtualdrives (optional) and remotedrives (optional). + + """ + #pylint:disable=R0914 + self.useOtherExplorer = self.ini.get('general', 'use other explorer') + if self.useOtherExplorer: + if os.path.isfile(self.useOtherExplorer): + print('_folders, use as default explorer: "%s"'% self.useOtherExplorer) + else: + print('_folders, variable "use other explorer" set to: "%s" (use data from "actions.ini")'% self.useOtherExplorer) + + ## callback time in seconds: + interval = self.ini.getFloat('general', 'track folders interval') + if interval: + self.trackFoldersInterval = int(interval*1000) # give in seconds + else: + self.trackFoldersInterval = 4000 # default 4 seconds + + self.recentfoldersDict = {} + inipath = self.ini.getFilename() + if inipath.endswith('.ini'): + _changingDataIniPath = inipath.replace(".ini", "changingdata.pickle") + self.pickleChangingData = inipath.replace(".ini", "changingdata.pickle") + else: + self.pickleChangingData = "" + + ## automatic tracking of recent folders : + self.trackFoldersHistory = self.ini.getInt('general', 'track folders history') + if self.trackFoldersHistory: + if self.pickleChangingData: + self.recentfoldersDict = self.loadRecentFoldersDict() + if self.recentfoldersDict: + print("recentfolders, set %s keys from _folderschangingdata.pickle"% len(self.recentfoldersDict)) + else: + print("recentfolder, no previous recentfolders cached in _folderschangingdata.pickle") + + self.doTrackFoldersHistory = True # can be started or stopped with command + # recent [folders] START or recent [folders] STOP + intervalSeconds = self.trackFoldersInterval/1000 + print('maintain a list of %s recent folders (Explorer or File Dialog) at every utterance and every %s seconds'% (self.trackFoldersHistory, intervalSeconds)) + natlinktimer.setTimerCallback(self.catchTimerRecentFolders, self.trackFoldersInterval) # every 5 seconds + else: + self.doTrackFoldersHistory = False + if self.doTrackFoldersHistory: + rfList = self.ini.get('recentfolders') + for key in rfList: + value = self.ini.get('recentfolders', key) + self.recentfoldersDict[key] = value + # extract special variables from ini file: + self.virtualDriveDict = {} + wantedVirtualDriveList = self.ini.get('virtualdrives') + if wantedVirtualDriveList: + self.resolveVirtualDrives(wantedVirtualDriveList) + self.virtualDriveList = list(self.virtualDriveDict.keys()) + # print '_folders, virtual drives from dict: %s'% repr(self.virtualDriveDict) + # print '_folders, virtual drives from list: %s'% ', '.join(self.virtualDriveList) + + # checking the passes of all folders: + foldersList = self.ini.get('folders') + self.foldersDict = {} + for f in foldersList: + raw_folder = self.ini.get('folders', f) + folder = self.substituteFolder(raw_folder) + if not os.path.isdir(folder): + print(f'warning _folders, folder "{f}" does not exist: "{folder}"') + # self.ini.delete('folders', f) + # self.ini.set('obsolete folders', f, folder) + continue + self.foldersDict[f] = folder + + # track virtual drives if in ini file: + self.trackFolders = self.ini.getList('general', 'track folders virtualdrives') + self.trackFiles = self.ini.getList('general', 'track files virtualdrives') + # below this threshold, the getting of subfolders and files in a directory is not printed in the messages window + self.notifyThresholdMilliseconds = self.ini.getInt("general", "notify threshold milliseconds", 50) + # print("_folders, notify threshold milliseconds: %s"% self.notifyThresholdMilliseconds) + # in order to accept .py but it should be (for fnmatch) *.py etc.: + self.acceptFileExtensions = self.ini.getList('general', 'track file extensions') + self.ignoreFilePatterns = self.ini.getList('general', 'ignore file patterns') + + # these are for automatic tracking the current folder: + self.trackAutoFolders = self.ini.getBool('general', 'automatic track folders') + self.trackAutoFiles = self.ini.getBool('general', 'automatic track files') + + self.foldersSections = ['folders'] + # track folders: + for trf in self.trackFolders: + if not trf: + continue + trf2 = self.substituteFolder(trf) + #print 'input: %s, output: %s'% (trf, trf2) + if not os.path.isdir(trf2): + print('warning, no valid folder associated with: %s (%s) (skip for track virtualdrives)'% (trf, trf2)) + continue + #else: + # print 'valid folder for tracking: %s (%s)'% (trf, trf2) + subf = [f for f in os.listdir(trf2) if os.path.isdir(os.path.join(trf2, f))] + self.trackFoldersSection = 'folders %s'% trf + self.ini.delete(self.trackFoldersSection) # not in inifile + self.foldersSections.append(self.trackFoldersSection) + self.acceptVirtualDrivesFolder(trf, trf2) # without f, take virtualdrive itself... + for f in subf: + ## if no strange signs in folder name: + self.acceptVirtualDrivesFolder(trf, trf2, f) + #self.cleanupIniFoldersSection(self.trackFoldersSection, trf) + #self.removeObsoleteIniSections(prefix="folders ", validPostfixes=self.trackFolders) + self.removeObsoleteIniSections(prefix="folders ", validPostfixes=[]) # do not keep in ini file + + # do the files: + self.filesDict = {} + self.trackFiles = self.ini.getList('general', 'track files virtualdrives') + # in order to accept .py but it should be (for fnmatch) *.py etc.: + self.acceptFileExtensions = self.ini.getList('general', 'track file extensions') + self.ignoreFilePatterns = self.ini.getList('general', 'ignore file patterns') + self.filesSections = ['files'] + # from section files (manual): + filesList = self.ini.get('files') + if filesList: + for f in filesList[:]: + filename = self.substituteFilename(self.ini.get('files', f)) + if not os.path.isfile(filename): + print(f'warning _folders, file "{f}" does not exist: "{filename}"') + # self.ini.delete('files', f) + # self.ini.set('obsolete files', f, filename) + continue + self.filesDict[f] = filename + + for trf in self.trackFiles: + if not trf: + continue + trf2 = self.substituteFolder(trf) + if not os.path.isdir(trf2): + print('warning, no valid folder associated with: %s (%s) (skip for track files)'% (trf, trf2)) + continue + filesList = [f for f in os.listdir(trf2) if os.path.isfile(os.path.join(trf2, f))] + self.trackFilesSection = 'files %s'% trf + self.ini.delete(self.trackFoldersSection) # not in inifile + self.filesSections.append(self.trackFilesSection) + for f in filesList: + self.acceptFileInFilesDict(trf, trf2, f) + #self.cleanupIniFilesSection(self.trackFilesSection, trf) + self.removeObsoleteIniSections(prefix="files ", validPostfixes=[]) + #self.removeObsoleteIniSections(prefix="files ", validPostfixes=self.trackFiles) # not in inifile any more + + # self.childBehavesLikeTop = self.ini.getDict('general', 'child behaves like top') + # self.topBehavesLikeChild = self.ini.getDict('general', 'top behaves like child') + # save changes if there were any: + self.ini.writeIfChanged() From 2d33ed415742961cc520400658e9ab3774e769ea Mon Sep 17 00:00:00 2001 From: Quintijn Date: Fri, 16 Feb 2024 16:06:03 +0100 Subject: [PATCH 15/26] tidy up detail --- src/unimacro/UnimacroGrammars/_folders.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index 9b5a477..3ef4d72 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -58,6 +58,7 @@ import natlink from natlinkcore import readwritefile from natlinkcore import natlinktimer +from natlinkcore import natlinkstatus from dtactions.unimacro import extenvvars from dtactions import messagefunctions as mess from dtactions import natlinkclipboard @@ -73,7 +74,7 @@ # import natlinkcore.natlinkutils as natut thisDir = str(Path(__file__).parent) - +status = natlinkstatus.NatlinkStatus() # for getting unicode explorer window titles: GetWindowText = ctypes.windll.user32.GetWindowTextW GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW @@ -95,10 +96,6 @@ doRecentFolderCommand = True # some child windows have to behave as top window (specified in ini file): # note: title is converted to lowercase, only full title is recognised -try: - thisGrammar -except NameError: - thisGrammar = None ancestor = natbj.IniGrammar class ThisGrammar(ancestor): @@ -298,7 +295,7 @@ def fillInstanceVariables(self): if self.trackFoldersInterval: print(f'track active folder every {interval:f.1} seconds') self.recentfoldersDict = {} - inipath = self.ini.getFilename() + # inipath = self.ini.getFilename() self.pickleChangingData = Path(status.getUnimacroDataDirectory())/"recentfoldersdata.pickle" ## automatic tracking of recent folders : @@ -2017,6 +2014,7 @@ def getValidDirectory(self, f, remote): return tryF fparts.pop(0) print('_folders, no valid remote folder found for %s and remote: %s'% (f, remote)) + return '' def getValidFile(self, f, remote): _fdrive, fdir = os.path.splitdrive(f) @@ -2028,6 +2026,7 @@ def getValidFile(self, f, remote): return tryF fparts.pop(0) print('_folders, no valid remote file found for %s and remote: %s'% (f, remote)) + return '' def gotResults(self, words,fullResults): From 1244d68628f2a2af7036ddcd91bd9cb18c446417 Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Fri, 16 Feb 2024 16:08:40 +0100 Subject: [PATCH 16/26] starting option checking for _folders --- src/unimacro/UnimacroGrammars/_folders.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index 9b5a477..9758a68 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -284,8 +284,10 @@ def fillInstanceVariables(self): take the lists of folders, virtualdrives (optional) and remotedrives (optional). """ - #pylint:disable=R0914 + optionsdict = {} + self.useOtherExplorer = self.ini.get('general', 'use other explorer') + optionsdict['use other explorer'] = 'use other explorer' if self.useOtherExplorer: if os.path.isfile(self.useOtherExplorer): print('_folders, use as default explorer: "%s"'% self.useOtherExplorer) @@ -293,17 +295,17 @@ def fillInstanceVariables(self): print('_folders, variable "use other explorer" set to: "%s" (use data from "actions.ini")'% self.useOtherExplorer) ## callback time in seconds: + optionsdict['timer track folders interval'] = 'timer track folders interval' interval = self.ini.getFloat('general', 'timer track folders interval') self.trackFoldersInterval = int(interval*1000) # give in seconds if self.trackFoldersInterval: print(f'track active folder every {interval:f.1} seconds') self.recentfoldersDict = {} - inipath = self.ini.getFilename() + # inipath = self.ini.getFilename() self.pickleChangingData = Path(status.getUnimacroDataDirectory())/"recentfoldersdata.pickle" - ## automatic tracking of recent folders : - self.trackFoldersHistory = self.ini.getInt('general', 'timer track folders interval') - if self.trackFoldersHistory: + # track recent folder at gotbegin or with timer: + if self.trackFoldersHistory or ...: if self.pickleChangingData: self.recentfoldersDict = self.loadRecentFoldersDict() if self.recentfoldersDict: From c6f8e7d206aa96f8023d4bff2dc8616a71967c6f Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Mon, 19 Feb 2024 16:12:03 +0100 Subject: [PATCH 17/26] refinement with switching on a unimacro grammar --- src/unimacro/_control.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/unimacro/_control.py b/src/unimacro/_control.py index feaa2ac..c63b050 100644 --- a/src/unimacro/_control.py +++ b/src/unimacro/_control.py @@ -67,8 +67,9 @@ class UtilGrammar(ancestor): # commands for controlling module actions specialList = [] specialList.append("actions") - if spokenforms: - specialList.append("'spoken forms'") + + # if spokenforms: ## assume spokenforms is imported!!! + specialList.append("'spoken forms'") if specialList: specials = "|" + '|'.join(specialList) else: @@ -297,10 +298,13 @@ def switch(self, gram, gname, switchOn): print(f'should not be here, do not switch on of off _control {gram}') return None if switchOn: - gram.checkInifile() - gram.ini.set('general', 'initial on', 1) - gram.ini.write() - unimacroutils.Wait(0.1) + if gram.ini: + gram.checkInifile() + gram.ini.set('general', 'initial on', 1) + gram.ini.write() + unimacroutils.Wait(0.1) + else: + print(f'--- ini file of grammar {gname} is invalid, please try "edit {gname}"...') gramName = gram.getName() unimacro_grammars_paths = self.getUnimacroGrammarNamesPaths() try: @@ -526,7 +530,7 @@ def gotResults_edit(self,words,fullResults): # print(f'grammar: {gramName}: {grammar}') if self.hasCommon(words, 'grammar'): unimacro_grammars_paths = self.getUnimacroGrammarNamesPaths() - print(f'unimacro_grammars_paths:\n{unimacro_grammars_paths}\n') + # print(f'unimacro_grammars_paths:\n{unimacro_grammars_paths}\n') try: filepath = unimacro_grammars_paths[gramName] except KeyError: From 29fcc5e42d413b0bc056b59fe39fe8be101861a5 Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Mon, 19 Feb 2024 16:12:36 +0100 Subject: [PATCH 18/26] effort to diminish the errors when an ini file is invalid... --- src/unimacro/UnimacroGrammars/_folders.py | 222 +++++++++++------- .../settings folders grammar.txt | 9 +- src/unimacro/natlinkutilsbj.py | 27 ++- 3 files changed, 153 insertions(+), 105 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index 42ea7dd..8c5903c 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -93,7 +93,6 @@ Classes = ('ExploreWClass', 'CabinetWClass') # together with track folders history: -doRecentFolderCommand = True # some child windows have to behave as top window (specified in ini file): # note: title is converted to lowercase, only full title is recognised @@ -124,6 +123,8 @@ class ThisGrammar(ancestor): | {foldercommands}; exported = folder up|folder up {n1-10}; + exported = recent [folder] ({recentfolders}|SHOW|HIDE|RESET|START|STOP) []; + exported = file ({files}|{subfiles})[|]; exported = ((here|this) file) (|); = {filecommands}| on ({letters}|{virtualdrivesspoken}) | @@ -137,17 +138,14 @@ class ThisGrammar(ancestor): = (copy (name|path)) | ((name|path) copy); """ - if doRecentFolderCommand: - gramSpec += """ exported = recent [folder] ({recentfolders}|SHOW|HIDE|RESET|START|STOP) [];""" def initialize(self): # self.envDict = natlinkcorefunctions.getAllFolderEnvironmentVariables() # for (generalised) environment variables - self.subfiles = self.subfiles = self.activeFolder = None # for catching on the fly in explorer windows (CabinetClassW) + self.subfiles = self.subfiles = self.activeFolder = self.activeTimerFolder = None # for catching on the fly in explorer windows (CabinetClassW) self.className = None self.dialogWindowTitle = "" # for recent folders dialog, grammar in natspeak.py self.dialogNumberRange = [] # ditto self.catchRemember = "" - self.activeFolder = None self.inTimerRecentFolders = False self.prevDisplayRecentFolders = None # displaying recent folders list self.subfoldersDict = {} @@ -175,12 +173,12 @@ def gotBegin(self,moduleInfo): hndle = self.progInfo.hndle classname = self.progInfo.classname # activeFolder = self.getActiveFolder(hndle, classname) - if self.trackAutoFiles or self.trackAutoFolders: + if self.trackFilesAtUtterance or self.trackSubfoldersAtUtterance: activeFolder = self.getActiveFolder(hndle, classname) self.handleTrackFilesAndFolders(activeFolder) - - if hndle and self.trackFoldersHistory: - self.catchTimerRecentFolders(hndle) + + if hndle and self.trackRecentFoldersAtUtterance: + self.catchTimerRecentFolders(hndle, classname) def gotResultsInit(self,words,fullResults): if self.mayBeSwitchedOn == 'exclusive': @@ -217,11 +215,10 @@ def handleTrackFilesAndFolders(self, activeFolder): if activeFolder and os.path.isdir(activeFolder): self.fillListsForActiveFolder(activeFolder) - nFiles, nFolders = len(self.subfilesDict), len(self.subfoldersDict) - print(f'set {nFiles} (sub)files and {nFolders} subfolders') + # nFiles, nFolders = len(self.subfilesDict), len(self.subfoldersDict) + # print(f'set {nFiles} (sub)files and {nFolders} subfolders') self.activeFolder = activeFolder return - print(f'_folders, handleTrackFilesAndFolders, invalid activeFolder: {activeFolder}') def fillList(self, listName): """fill a list in the grammar from the data of the inifile @@ -266,62 +263,85 @@ def fillList(self, listName): def dumpRecentFoldersDict(self): """for making the dict of recent folders persistent """ - dumpToPickle(self.recentfoldersDict, self.pickleChangingData) + if self.pickleChangingData: + dumpToPickle(self.recentfoldersDict, self.pickleChangingData) + else: + print('dumpRecentFoldersDict, no self.pickleChangingData') def loadRecentFoldersDict(self): """for getting the dict of recent folders from previous session """ - result = loadFromPickle(self.pickleChangingData) - if result and isinstance(result, dict): - return result - return {} + if self.pickleChangingData and Path(self.pickleChangingData).is_file(): + result = loadFromPickle(str(self.pickleChangingData)) + if result and isinstance(result, dict): + self.recentfoldersDict = result + return + print('no recent folders dict') + self.recentfoldersDict = {} def fillInstanceVariables(self): """fills the necessary instance variables take the lists of folders, virtualdrives (optional) and remotedrives (optional). """ + # valid options, value possibly corresponding to an previous option text optionsdict = {} + optionsdict['initial on'] = '' + optionsdict['child behaves like top'] = '' + + actualoptions = set(self.ini.get('general')) + self.useOtherExplorer = self.ini.get('general', 'use other explorer') - optionsdict['use other explorer'] = 'use other explorer' + optionsdict['use other explorer'] = '' if self.useOtherExplorer: if os.path.isfile(self.useOtherExplorer): print('_folders, use as default explorer: "%s"'% self.useOtherExplorer) else: print('_folders, variable "use other explorer" set to: "%s" (use data from "actions.ini")'% self.useOtherExplorer) + + # these are for automatic tracking the current folder at an utterance: + optionsdict['track files at utterance'] = 'automatic track files' + optionsdict['track subfolders at utterance'] = 'automatic track folders' + self.trackSubfoldersAtUtterance = self.ini.getInt('general', 'track subfolders at utterance', 0) + self.trackFilesAtUtterance = self.ini.getInt('general', 'track files at utterance', 0) + + optionsdict['track recent folders at utterance'] = '' + optionsdict['max recent folders'] = '' + # track recent folder at gotbegin or with timer: ## callback time in seconds: - optionsdict['timer track folders interval'] = 'timer track folders interval' - interval = self.ini.getFloat('general', 'timer track folders interval') - self.trackFoldersInterval = int(interval*1000) # give in seconds - if self.trackFoldersInterval: - print(f'track active folder every {interval:f.1} seconds') + optionsdict['timer track folders interval'] = '' + interval = self.ini.getInt('general', 'timer track folders interval', 4) # default 4 sec + self.trackFoldersTimerInterval = int(interval*1000) # give in seconds + if self.trackFoldersTimerInterval: + print(f'track active folder as "recent" every {self.trackFoldersTimerInterval} milliseconds') self.recentfoldersDict = {} - # inipath = self.ini.getFilename() - self.pickleChangingData = Path(status.getUnimacroDataDirectory())/"recentfoldersdata.pickle" - - # track recent folder at gotbegin or with timer: - if self.trackFoldersHistory or ...: - if self.pickleChangingData: - self.recentfoldersDict = self.loadRecentFoldersDict() - if self.recentfoldersDict: - print("recentfolders, set %s keys from _folderschangingdata.pickle"% len(self.recentfoldersDict)) - else: - print("recentfolder, no previous recentfolders cached in _folderschangingdata.pickle") - self.doTrackFoldersHistory = True # can be started or stopped with command + self.trackRecentFoldersAtUtterance = self.ini.getBool('general', 'track recent folders at utterance') + self.maxRecentFolders = 0 + + self.pickleChangingData = str(Path(status.getUnimacroDataDirectory())/"recentfoldersdata.pickle") + if self.trackFoldersTimerInterval or self.trackRecentFoldersAtUtterance: + + optionsdict['max recent folders'] = '' + self.maxRecentFolders = self.ini.getInt('general', 'max recent folders', 50) + self.doTrackRecentFolders = True # can be started or stopped with command # recent [folders] START or recent [folders] STOP - intervalSeconds = self.trackFoldersInterval/1000 - print('maintain a list of %s recent folders (Explorer or File Dialog) at every utterance and every %s seconds'% (self.trackFoldersHistory, intervalSeconds)) - natlinktimer.setTimerCallback(self.catchTimerRecentFolders, self.trackFoldersInterval) # every 5 seconds + intervalSeconds = int(self.trackFoldersTimerInterval/1000) + if self.trackFoldersTimerInterval or self.trackRecentFoldersAtUtterance: + if not self.trackFoldersTimerInterval: + print(f'maintain a list of (max) {self.maxRecentFolders} recent folders (Explorer or File Dialog) at every utterance') + elif not self.trackRecentFoldersAtUtterance: + print(f'maintain a list of (max) {self.maxRecentFolders} recent folders (Explorer or File Dialog) every {intervalSeconds} seconds') + else: + print(f'maintain a list of (max) {self.maxRecentFolders} recent folders (Explorer or File Dialog) at every utterance and every {intervalSeconds} seconds') + if self.trackFoldersTimerInterval: + natlinktimer.setTimerCallback(self.catchTimerRecentFolders, self.trackFoldersTimerInterval) # every 5 seconds default... else: - self.doTrackFoldersHistory = False - if self.doTrackFoldersHistory: - rfList = self.ini.get('recentfolders') - for key in rfList: - value = self.ini.get('recentfolders', key) - self.recentfoldersDict[key] = value + self.doTrackRecentFolders = False + + # virtual drives: # extract special variables from ini file: self.virtualDriveDict = {} wantedVirtualDriveList = self.ini.get('virtualdrives') @@ -345,18 +365,18 @@ def fillInstanceVariables(self): self.foldersDict[f] = folder # track virtual drives if in ini file: + optionsdict['track folders virtualdrives'] = '' + optionsdict['track files virtualdrives'] = '' self.trackFolders = self.ini.getList('general', 'track folders virtualdrives') self.trackFiles = self.ini.getList('general', 'track files virtualdrives') - # below this threshold, the getting of subfolders and files in a directory is not printed in the messages window - self.notifyThresholdMilliseconds = self.ini.getInt("general", "notify threshold milliseconds", 50) - # print("_folders, notify threshold milliseconds: %s"% self.notifyThresholdMilliseconds) - # in order to accept .py but it should be (for fnmatch) *.py etc.: + + # in order to accept files in the list but it should be (for fnmatch) *.py etc.: + optionsdict['track file extensions'] = '' + optionsdict['ignore file patterns'] = '' + self.acceptFileExtensions = self.ini.getList('general', 'track file extensions') self.ignoreFilePatterns = self.ini.getList('general', 'ignore file patterns') - # these are for automatic tracking the current folder: - self.trackAutoFolders = self.ini.getBool('general', 'automatic track folders') - self.trackAutoFiles = self.ini.getBool('general', 'automatic track files') self.foldersSections = ['folders'] # track folders: @@ -386,8 +406,6 @@ def fillInstanceVariables(self): self.filesDict = {} self.trackFiles = self.ini.getList('general', 'track files virtualdrives') # in order to accept .py but it should be (for fnmatch) *.py etc.: - self.acceptFileExtensions = self.ini.getList('general', 'track file extensions') - self.ignoreFilePatterns = self.ini.getList('general', 'ignore file patterns') self.filesSections = ['files'] # from section files (manual): filesList = self.ini.get('files') @@ -421,8 +439,26 @@ def fillInstanceVariables(self): # self.childBehavesLikeTop = self.ini.getDict('general', 'child behaves like top') # self.topBehavesLikeChild = self.ini.getDict('general', 'top behaves like child') # save changes if there were any: - self.ini.writeIfChanged() + self.ini.writeIfChanged() + + self.checkValidOptions(optionsdict, actualoptions) + def checkValidOptions(self, optionsdict, actualoptions): + """check if all options given are valid and give feedback + """ + validoptions = set(optionsdict) + oldoptions = actualoptions - validoptions + if oldoptions: + print(f'obsolete options: {oldoptions}') + # give new option, if available: + for old in oldoptions: + for k,v in optionsdict.items(): + if v == old: + print(f'replace option "{old}" into "{k}" please') + break + unusedoptions = validoptions - actualoptions + for unused in unusedoptions: + print(f'unset option for _folders: "{unused}",\n\tplease set (possibly without value), section [general]') def fillGrammarLists(self, listOfLists=None): """fills the lists of the grammar with data from inifile @@ -537,6 +573,7 @@ def getActiveFolder(self, hndle=None, className=None): if not className: return None f = None + if className == "CabinetWClass": f = mess.getFolderFromCabinetWClass(hndle) # if f and f.startswith("search-ms"): @@ -549,7 +586,7 @@ def getActiveFolder(self, hndle=None, className=None): f = mess.getFolderFromDialog(hndle, className) if not f: return None - # if not f: + # if not f: # print "getActiveFolder, #32770 failed: %s"% hndle else: # print 'class for activeFolder: %s'% className @@ -582,39 +619,44 @@ def fillListsForActiveFolder(self, activeFolder): # print 'subs: %s'% subs subfolders = [s for s in subs if os.path.isdir(os.path.join(activeFolder, s))] subfiles = [s for s in subs if os.path.isfile(os.path.join(activeFolder, s))] - if len(subfiles) <= 20: + if self.trackFilesAtUtterance: + if len(subfiles) > self.trackFilesAtUtterance: + print(f'_folders, only set first {self.trackFilesAtUtterance} files, total: {len(subfiles)}') + subfiles = subfiles[:self.trackFilesAtUtterance] self.subfilesDict = self.getSpokenFormsDict(subfiles, extensions=1) else: - print(f'_folders, do not set sub files, too many: {len(subfiles)}') self.subfilesDict = {} - - if len(subfolders) < 50: + + if self.trackSubfoldersAtUtterance: + if len(subfolders) > self.trackSubfoldersAtUtterance: + print(f'_folders, only set first {self.trackSubfoldersAtUtterance} subfolders of total: {len(subfolders)}') + subfolders = subfolders[:self.trackSubfoldersAtUtterance] self.subfoldersDict = self.getSpokenFormsDict(subfolders) else: - print(f'_folders, do not set sub folders, too many: {len(subfolders)}') self.subfoldersDict = {} # print 'activeFolder, %s, subfolders: %s'% (activeFolder, self.subfoldersDict.keys()) # print 'activeFolder, %s, subfiles: %s'% (activeFolder, self.subfilesDict.keys()) # print 'activeFolder, %s, subfiles: %s'% (activeFolder, self.subfilesDict) - if self.trackAutoFiles and self.subfilesDict: + if self.subfilesDict: self.setList('subfiles', list(self.subfilesDict.keys())) - if self.trackAutoFolders and self.subfoldersDict: - n0 = time.time() + if self.subfoldersDict: self.setList('subfolders', list(self.subfoldersDict.keys())) - n1 = time.time() - elapsed = int((n1 - n0)*1000) - if elapsed > self.notifyThresholdMilliseconds: - print('set %s subfolders in %s milliseconds'% (len(list(self.subfoldersDict.keys())), elapsed)) self.activeFolder = activeFolder + if self.subfilesDict and self.subfoldersDict: + print(f'activeFolder, set {len(subfiles)} files and {len(subfolders)} subfolders') + elif self.subfilesDict: + print(f'activeFolder, set {len(subfiles)} files') + elif self.subfoldersDict: + print(f'activeFolder, set {len(subfolders)} subfolders') def emptyListsForActiveFolder(self): """no sublists, empty """ - if self.trackAutoFiles: + if self.trackFilesAtUtterance: self.emptyList('subfiles') self.subfilesDict.clear() - if self.trackAutoFolders: + if self.trackSubfoldersAtUtterance: self.emptyList('subfolders') self.subfoldersDict.clear() self.activeFolder = None @@ -706,10 +748,10 @@ def catchTimerRecentFolders(self, hndle=None, className=None): Or with the subfolder or folder ... on virtual drive command. - Whenever there is a folder in the foreground, is is cached as recentfolder. + Whenever there is a folder in the foreground, it is cached as recentfolder. When the buffer grows too large, the first inserted items are removed from the list - (QH, March 2020) + (QH, March 2020, Febr 2024) """ # print('_folders, catchTimerRecentFolders') if self.inTimerRecentFolders: @@ -719,14 +761,14 @@ def catchTimerRecentFolders(self, hndle=None, className=None): activeFolder = self.getActiveFolder(hndle, className) if not activeFolder: return - if activeFolder == self.activeFolder: + if activeFolder == self.activeTimerFolder: return - self.activeFolder = activeFolder - print(f'get new folders for {activeFolder}') + self.activeTimerFolder = activeFolder # activeFolder = os.path.normcase(activeFolder) if self.recentfoldersDict and activeFolder == list(self.recentfoldersDict.values())[-1]: return spokenName = self.getFolderBasenameRemember(activeFolder) + print(f'add activeFolder "{activeFolder}" to recent, spoken name: "{spokenName}"') self.manageRecentFolders(spokenName, activeFolder) finally: self.inTimerRecentFolders = False @@ -738,10 +780,10 @@ def manageRecentFolders(self, Spoken, Folder): Or from the subfolder or folder ... on virtual drive commands """ # first see if the buffer needs to be shrinked: - buffer = max(10, self.trackFoldersHistory//10) - if len(self.recentfoldersDict) > self.trackFoldersHistory + buffer: - print("shrink recentfoldersDict with %s items to %s"% (buffer, self.trackFoldersHistory)) - while len(self.recentfoldersDict) >= self.trackFoldersHistory: + buffer = max(10, self.maxRecentFolders//10) + if len(self.recentfoldersDict) > self.maxRecentFolders + buffer: + print("shrink recentfoldersDict with %s items to %s"% (buffer, self.maxRecentFolders)) + while len(self.recentfoldersDict) >= self.maxRecentFolders: keysList = list(self.recentfoldersDict.keys()) _removeItem = self.recentfoldersDict.pop(keysList[0]) # print('_folders, remove from recent folders: %s (%s)'% (keysList[0], removeItem)) @@ -772,18 +814,18 @@ def manageRecentFolders(self, Spoken, Folder): # self.pickleChangingData.writeIfChanged() def startRecentFolders(self): - self.doTrackFoldersHistory = True + self.doTrackRecentFolders = True self.fillList('recentfolders') - natlinktimer.setTimerCallback(self.catchTimerRecentFolders, self.trackFoldersInterval) # should have milliseconds - print("track folders history is started, the timer callback is on") + natlinktimer.setTimerCallback(self.catchTimerRecentFolders, self.trackFoldersTimerInterval) # should have milliseconds + print("the track recent folders timer is started") def stopRecentFolders(self): - self.doTrackFoldersHistory = True + self.doTrackRecentFolders = False natlinktimer.setTimerCallback(self.catchTimerRecentFolders, 0) self.dumpRecentFoldersDict() self.recentfoldersDict = {} self.emptyList('recentfolders') - print("track folders history is stopped, the timer callback is off") + print("the track recent folders timer is stopped, the recentfolder list is emptied") def resetRecentFolders(self): self.recentfoldersDict = {} @@ -791,7 +833,7 @@ def resetRecentFolders(self): # self.pickleChangingData.delete('recentfolders') # self.pickleChangingData.writeIfChanged() self.emptyList('recentfolders') - + def displayRecentFolders(self): """display the list of recent folders """ @@ -981,7 +1023,6 @@ def gotResults_subfolder(self, words, fullResults): if self.activeFolder and folderWord in self.subfoldersDict: subfolder = self.subfoldersDict[folderWord] folder = os.path.join(self.activeFolder, subfolder) - print('subfolder: %s'% folder) else: print('cannot find subfolder: %s'% folderWord) print('subfoldersDict: %s'% self.subfoldersDict) @@ -993,8 +1034,7 @@ def gotResults_subfolder(self, words, fullResults): # if no next rule, simply go: self.wantedFolder = folder self.Here = True - if doRecentFolderCommand: - self.manageRecentFolders(folderWord, folder) + self.manageRecentFolders(folderWord, folder) def gotResults_recentfolder(self,words,fullResults): """give list of recent folders and choose option @@ -2341,9 +2381,6 @@ def dumpToPickle(data, picklePath): """dump the data to picklePath """ # print("dumpToPickle %s, %s"% (picklePath, len(data))) - if not data: - os.remove(picklePath) - return try: with open(picklePath, 'wb') as pp: pickle.dump(data, pp) @@ -2374,13 +2411,16 @@ def unload(): natlink.natConnect() try: thisGrammar = ThisGrammar(inifile_stem="_folders") - thisGrammar.startInifile() + # thisGrammar.startInifile() thisGrammar.initialize() + # get hndle of a explore window (via _general "give window info") and try interactive + thisGrammar.catchTimerRecentFolders(132524, "CabinetWClass") Words = ['folder', 'dtactions'] Fr = {} thisGrammar.gotResultsInit(Words, Fr) thisGrammar.gotResults_folder(Words, Fr) finally: + thisGrammar.unload() natlink.natDisconnect() elif __name__.find('.') == -1: # standard startup when Dragon starts: diff --git a/src/unimacro/UnimacroGrammars/settings folders grammar.txt b/src/unimacro/UnimacroGrammars/settings folders grammar.txt index f5907c5..3dcfdca 100644 --- a/src/unimacro/UnimacroGrammars/settings folders grammar.txt +++ b/src/unimacro/UnimacroGrammars/settings folders grammar.txt @@ -14,15 +14,14 @@ newer settings febr 2024 [general] ignore file patterns = ~*; .* initial on = 1 -max files in folder = 40 -max subfolders in folder = 75 notify threshold milliseconds = 50 start this computer = HW start, My Computer start windows explorer = HW start, Windows explorer track file extensions = .py; .txt; .ini; .jpg; .jpeg; .png; .docx; .xlsx -track files at begin utterance = T +*****track files at utterance = 45 track files virtualdrives = -track folders at begin utterance = T -track folders history = 75 +track recent folder at utterance = 50 +*****track subfolders at utterance = 100 +max recent folders = 75 track folders virtualdrives = md; dr; pr; fb track recent folders timer interval = 4 diff --git a/src/unimacro/natlinkutilsbj.py b/src/unimacro/natlinkutilsbj.py index 3eff56b..9c3869d 100644 --- a/src/unimacro/natlinkutilsbj.py +++ b/src/unimacro/natlinkutilsbj.py @@ -483,7 +483,7 @@ def activate(self, ruleName, window=0, exclusive=None, noError=0): if exclusive is not None: self.setExclusive(exclusive) - def deactivate(self, ruleName, noError=0): + def deactivate(self, ruleName, noError=0, dpi16trick=True): self.__inherited.deactivate(self, ruleName, noError) # if not self.activeRules: # self.isActive = 0 @@ -1029,6 +1029,8 @@ def __init__(self, inifile_stem=None): if not hasattr(self, 'ini'): self.startInifile() self.name = self.checkName() + if self.ini is None: + print(f'Serious warning, grammar "{self.name}" has no valid ini file, please correct errors') try: self.iniIgnoreGrammarLists except AttributeError: @@ -1097,10 +1099,12 @@ def checkName(self): else: if not self.name is self.__class__.name: return self.name - - n = self.ini.get('grammar name', 'name') - if n: - return n + try: + n = self.ini.get('grammar name', 'name') + if n: + return n + except AttributeError: + pass try: n = self.name @@ -1108,9 +1112,10 @@ def checkName(self): n = self.__module__ n = n.replace('_', ' ') n = n.strip() - print(f'setting grammar name to: {n}') - self.ini.set('grammar name', 'name', n) - self.ini.write() + if self.ini: + print(f'setting grammar name to: {n}') + self.ini.set('grammar name', 'name', n) + self.ini.write() return n def hasCommon(self, one, two, allResults=None, withIndex=None): @@ -1173,6 +1178,9 @@ def translateGrammar(self, gramSpec): """ #pylint:disable=R0914, R0912, R0915, R1702 + if not self.ini: + return None + self.gramWords = {} # keys: new grammar words, values mappings to the old grammar words maybe empty if no translateWords translateWords = self.getDictOfGrammarWordsTranslations() # from current inifile if not translateWords: @@ -1813,7 +1821,8 @@ def fillGrammarLists(self, listOfLists=None): """ #pylint:disable=R0912 if not self.ini: - raise UnimacroError('no ini file active for grammar: %s'% self.GetName()) + print(f'--- no valid ini file for grammar: "{self.GetName()}", will not fill grammar lists\n\tPlease try to correct via "edit {self.getName()}"') + return ini = self.ini fromGrammar = copy.copy(self.validLists) allListsFromIni = ini.get() From 3ffd9e72586fa8283ce416a36c60de87bfded3f2 Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Mon, 19 Feb 2024 16:15:13 +0100 Subject: [PATCH 19/26] _folders automatic catching files and folders made working again, including the natlinktimer. improved the options and feedback when options are mistyped or missing or have been changed --- src/unimacro/UnimacroGrammars/_folders.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index 8c5903c..5ce0288 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -6,7 +6,7 @@ # grammar: _folders.py # Written by: Quintijn Hoogenboom (QH softwaretraining & advies) # starting 2003, revised QH march 2011 -# moved to the GitHub/Dictation-toolbox April 2020 +# moved to the GitHub/Dictation-toolbox April 2020, improved vastly Febr 2024 (with partly new options) #pylint:disable=C0302, W0613, W0702, R0911, R0912, R0913, R0914, R0915, W0212 #pylint:disable=E1101, C0209 r"""with this grammar, you can reach folders, files and websites from any window. @@ -649,6 +649,7 @@ def fillListsForActiveFolder(self, activeFolder): print(f'activeFolder, set {len(subfiles)} files') elif self.subfoldersDict: print(f'activeFolder, set {len(subfolders)} subfolders') + def emptyListsForActiveFolder(self): """no sublists, empty From 6b8867f7853c4c2e24fd3e1777bc40eb0ba3cb98 Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Tue, 20 Feb 2024 12:00:21 +0100 Subject: [PATCH 20/26] add number simple.ini to sample ini files... --- .../enx_inifiles/_number simple.ini | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/unimacro/sample_ini/enx_inifiles/_number simple.ini diff --git a/src/unimacro/sample_ini/enx_inifiles/_number simple.ini b/src/unimacro/sample_ini/enx_inifiles/_number simple.ini new file mode 100644 index 0000000..56f96aa --- /dev/null +++ b/src/unimacro/sample_ini/enx_inifiles/_number simple.ini @@ -0,0 +1,20 @@ +[general] +initial on = 1 + + +[grammar name] +name = number simple + + +[grammar words] +and = and +combination = combination +comma = comma +dot = dot +hundred = hundred +million = million +minus = minus +number = Number +point = point +thousand = thousand +through = Through From bd8e1b454c4cd29f1a04c4ac1bccc7c181cb399b Mon Sep 17 00:00:00 2001 From: Quintijn Hoogenboom Date: Fri, 23 Feb 2024 16:33:04 +0100 Subject: [PATCH 21/26] minor, commenting out print statements --- src/unimacro/UnimacroGrammars/_folders.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index 5ce0288..dc50fd0 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -769,7 +769,7 @@ def catchTimerRecentFolders(self, hndle=None, className=None): if self.recentfoldersDict and activeFolder == list(self.recentfoldersDict.values())[-1]: return spokenName = self.getFolderBasenameRemember(activeFolder) - print(f'add activeFolder "{activeFolder}" to recent, spoken name: "{spokenName}"') + # print(f'add activeFolder "{activeFolder}" to recent, spoken name: "{spokenName}"') self.manageRecentFolders(spokenName, activeFolder) finally: self.inTimerRecentFolders = False @@ -802,7 +802,7 @@ def manageRecentFolders(self, Spoken, Folder): self.recentfoldersDict[Spoken] = Folder self.dumpRecentFoldersDict() elif Folder not in self.foldersSet: - print('-- "recent [folder] %s": %s\nNote: "folder %s", points to: %s'% (Spoken, Folder, Spoken, spokenFolder)) + # print('-- "recent [folder] %s": %s\nNote: "folder %s", points to: %s'% (Spoken, Folder, Spoken, spokenFolder)) del self.recentfoldersDict[Spoken] self.recentfoldersDict[Spoken] = Folder self.dumpRecentFoldersDict() From c8219f615d83cc16086eb79d83ee0c61c038579a Mon Sep 17 00:00:00 2001 From: Doug Ransom Date: Sat, 2 Mar 2024 07:10:37 -0800 Subject: [PATCH 22/26] merging logging. --- pyproject.toml | 8 +- src/unimacro/UnimacroGrammars/_folders.py | 79 ++++---- src/unimacro/__init__.py | 27 +-- src/unimacro/_control.py | 180 +++++++++++++----- .../sample_ini/enx_inifiles/_brackets.ini | 31 --- .../sample_ini/enx_inifiles/_clickbyvoice.ini | 37 ---- .../sample_ini/enx_inifiles/_folders.ini | 120 ------------ .../sample_ini/enx_inifiles/_general.ini | 88 --------- .../sample_ini/enx_inifiles/_lines.ini | 77 -------- .../enx_inifiles/_number simple.ini | 20 -- .../sample_ini/enx_inifiles/_tasks.ini | 133 ------------- 11 files changed, 187 insertions(+), 613 deletions(-) delete mode 100644 src/unimacro/sample_ini/enx_inifiles/_brackets.ini delete mode 100644 src/unimacro/sample_ini/enx_inifiles/_clickbyvoice.ini delete mode 100644 src/unimacro/sample_ini/enx_inifiles/_folders.ini delete mode 100644 src/unimacro/sample_ini/enx_inifiles/_general.ini delete mode 100644 src/unimacro/sample_ini/enx_inifiles/_lines.ini delete mode 100644 src/unimacro/sample_ini/enx_inifiles/_number simple.ini delete mode 100644 src/unimacro/sample_ini/enx_inifiles/_tasks.ini diff --git a/pyproject.toml b/pyproject.toml index f6cdeba..469a18a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "flit_core.buildapi" name = "unimacro" authors = [{name = "Quintijn Hoogenboom (maintainer)", email = "q.hoogenboom@antenna.nl"}] dynamic = ["version", "description"] -requires-python = ">=3.9" +requires-python = ">=3.10" readme = "README.md" @@ -35,11 +35,15 @@ test = [ "pytest >=7.1.2","flake8" ] dev = [ - "pyenvutils","entry-point-inspector" + "pyenvutils","entry-point-inspector","build" ] [project.entry-points."natlink.grammars"] unimacro_builtins = "unimacro.UnimacroGrammars:locateme" +[project.entry-points."dt.loggers"] + unimacro ="unimacro:logname" + control="unimacro:control_logger_name" + [tool.pytest.ini_options] minversion = "7.1.2" addopts = "--capture=tee-sys " diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index dc50fd0..d701088 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -312,10 +312,11 @@ def fillInstanceVariables(self): # track recent folder at gotbegin or with timer: ## callback time in seconds: optionsdict['timer track folders interval'] = '' - interval = self.ini.getInt('general', 'timer track folders interval', 4) # default 4 sec + interval = self.ini.getInt('general', 'timer track folders interval', 0) # default 0 (off). + if interval and interval > 100: + print(f'_folders, warning, "timer track folders interval" should be set in seconds, not {interval}') + interval = 0 self.trackFoldersTimerInterval = int(interval*1000) # give in seconds - if self.trackFoldersTimerInterval: - print(f'track active folder as "recent" every {self.trackFoldersTimerInterval} milliseconds') self.recentfoldersDict = {} self.trackRecentFoldersAtUtterance = self.ini.getBool('general', 'track recent folders at utterance') @@ -458,7 +459,7 @@ def checkValidOptions(self, optionsdict, actualoptions): break unusedoptions = validoptions - actualoptions for unused in unusedoptions: - print(f'unset option for _folders: "{unused}",\n\tplease set (possibly without value), section [general]') + print(f'-- option "{unused}" is not set, grammar "_folders",\n\tplease set (possibly without value) in section [general]') def fillGrammarLists(self, listOfLists=None): """fills the lists of the grammar with data from inifile @@ -576,35 +577,15 @@ def getActiveFolder(self, hndle=None, className=None): if className == "CabinetWClass": f = mess.getFolderFromCabinetWClass(hndle) - # if f and f.startswith("search-ms"): - # keystroke("{esc}") - # unimacroutils.Wait() - # f = mess.getFolderFromDialog(hndle, className) - if not f: - print("getActiveFolder, CabinetWClass failed: %s"% hndle) elif className == '#32770': f = mess.getFolderFromDialog(hndle, className) - if not f: - return None - # if not f: - # print "getActiveFolder, #32770 failed: %s"% hndle - else: - # print 'class for activeFolder: %s'% className - return None if not f: - if className == 'CabinetWClass': - print('_folders, getActiveFolder, no folder found in className %s'% className) return None if os.path.isdir(f): nf = os.path.normpath(f) # print("getActiveFolder: %s"% nf) return nf - # print("folder in getActiveFolder: %s"% f) - realFolder = extenvvars.getFolderFromLibraryName(f) - if realFolder: - # print("getActiveFolder realFolder for %s: %s"% (f, realFolder)) - return realFolder - print('_folders, getActiveFolder, could not find folder for %s'% f) + print(f'getActiveFolder, strange invalid path for folder: "{f}"' ) return None def fillListsForActiveFolder(self, activeFolder): @@ -782,15 +763,17 @@ def manageRecentFolders(self, Spoken, Folder): """ # first see if the buffer needs to be shrinked: buffer = max(10, self.maxRecentFolders//10) - if len(self.recentfoldersDict) > self.maxRecentFolders + buffer: - print("shrink recentfoldersDict with %s items to %s"% (buffer, self.maxRecentFolders)) - while len(self.recentfoldersDict) >= self.maxRecentFolders: - keysList = list(self.recentfoldersDict.keys()) - _removeItem = self.recentfoldersDict.pop(keysList[0]) - # print('_folders, remove from recent folders: %s (%s)'% (keysList[0], removeItem)) - # print("refilling recentfolders list with %s items'"% len(self.recentfoldersDict)) - self.setList('recentfolders', list(self.recentfoldersDict.keys())) - self.dumpRecentFoldersDict() + print(f'manageRecentFolders buffer: {buffer}, self.maxRecentFolders: {self.maxRecentFolders}, len(recentfoldersDict): {len(self.recentfoldersDict)}') + if self.recentfoldersDict: + if len(self.recentfoldersDict) > self.maxRecentFolders + buffer: + print("shrink recentfoldersDict with %s items to %s"% (buffer, self.maxRecentFolders)) + while len(self.recentfoldersDict) >= self.maxRecentFolders: + keysList = list(self.recentfoldersDict.keys()) + _removeItem = self.recentfoldersDict.pop(keysList[0]) + # print('_folders, remove from recent folders: %s (%s)'% (keysList[0], removeItem)) + # print("refilling recentfolders list with %s items'"% len(self.recentfoldersDict)) + self.setList('recentfolders', list(self.recentfoldersDict.keys())) + self.dumpRecentFoldersDict() if not Spoken: return @@ -817,8 +800,13 @@ def manageRecentFolders(self, Spoken, Folder): def startRecentFolders(self): self.doTrackRecentFolders = True self.fillList('recentfolders') + timerInterval = self.trackFoldersTimerInterval + if timerInterval: + print(f'start timer interval {timerInterval} milliseconds') + else: + timerInterval = 1000 + print(f'start timer with interval {timerInterval} milliseconds, for this session only') natlinktimer.setTimerCallback(self.catchTimerRecentFolders, self.trackFoldersTimerInterval) # should have milliseconds - print("the track recent folders timer is started") def stopRecentFolders(self): self.doTrackRecentFolders = False @@ -826,7 +814,11 @@ def stopRecentFolders(self): self.dumpRecentFoldersDict() self.recentfoldersDict = {} self.emptyList('recentfolders') - print("the track recent folders timer is stopped, the recentfolder list is emptied") + if self.trackFoldersTimerInterval: + print("the track recent folders timer is stopped, for this session") + else: + print("the track recent folders timer is stopped.") + def resetRecentFolders(self): self.recentfoldersDict = {} @@ -2414,12 +2406,17 @@ def unload(): thisGrammar = ThisGrammar(inifile_stem="_folders") # thisGrammar.startInifile() thisGrammar.initialize() + # get hndle of a explore window (via _general "give window info") and try interactive - thisGrammar.catchTimerRecentFolders(132524, "CabinetWClass") - Words = ['folder', 'dtactions'] - Fr = {} - thisGrammar.gotResultsInit(Words, Fr) - thisGrammar.gotResults_folder(Words, Fr) + # thisGrammar.catchTimerRecentFolders(132524, "CabinetWClass") + thisGrammar.getActiveFolder(198518) + + + # # Words = ['folder', 'dtactions'] + # Fr = {} + # Words = ['subfolder', 'hello'] + # thisGrammar.gotResultsInit(Words, Fr) + # thisGrammar.gotResults_subfolder(Words, Fr) finally: thisGrammar.unload() natlink.natDisconnect() diff --git a/src/unimacro/__init__.py b/src/unimacro/__init__.py index 213d197..879c3a2 100644 --- a/src/unimacro/__init__.py +++ b/src/unimacro/__init__.py @@ -1,28 +1,21 @@ """Unimacro __init__ -utility functions, to get calling directory of module (in site-packages), -...and to check the existence of a directory, for example .natlink in the home directory. +Note there will be a global variable created in the unimacro module 'ulogger' which is Logging.Logger object named 'natlink.unimacro' +You can always access it by name. It is created in _control.py. -Note: -as user, having pipped the package, the scripts run from the site-packages directory - -as developer, you have to clone the package, then `build_package` and, - after a `pip uninstall unimacro`, `flit install --symlink`. - See instructions in the file README.md in the source directory of the package. - -get_site_packages_dir: can be called in the calling module like: - -``` -try: - from unimacro.__init__ import get_site_packages_dir -except ModuleNotFoundError: - print('Run this module after "build_package" and "flit install --symlink"\n') - -sitePackagesDir = get_site_packages_dir(__file__) -``` """ import os import sys +#these functions are in this module so that they can be loaded without loading a lot of unimacro code. +#they could be in a seperate .py file in unimacro to achieve the same (ie not in the control grammar). + +def control_logger_name() -> str : + return "natlink.unimacro.control" +def logname() -> str: + """ Returns the name of the unimacro logger.""" + return "natlink.unimacro" __version__ = '4.1.4.2' diff --git a/src/unimacro/_control.py b/src/unimacro/_control.py index c63b050..4c975c6 100644 --- a/src/unimacro/_control.py +++ b/src/unimacro/_control.py @@ -9,7 +9,7 @@ # _control.py, adapted version of_gramutils.py # Author: Bart Jan van Os, Version: 1.0, nov 1999 # starting new version Quintijn Hoogenboom, August 2003, for python3 2023 -#pylint:disable=C0115, C0116, W0702, R0904, R0911, R0912, R0914, R0915, W0201, W0613, W0107, C0209, E0601, W0602 +#pylint:disable=C0115, C0116, W0702, R0904, R0911, R0912, R0914, R0915, W0201, W0613, W0107, C0209, E0601, W0602, C0112 #pylint:disable=E1101 import os @@ -25,11 +25,39 @@ from dtactions.unimacro import unimacroactions as actions from unimacro import natlinkutilsbj as natbj -from unimacro import spokenforms +from unimacro import spokenforms +import importlib.metadata as meta +import sys +#from unimacro.logger import ulogger status = natlinkstatus.NatlinkStatus() natlinkmain = loader.NatlinkMain() +#a global logger for unimacro. perfectly reasonable to access by name instead. +import logging as l + +#for some reason, importing amodule which does this doesn't work. Likely because natlinkmain must be started first for +#this sublogger natlink.unimacro to work correctly. +import unimacro as unimacro_l #bring in so we can add a variable ulogger to the namespace. +ulogger : l.Logger = l.getLogger("natlink.unimacro") + +unimacro_l.__dict__['ulogger']=ulogger +ulogger.debug("natlink.unimacro logger available") + +#Loggers can be created for any module, and they can propogate to the parent Logger, or not. +#As an example, this module for the control grammar has its own child logger of unimacro. +#Note an entry point has to be defined as well, in pyproject.toml, so Loggers for various natlink components can be discovered. + + + + +control_logger=l.getLogger(unimacro_l.control_logger_name()) + + + + + + tracecount = list(map(str, list(range(1, 10)))) # #Constants for the UtilGrammar @@ -42,23 +70,45 @@ # # showAll = 1 # reset if no echo of exclusive commands is wished -#voicecodeHome = None -#if 'VCODE_HOME' in os.environ: -# voicecodeHome = os.environ['VCODE_HOME'] -# if os.path.isdir(voicecodeHome): -# for subFolder in ('Admin', 'Config', 'Mediator'): -# newFolder = os.path.join(voicecodeHome, subFolder) -# if os.path.isdir(newFolder) and newFolder not in sys.path: -# sys.path.append(newFolder) -# print 'appending to sys.path: %s'% newFolder -# else: -# print '_control: VCODE_HOME points NOT to a directory: %s'% voicecodeHome -# voicecodeHome = None - + +def natlink_loggers() ->dict: + """ + returns dictionary, keys are the names of the module to show to users (or for them to use in dication), + values are the string names of the loggers. + For example, {'unimacro':'natlink.unimacro'}. + Any python module/package/etc. can enable their own logger by defining an entry point in group 'natlink.loggers'. + The entry point must be a function that returns a logger name. Is the Python 'logging' module. + + """ + + + discovered_eps=meta.entry_points(group='dt.loggers') + ulogger.debug(f"Entry Points for natlink.loggers: {discovered_eps}") + loggers=dict() + for ep in discovered_eps: + try: + (name,_)=ep + module=ep.module + module_loaded=module in sys.modules + + ulogger.debug(f"Entry Point {ep} module: {module} is loaded: {module_loaded}. {'' if module_loaded else 'Not adding to list of available loggers.'} ") + + #only add the logger to the list of available loggers if the module is already loaded. + if module_loaded: + f=ep.load() + logname=f() + loggers[name]=logname + except Exception as e: + ulogger.error(f"Attempting to load EntryPoint {ep},error\n {e}") + return loggers ancestor = natbj.IniGrammar class UtilGrammar(ancestor): language = status.get_language() + + loggers=natlink_loggers() + ulogger.debug(f"Control: Available Loggers {loggers}") + loggers_names=sorted(loggers.keys()) iniIgnoreGrammarLists = ['gramnames', 'tracecount', 'message'] # are set in this module name = 'control' @@ -70,12 +120,13 @@ class UtilGrammar(ancestor): # if spokenforms: ## assume spokenforms is imported!!! specialList.append("'spoken forms'") + specialList.append("loggers") if specialList: specials = "|" + '|'.join(specialList) else: specials = "" - gramRules = ['show', 'edit', 'switch', 'showexclusive', 'resetexclusive', 'checkalphabet', 'message'] + gramRules = ['show', 'edit', 'switch', 'showexclusive', 'resetexclusive', 'checkalphabet', 'message','setlogging','loglevel'] gramDict = {} gramDict['show'] = """ exported = show ((all|active) grammars | {gramnames} | (grammar|inifile) {gramnames} @@ -87,6 +138,9 @@ class UtilGrammar(ancestor): gramDict['resetexclusive'] = """ exported = reset (exclusive | exclusive grammars);""" gramDict['checkalphabet'] = """ exported = check alphabet;""" gramDict['message'] = """ exported = {message};""" + gramDict['setlogging'] = """ exported = {logmodulename} loglevel ;""" + gramDict['loglevel'] = " = (debug|info|warning|error|critical);" + gramSpec = [] assert set(gramRules) == set(gramDict.keys()) @@ -98,7 +152,8 @@ class UtilGrammar(ancestor): gramSpec.append(gramDict[rulename]) - ## extra: the trace rule: + ## extra: the trace rule: + ## TODO QH, switchoff for the moment, febr24 if specials: specials2 = specials[1:] # remove initial "|" (at show it is "| actions | 'spoken forms'", here it is # "actions | 'spoken forms'" only, because gramnames etc are not implemented @@ -106,8 +161,8 @@ class UtilGrammar(ancestor): traceSpecial = """ exported = trace (("""+ specials2 +""") | ((on|off| {tracecount})("""+ specials2 +""")) | (("""+ specials2 +""") (on|off|{tracecount}))) ;""" - gramSpec.append(traceSpecial) # add trace for actions of spoken forms (latter not implemented) - + # gramSpec.append(traceSpecial) # add trace for actions of spoken forms (latter not implemented) + ulogger.info('check, correct _control?????') Mode = Normal LastMode = Normal @@ -118,12 +173,14 @@ def initialize(self): # temp set allResults to 0, disabling the messages trick: if not self.load(self.gramSpec, allResults=showAll): return + self.setList('logmodulename',self.loggers_names) self.RegisterControlObject(self) self.emptyList('message') # at post load # allGramNames = self.getUnimacroGrammarNames() # self.setList('gramnames', allGramNames) - self.setNumbersList('tracecount', tracecount) + ## TODO QH switch on again if tracing is activated again + # self.setNumbersList('tracecount', tracecount) self.activateAll() self.setMode(Normal) @@ -131,7 +188,7 @@ def initialize(self): ## if unimacroutils.getUser() == 'martijn': ## print 'martijn, set exclusive %s'% self.name ## self.setExclusive(1) - print('---now starting other Unimacro grammars:') + control_logger.info('---now starting other Unimacro grammars:') def unload(self): @@ -183,14 +240,14 @@ def restoreMode(self): def gotResults_checkalphabet(self,words,fullResults): """check the exact spoken versions of the alphabet in spokenforms """ - version = unimacroutils.getDNSVersion() + version = status.getDNSVersion() _spok = spokenforms.SpokenForms(self.language, version) alph = 'alphabet' ini = spokenforms.ini for letter in string.ascii_lowercase: spoken = ini.get(alph, letter, '') if not spoken: - print('fill in in "%s_spokenform.ini", [alphabet] spoken for: "%s"'% (self.language, letter)) + control_logger.info('fill in in "%s_spokenform.ini", [alphabet] spoken for: "%s"'% (self.language, letter)) continue if version < 11: normalform = '%s\\%s'% (letter.upper(), spoken) @@ -199,7 +256,7 @@ def gotResults_checkalphabet(self,words,fullResults): try: natlink.recognitionMimic([normalform]) except natlink.MimicFailed: - print('invalid spoken form "%s" for "%s"'% (spoken, letter)) + control_logger.info('invalid spoken form "%s" for "%s"'% (spoken, letter)) if spoken == spoken.lower(): spoken = spoken.capitalize() trying = 'try capitalized variant' @@ -215,15 +272,15 @@ def gotResults_checkalphabet(self,words,fullResults): try: natlink.recognitionMimic([normalform]) except natlink.MimicFailed: - print('%s fails also: "%s" for "%s"'% (trying, spoken, letter)) + control_logger.info('%s fails also: "%s" for "%s"'% (trying, spoken, letter)) else: - print('alphabet section is corrected with: "%s = %s"'% (letter, spoken)) + control_logger.info('alphabet section is corrected with: "%s = %s"'% (letter, spoken)) ini.set(alph, letter, spoken) ini.writeIfChanged() def gotResults_trace(self,words,fullResults): - print('control, trace: %s'% words) + control_logger.info('control, trace: %s'% words) traceNumList = self.getNumbersFromSpoken(words) # returns a string or None if traceNumList: traceNum = int(traceNumList[0]) @@ -242,7 +299,7 @@ def gotResults_trace(self,words,fullResults): else: actions.debugActions(1) elif self.hasCommon(words, 'spoken forms'): - print("no tracing possible for spoken forms") + control_logger.info("no tracing possible for spoken forms") #def gotResults_voicecode(self,words,fullResults): # """switch on if requirements are fulfilled @@ -286,7 +343,7 @@ def gotResults_switch(self,words,fullResults): self.switch(gram, gname, switchOn) # self never needs switching on else: - print('_control switch, no valid grammar found, command: %s'% words) + control_logger.info('_control switch, no valid grammar found, command: %s'% words) def switch(self, gram, gname, switchOn): """switch on or off grammar, and set in inifile, @@ -295,7 +352,7 @@ def switch(self, gram, gname, switchOn): switchOn is True or False """ if gram == self: - print(f'should not be here, do not switch on of off _control {gram}') + control_logger.error(f'should not be here, do not switch on of off _control {gram}') return None if switchOn: if gram.ini: @@ -304,16 +361,16 @@ def switch(self, gram, gname, switchOn): gram.ini.write() unimacroutils.Wait(0.1) else: - print(f'--- ini file of grammar {gname} is invalid, please try "edit {gname}"...') + control_logger.error(f'--- ini file of grammar {gname} is invalid, please try "edit {gname}"...') gramName = gram.getName() unimacro_grammars_paths = self.getUnimacroGrammarNamesPaths() try: filepath = Path(unimacro_grammars_paths[gramName]) except KeyError: - print(f'_control, grammar not in unimacro_grammars_paths dict: {gramName}, cannot switchOn') + control_logger.error(f'_control, grammar not in unimacro_grammars_paths dict: {gramName}, cannot switchOn') return None # now reload with force option. - print(f'_control, now reload grammar "{gramName}":') + control_logger.info(f'_control, now reload grammar "{gramName}":') natlinkmain.seen.clear() natlinkmain.load_or_reload_module(filepath, force_load=True) @@ -325,9 +382,35 @@ def switch(self, gram, gname, switchOn): gram.cancelMode() gram.deactivateAll() # gram.unload() - print('grammar "%s" switched off'% gram.getName()) + control_logger.info('grammar "%s" switched off'% gram.getName()) return 1 - + + def gotResults_setlogging(self,words, fullresults): + """ + """ + control_logger.debug(f"unimacro logger gotResults_logging_level words: {words} fullResults: {fullresults}") + + loglevel_for = words[0] # something like natlink, unimacro,... + new_level_str_mc,_=fullresults[-1] + new_log_level_str=new_level_str_mc.upper() + #the string should be in the + logger_name=self.loggers[loglevel_for] + new_log_level=l.__dict__[new_log_level_str] + + control_logger.debug(f"New Log Level {new_log_level_str} for logger {logger_name}") + logger=l.getLogger(logger_name) + logger.setLevel(new_log_level) + + + + +#Hide numbers def gotResults_loglevel(self,words,fullresults): + """ + """ + control_logger.debug(f"gotResults_logging_level words: {words} fullResults: {fullresults}") + return + + def gotResults_showexclusive(self,words,fullResults): All = 0 @@ -337,7 +420,7 @@ def gotResults_showexclusive(self,words,fullResults): else: Start=() # fix state at this moment (in case of Active grammars popup) - print(f'_control, showexclusive, exclusiveGrammars: {natbj.exclusiveGrammars}') + control_logger.info(f'_control, showexclusive, exclusiveGrammars: {natbj.exclusiveGrammars}') if natbj.exclusiveGrammars: Exclusive = 1 self.BrowsePrepare(Start, All, Exclusive) @@ -361,7 +444,7 @@ def gotResults_showexclusive(self,words,fullResults): def gotResults_resetexclusive(self,words,fullResults): - print('reset exclusive') + control_logger.info('reset exclusive') exclGrammars = natbj.getExclusiveGrammars() if exclGrammars: T = ['exclusive grammars:'] @@ -400,14 +483,17 @@ def gotResults_show(self,words,fullResults): if self.hasCommon(words, 'exclusive'): G = self.getExclusiveGrammars() exclNames = [gname for gname, gram in G.items() if gram.isExclusive()] - print(f'exclusive grammars (+ control) are: {exclNames}') + control_logger.info(f'exclusive grammars (+ control) are: {exclNames}') self.gotResults_showexclusive(words, fullResults) return + if self.hasCommon(words,"loggers"): + control_logger.info(f"Available Loggers: {self.loggers}") + return grammars = self.getUnimacroGrammars() gramNames = list(grammars.keys()) - # print(f'_control, gramNames: {gramNames}') + control_logger.debug(f'_control, gramNames: {gramNames}') gramName = self.hasCommon(words, gramNames) if gramName: grammar = grammars[gramName] @@ -452,9 +538,9 @@ def gotResults_show(self,words,fullResults): activeGrammars = [g for g in G if G[g].isActive()] inactiveGrammars = [g for g in G if G[g].isLoaded() and not G[g].isActive()] switchedOffGrammars = [g for g in G if not G[g].isLoaded()] - print(f'activeGrammars: {activeGrammars}') - print(f'inactiveGrammars: {inactiveGrammars}') - print(f'switchedOffGrammars: {switchedOffGrammars}') + control_logger.info(f'activeGrammars: {activeGrammars}') + control_logger.info(f'inactiveGrammars: {inactiveGrammars}') + control_logger.info(f'switchedOffGrammars: {switchedOffGrammars}') # for grammar_name, gram in G.items(): # print(f'grammar_name: {grammar_name}, gram: {gram}') @@ -525,7 +611,7 @@ def gotResults_edit(self,words,fullResults): try: grammar = grammars[gramName] except KeyError: - print(f'grammar {words[-1:]} not found in list of gramNames:\n{gramNames}') + control_logger.error(f'grammar {words[-1:]} not found in list of gramNames:\n{gramNames}') return # print(f'grammar: {gramName}: {grammar}') if self.hasCommon(words, 'grammar'): @@ -534,9 +620,9 @@ def gotResults_edit(self,words,fullResults): try: filepath = unimacro_grammars_paths[gramName] except KeyError: - print(f'grammar not in unimacro_grammars_paths dict: {gramName}') + control_logger.error(f'grammar not in unimacro_grammars_paths dict: {gramName}') return - print(f'open for edit file: "{filepath}"') + control_logger.info(f'open for edit file: "{filepath}"') self.openFileDefault(filepath, mode="edit", name=f'edit grammar {gramName}') else: # edit the inifile @@ -550,7 +636,7 @@ def switchOff(self, **kw): """overload, this grammar never switches off """ - print('remains switched on: %s' % self) + control_logger.info('remains switched on: %s' % self) def switchOn(self, **kw): """overload, just switch on @@ -610,7 +696,7 @@ def getUnimacroGrammarNamesPaths(self): unimacro_modules[name] = try_file break else: - print(f'not found in natlink_modules_files: {name}') + control_logger.info(f'not found in natlink_modules_files: {name}') unimacro_modules[name] = name # not found return unimacro_modules diff --git a/src/unimacro/sample_ini/enx_inifiles/_brackets.ini b/src/unimacro/sample_ini/enx_inifiles/_brackets.ini deleted file mode 100644 index 53589f1..0000000 --- a/src/unimacro/sample_ini/enx_inifiles/_brackets.ini +++ /dev/null @@ -1,31 +0,0 @@ -[brackets] -angle brackets = <> -asteriscs = ** -braces = {} -brackets = () -colons = :: -double angle brackets = <<>> -double quotes = '""' -double underscores = ____ -index = [] -parens = () -parenthesis = () -quotes = '""' -single quotes = "''" -square brackets = [] -triple quotes = '""""""' -underscores = __ -vertical bars = || -HTML square brackets = [|] -HTML angle brackets = <|> - -[general] -initial on = 1 - - -[grammar name] -name = brackets - - -[grammar words] -between = between diff --git a/src/unimacro/sample_ini/enx_inifiles/_clickbyvoice.ini b/src/unimacro/sample_ini/enx_inifiles/_clickbyvoice.ini deleted file mode 100644 index 1f24787..0000000 --- a/src/unimacro/sample_ini/enx_inifiles/_clickbyvoice.ini +++ /dev/null @@ -1,37 +0,0 @@ -[additionalonoroff] -on = + -contrast = c -hibrid = h -overlay = o -off = - - - -[general] -initial on = 1 -show numbers = :+h - -[grammar name] -name = clickbyvoice - - -[grammar words] -pick = click | pick - -[navigateoptions] -drop down = f;{alt+down} -focus = f -hover = h -copy content = s -copy link = k -link copy = k -new doc = t -new document = t -new tab = t -new tab stay = b -new window = w -quick menu = f;;;;{shift+f10} -shortcut menu = f;;;;{shift+f10} - - -[pagecommands] -refresh = {f5} diff --git a/src/unimacro/sample_ini/enx_inifiles/_folders.ini b/src/unimacro/sample_ini/enx_inifiles/_folders.ini deleted file mode 100644 index e4d45ab..0000000 --- a/src/unimacro/sample_ini/enx_inifiles/_folders.ini +++ /dev/null @@ -1,120 +0,0 @@ -[extensions] -pdf = pdf -doc X. = docx -C. S. S. = css -H. T. M. L. = html -T. X. T. = txt -doc = doc -ini = ini - -[filecommands] -close = <> -copy = copy -edit = edit -maximise = <> -minimise = <> -paste = paste -print = <> -restore = <> - - -[fileopenprograms] -edit = edit -emacs = emacs -notepad = notepad -word = winword - -[files] - - -[foldercommands] -close = <> -copy = copy -explorer = explorer -maximize = <> -minimize = <> -new = new -paste = paste -restore = <> - -[folders] -documents = ~ - - -[general] -automatic track files = F -automatic track folders = F -child behaves like top = natspeak: dragonbar, dictation box -ignore file patterns = ~*; .* -track file extensions = .py; .doc; .xls; .txt; .ini; .docx; .xlsx -track files virtualdrives = -track folders virtualdrives = - -[grammar name] -name = folders - - -[grammar words] -drive = drive -edit = edit -environment = environment -explorer = explorer -file = file -folder = folder -folder up = folder up -folders = folders -input = input -local = local -maximise = maximise -new = new -on = on -open with = open with -set = set -site = site -this = this -website = website - - -[letters] -Quebec = q -alpha = a -charlie = c -delta = d -echo = e -foxtrot = f -golf = g -hotel = h -india = i -juliet = j -kilo = k - -[subversionfilecommands] -add = add -commit = commit -update = update -log = log - - -[subversionfoldercommands] -add = add -commit = commit -export = export -update = update -log = log - -[virtualdrives] -md = %HOME% -pf = %PROGRAMFILES% - - -[websiteopenprograms] -chrome = chrome -firefox = firefox -internet explorer = iexplore -explorer = iexplore - - -[websites] -sitegen = sitegen.nl -unimacro = http://qh.antenna.nl/unimacro -speech computing forum = http://www.speechcomputing.com/tracker \ No newline at end of file diff --git a/src/unimacro/sample_ini/enx_inifiles/_general.ini b/src/unimacro/sample_ini/enx_inifiles/_general.ini deleted file mode 100644 index 8df4936..0000000 --- a/src/unimacro/sample_ini/enx_inifiles/_general.ini +++ /dev/null @@ -1,88 +0,0 @@ -[browsers] -Chrome = chrome -Firefox = firefox -Google Chrome = chrome -Internet Explorer = iexplore -Netscape = netscape -Opera = opera - -unimacro grammars = - _general; _oops; sol; _folders - _brackets; _commands; _editcomments - _number;_repeat;_tasks; firefox_browsing - _keystrokes; _lines; - -unimacro modules = natlinkutilsqh; natlinkutilsbj; actions; BrowseGrammar - -[general] -initial on = 1 - - -[grammar name] -name = general - - -[grammar words] -after = after -back = back -batch = batch -before = before -browse with = browse with -command = Command -comment = Comment -do = do -documentation = documentation -down = down -extend = extend -for = for -forward = forward -give = Give -go back = go back -here = Here -hyphenate = Hyphenate -info = info -information = information -insert = insert -last = last -make = Make -method = Method -mode = mode -mouse = Mouse -name = Name -natlink = Natlink -new = new -path = path -phrase = Phrase -redo = Redo -release = Release -reload = reload -search = search -test = test -that = That -times = times -trick = trick -undo = Undo -unimacro = unimacro -up = up -user = user -variable = Variable -window = window -with = with -without = without -word = word -words = words - -[name phrase] -inbetweenwords = van; de; the -secondchristiannames = louise - - -[searchwords] -class = "class " -function = "def " -section = "[" - - -[spoken forms] -kcs = kay see es -ksc = kay essee \ No newline at end of file diff --git a/src/unimacro/sample_ini/enx_inifiles/_lines.ini b/src/unimacro/sample_ini/enx_inifiles/_lines.ini deleted file mode 100644 index 48fcace..0000000 --- a/src/unimacro/sample_ini/enx_inifiles/_lines.ini +++ /dev/null @@ -1,77 +0,0 @@ -[general] -deactivate = - sol - natspeak: messages, correct - -ignore = - empty - natspeak: mouse grid - - -initial on = 1 - -[grammar name] -name = lines - -[grammar words] -and = and -copy = copy -down = down -here = Here -hundred = hundred -left = left -line = line -line base = line base -lines = lines -million = million -move = move -next = next -number = number -off = off -plus = plus -previous = previous -right = right -that = THAT -these = these -this = this -thousand = thousand -through = through -to = to -up = up -word = word -words = words - - -[simpleaction] -collapse = {numkey-} -comment = <> -copy = <> -copy to DragonPad = HW copy that to DragonPad -cut = <> -cut to DragonPad = HW cut that to DragonPad -delete = <> -duplicate = <> -edit = HW Edit that -edit comment = HW Edit, Comment -edit docstring = HW Edit, Doc String -emacs = <> -end = <> -expand = {numkey+} -indent = <> -indent too = <><> -insert = <> -paste = <><> -paste over = <> -select = -uncomment = <> -unindent = <> -unindent too = <><> - - -[wordaction] -copy = <> -cut = <> -delete = <> -paste = <> -select = -self dot paste = self.<> diff --git a/src/unimacro/sample_ini/enx_inifiles/_number simple.ini b/src/unimacro/sample_ini/enx_inifiles/_number simple.ini deleted file mode 100644 index 56f96aa..0000000 --- a/src/unimacro/sample_ini/enx_inifiles/_number simple.ini +++ /dev/null @@ -1,20 +0,0 @@ -[general] -initial on = 1 - - -[grammar name] -name = number simple - - -[grammar words] -and = and -combination = combination -comma = comma -dot = dot -hundred = hundred -million = million -minus = minus -number = Number -point = point -thousand = thousand -through = Through diff --git a/src/unimacro/sample_ini/enx_inifiles/_tasks.ini b/src/unimacro/sample_ini/enx_inifiles/_tasks.ini deleted file mode 100644 index b33257e..0000000 --- a/src/unimacro/sample_ini/enx_inifiles/_tasks.ini +++ /dev/null @@ -1,133 +0,0 @@ -[application] -calc = calc -calculator = calc -command = cmd -dragonpad = dragonpad -edit = edit -emacs = emacs -email = outlook -excel = excel -firefox = firefox -idle = idle -internet = iexplore -messages = messages -notepad = notepad -pythonwin = pythonwin -voice code = voicecode -voice coder = voicecode -word = winword - - -[directionplusreverse] -center = center; middle - -centerdown = - centerdown; downcenter; centerbottom; bottomcenter; - middledown; downmiddle; middlebottom; bottommiddle; - - -centerup = centerup; upcenter; centertop; topcenter; middleup; middletop; upmiddle; topmiddle - -down = down; bottom -left = left -leftcenter = leftcenter; centerleft; leftmiddle; middleleft -leftdown = leftdown; leftbottom; bottomleft; downleft -leftup = leftup; upleft; lefttop; topleft -right = -rightcenter = rightcenter; centerright; rightmiddle; middleright -rightdown = rightdown; rightbottom; downright; bottomright -rightup = righttop; rightup; upright; topright -up = up; top - - -[directionreverse] -down = down -left = left -right = -up = up; top - - -[general] -center mouse = T -enable search commands = F -do taskswitch with windows key = F -initial on = 1 -max icon number = 15 -max task number = 30 -max window number = 9 -split left right = 0.5 -split top down = 0.5 -switchapps = pythonwin; emacs; dragonpad; edit; idle - - -[grammar name] -name = tasks - - -[grammar words] -back = Back -centimeters = centimeters -clock = clock -convert = convert -degrees = degrees -dos = dos -file = file -get task position = get task position -give = give -here = Here -icon = icon -inches = inches -info = info -menu = menu -millimeters = millimeters -move = move -next = Next -percent = percent -pixels = pixels -position = position -previous = Previous -remove cursor = remove cursor -resize = resize -search = search -shrink = shrink -start = start -stretch = stretch -switch file to = switch file to -task = task -to = to -unix = unix -window = Window -windows = windows - - -[iconaction] -OK = {enter} -shortcut menu = {shift+f10} -space = {space} - - -[startmenucommands] -all programs = {extup}{extright} -close = SSK {esc} - -[firstlast] -before last = -2 -fifth = 5 -first = 1 -fourth = 4 -last = -1 -second = 2 -third = 3 - -[taskaction] -close = <> -kill = KW -maximise = <> -minimise = <> -mouse = -other display = <> -refresh = <> -resize = <> -restore = <> -test = <>;<>; <> -tile = RW;TOCLOCK right; h; RTW From 699384348fd27b707d35568d2e3c8d1e78a20560 Mon Sep 17 00:00:00 2001 From: Doug Ransom Date: Sun, 3 Mar 2024 10:28:51 -0800 Subject: [PATCH 23/26] fixed some errors switching to logging. --- pyproject.toml | 1 + src/unimacro/UnimacroGrammars/_folders.py | 388 ++++++++++++---------- src/unimacro/__init__.py | 4 + src/unimacro/_control.py | 79 +++-- src/unimacro/natlinkutilsbj.py | 49 ++- 5 files changed, 294 insertions(+), 227 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 469a18a..f282a2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ unimacro_builtins = "unimacro.UnimacroGrammars:locateme" [project.entry-points."dt.loggers"] unimacro ="unimacro:logname" control="unimacro:control_logger_name" + folders="unimacro:folders_logger_name" [tool.pytest.ini_options] minversion = "7.1.2" diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index d701088..adef8d1 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -54,6 +54,8 @@ import win32gui from win32com.client import Dispatch import win32clipboard +from logging import Logger,getLogger +from io import StringIO import natlink from natlinkcore import readwritefile @@ -97,6 +99,25 @@ # note: title is converted to lowercase, only full title is recognised ancestor = natbj.IniGrammar + +#note this is basically copy & pasted into ThisGrammar +#some global scope functions need the same logger. +def logger_name(): + "natlink.unimacro.folders" + +logger = getLogger(logger_name()) + +#logger should be used instead of print +#replace print to avoid unintended use. +builtin_print=print +def our_print(*args,**kwargs): + f=StringIO() + builtin_print(args,kwargs,file=f) + value=f.getvalue() + logger.debug("print called instead of logging functions: %s" , value) + logger.error(value) + + class ThisGrammar(ancestor): """grammar for quickly going to folders, files and websites """ @@ -152,11 +173,19 @@ def initialize(self): self.subfilesDict = {} self.foldersSet = set() if not self.language: - print("no valid language in grammar "+__name__+" grammar not initialized") + self.error("no valid language in grammar "+__name__+" grammar not initialized") return self.load(self.gramSpec) self.switchOnOrOff() # initialises lists from inifile, and switches on + def loggerName(self) ->str: + """Returns the name of a logger. Replace this and loggerShortName to create a logger for an inherited grammar. """ + return "natlink.unimacro.folders" + + def loggerShortName(self) ->str: + """A key for use as a spoken form or user interface item. """ + return "folders" + def gotBegin(self,moduleInfo): if self.checkForChanges: self.checkInifile() # refills grammar lists and instance variables @@ -165,7 +194,7 @@ def gotBegin(self,moduleInfo): self.checkForChanges -= 1 if self.mayBeSwitchedOn == 'exclusive': - print("exclusive (_folders), do switchOnOrOff") + self.info("exclusive (_folders), do switchOnOrOff") self.switchOnOrOff() self.progInfo = unimacroutils.getProgInfo() @@ -182,7 +211,7 @@ def gotBegin(self,moduleInfo): def gotResultsInit(self,words,fullResults): if self.mayBeSwitchedOn == 'exclusive': - print('recog folders, switch off mic') + self.info('recog folders, switch off mic') natbj.SetMic('off') self.wantedFolder = self.wantedFile = self.wantedWebsite = None self.catchRemember = None @@ -210,7 +239,7 @@ def handleTrackFilesAndFolders(self, activeFolder): if self.activeFolder: self.emptyListsForActiveFolder() - # print 'empty lists for active folder %s, now: %s'% (self.activeFolder, activeFolder) + self.debug('empty lists for active folder %s, now: %s',self.activeFolder, activeFolder) self.activeFolder = None if activeFolder and os.path.isdir(activeFolder): @@ -234,14 +263,14 @@ def fillList(self, listName): # print("foldersSet: %s"% self.foldersSet) self.setList('folders', items) return items - print('no folders to set list to') + self.info('no folders to set list to') self.emptyList('folders') elif listName == 'subfolders': if self.subfoldersDict: items = list(self.subfoldersDict.keys()) self.setList('subfolders', items) return items - print('no subfolders to set list to') + self.info('no subfolders to set list to') self.emptyList('subfolders') elif listName == 'files': @@ -266,7 +295,7 @@ def dumpRecentFoldersDict(self): if self.pickleChangingData: dumpToPickle(self.recentfoldersDict, self.pickleChangingData) else: - print('dumpRecentFoldersDict, no self.pickleChangingData') + self.info('dumpRecentFoldersDict, no self.pickleChangingData') def loadRecentFoldersDict(self): """for getting the dict of recent folders from previous session @@ -276,7 +305,7 @@ def loadRecentFoldersDict(self): if result and isinstance(result, dict): self.recentfoldersDict = result return - print('no recent folders dict') + self.info('no recent folders dict') self.recentfoldersDict = {} def fillInstanceVariables(self): @@ -296,11 +325,10 @@ def fillInstanceVariables(self): optionsdict['use other explorer'] = '' if self.useOtherExplorer: if os.path.isfile(self.useOtherExplorer): - print('_folders, use as default explorer: "%s"'% self.useOtherExplorer) + self.info('_folders, use as default explorer: "%s"', self.useOtherExplorer) else: - print('_folders, variable "use other explorer" set to: "%s" (use data from "actions.ini")'% self.useOtherExplorer) - - + self.info('_folders, variable "use other explorer" set to: "%s" (use data from "actions.ini")' , self.useOtherExplorer) + # these are for automatic tracking the current folder at an utterance: optionsdict['track files at utterance'] = 'automatic track files' optionsdict['track subfolders at utterance'] = 'automatic track folders' @@ -314,7 +342,7 @@ def fillInstanceVariables(self): optionsdict['timer track folders interval'] = '' interval = self.ini.getInt('general', 'timer track folders interval', 0) # default 0 (off). if interval and interval > 100: - print(f'_folders, warning, "timer track folders interval" should be set in seconds, not {interval}') + self.warning(f'_folders, warning, "timer track folders interval" should be set in seconds, not {interval}') interval = 0 self.trackFoldersTimerInterval = int(interval*1000) # give in seconds self.recentfoldersDict = {} @@ -332,11 +360,12 @@ def fillInstanceVariables(self): intervalSeconds = int(self.trackFoldersTimerInterval/1000) if self.trackFoldersTimerInterval or self.trackRecentFoldersAtUtterance: if not self.trackFoldersTimerInterval: - print(f'maintain a list of (max) {self.maxRecentFolders} recent folders (Explorer or File Dialog) at every utterance') + track_message=f'maintain a list of (max) {self.maxRecentFolders} recent folders (Explorer or File Dialog) at every utterance' elif not self.trackRecentFoldersAtUtterance: - print(f'maintain a list of (max) {self.maxRecentFolders} recent folders (Explorer or File Dialog) every {intervalSeconds} seconds') + track_message=f'maintain a list of (max) {self.maxRecentFolders} recent folders (Explorer or File Dialog) every {intervalSeconds} seconds' else: - print(f'maintain a list of (max) {self.maxRecentFolders} recent folders (Explorer or File Dialog) at every utterance and every {intervalSeconds} seconds') + track_message=f'maintain a list of (max) {self.maxRecentFolders} recent folders (Explorer or File Dialog) at every utterance and every {intervalSeconds} seconds' + self.info(track_message) if self.trackFoldersTimerInterval: natlinktimer.setTimerCallback(self.catchTimerRecentFolders, self.trackFoldersTimerInterval) # every 5 seconds default... else: @@ -359,7 +388,7 @@ def fillInstanceVariables(self): raw_folder = self.ini.get('folders', f) folder = self.substituteFolder(raw_folder) if not os.path.isdir(folder): - print(f'warning _folders, folder "{f}" does not exist: "{folder}"') + self.warning(f'warning _folders, folder "{f}" does not exist: "{folder}"') # self.ini.delete('folders', f) # self.ini.set('obsolete folders', f, folder) continue @@ -387,7 +416,7 @@ def fillInstanceVariables(self): trf2 = self.substituteFolder(trf) #print 'input: %s, output: %s'% (trf, trf2) if not os.path.isdir(trf2): - print('warning, no valid folder associated with: %s (%s) (skip for track virtualdrives)'% (trf, trf2)) + self.warning('warning, no valid folder associated with: %s (%s) (skip for track virtualdrives)', trf, trf2) continue #else: # print 'valid folder for tracking: %s (%s)'% (trf, trf2) @@ -414,7 +443,7 @@ def fillInstanceVariables(self): for f in filesList[:]: filename = self.substituteFilename(self.ini.get('files', f)) if not os.path.isfile(filename): - print(f'warning _folders, file "{f}" does not exist: "{filename}"') + self.warning(f'warning _folders, file "{f}" does not exist: "{filename}"') # self.ini.delete('files', f) # self.ini.set('obsolete files', f, filename) continue @@ -425,7 +454,7 @@ def fillInstanceVariables(self): continue trf2 = self.substituteFolder(trf) if not os.path.isdir(trf2): - print('warning, no valid folder associated with: %s (%s) (skip for track files)'% (trf, trf2)) + self.warning('warning, no valid folder associated with: %s (%s) (skip for track files)', trf, trf2) continue filesList = [f for f in os.listdir(trf2) if os.path.isfile(os.path.join(trf2, f))] self.trackFilesSection = 'files %s'% trf @@ -450,16 +479,16 @@ def checkValidOptions(self, optionsdict, actualoptions): validoptions = set(optionsdict) oldoptions = actualoptions - validoptions if oldoptions: - print(f'obsolete options: {oldoptions}') + self.info(f'obsolete options: {oldoptions}') # give new option, if available: for old in oldoptions: for k,v in optionsdict.items(): if v == old: - print(f'replace option "{old}" into "{k}" please') + self.info(f'replace option "{old}" into "{k}" please') break unusedoptions = validoptions - actualoptions for unused in unusedoptions: - print(f'-- option "{unused}" is not set, grammar "_folders",\n\tplease set (possibly without value) in section [general]') + self.warning(f'-- option "{unused}" is not set, grammar "_folders",\n\tplease set (possibly without value) in section [general]') def fillGrammarLists(self, listOfLists=None): """fills the lists of the grammar with data from inifile @@ -504,7 +533,7 @@ def resolveVirtualDrives(self, wantedVirtualDrives): if wantedVirtualDrives: textline = ", ".join(wantedVirtualDrives) - print(f'Warning: could not resolve "virtualdrive" entries: {textline}, ignore') + self.warning(f'Warning: could not resolve "virtualdrive" entries: {textline}, ignore') # for dr in wantedVirtualDrives: # virtualDrive = self.ini.get('virtualdrives', dr) # self.ini.delete('virtualdrives', dr) @@ -570,7 +599,7 @@ def getActiveFolder(self, hndle=None, className=None): return None if className is None: className = win32gui.GetClassName(hndle) - # print 'getActiveFolder, className: %s'% className + self.debug('getActiveFolder, className: %s', className) if not className: return None f = None @@ -583,9 +612,9 @@ def getActiveFolder(self, hndle=None, className=None): return None if os.path.isdir(f): nf = os.path.normpath(f) - # print("getActiveFolder: %s"% nf) + self.debug("getActiveFolder: %s",nf) return nf - print(f'getActiveFolder, strange invalid path for folder: "{f}"' ) + self.warning(f'getActiveFolder, strange invalid path for folder: "{f}"' ) return None def fillListsForActiveFolder(self, activeFolder): @@ -602,7 +631,7 @@ def fillListsForActiveFolder(self, activeFolder): subfiles = [s for s in subs if os.path.isfile(os.path.join(activeFolder, s))] if self.trackFilesAtUtterance: if len(subfiles) > self.trackFilesAtUtterance: - print(f'_folders, only set first {self.trackFilesAtUtterance} files, total: {len(subfiles)}') + self.info(f'_folders, only set first {self.trackFilesAtUtterance} files, total: {len(subfiles)}') subfiles = subfiles[:self.trackFilesAtUtterance] self.subfilesDict = self.getSpokenFormsDict(subfiles, extensions=1) else: @@ -610,7 +639,7 @@ def fillListsForActiveFolder(self, activeFolder): if self.trackSubfoldersAtUtterance: if len(subfolders) > self.trackSubfoldersAtUtterance: - print(f'_folders, only set first {self.trackSubfoldersAtUtterance} subfolders of total: {len(subfolders)}') + self.info(f'_folders, only set first {self.trackSubfoldersAtUtterance} subfolders of total: {len(subfolders)}') subfolders = subfolders[:self.trackSubfoldersAtUtterance] self.subfoldersDict = self.getSpokenFormsDict(subfolders) else: @@ -625,11 +654,11 @@ def fillListsForActiveFolder(self, activeFolder): self.setList('subfolders', list(self.subfoldersDict.keys())) self.activeFolder = activeFolder if self.subfilesDict and self.subfoldersDict: - print(f'activeFolder, set {len(subfiles)} files and {len(subfolders)} subfolders') + self.info(f'activeFolder, set {len(subfiles)} files and {len(subfolders)} subfolders') elif self.subfilesDict: - print(f'activeFolder, set {len(subfiles)} files') + self.info(f'activeFolder, set {len(subfiles)} files') elif self.subfoldersDict: - print(f'activeFolder, set {len(subfolders)} subfolders') + self.info(f'activeFolder, set {len(subfolders)} subfolders') def emptyListsForActiveFolder(self): @@ -653,10 +682,10 @@ def cleanupIniFoldersSection(self, section, vd): continue folder = self.substituteFolder(vd + ':/' + f) if not os.path.isdir(folder): - print('remove entry from ini folders section %s: %s (%s)'% (section, f, folder)) + self.info('remove entry from ini folders section %s: %s (%s)', section, f, folder) self.ini.delete(section, f) elif not self.acceptFileName(f): - print('remove entry from ini folders section %s: %s (%s)(invalid folder name)'% (section, f, folder)) + self.info('remove entry from ini folders section %s: %s (%s)(invalid folder name)' ,section, f, folder) self.ini.delete(section, f) self.ini.writeIfChanged() @@ -667,13 +696,13 @@ def cleanupIniFilesSection(self, section, vd): filename = self.substituteFolder(vd + ':/' + f) trunk, ext = os.path.splitext(f) if not self.acceptExtension(ext): - print('remove entry from ini files section %s: %s (%s)(invalid extension)'% (section, f, filename)) + self.info('remove entry from ini files section %s: %s (%s)(invalid extension)', section, f, filename) self.ini.delete(section, f) elif not self.acceptFileName(trunk): - print('remove entry from ini files section %s: %s (%s)(invalid filename)'% (section, f, filename)) + self.info('remove entry from ini files section %s: %s (%s)(invalid filename)', section, f, filename) self.ini.delete(section, f) elif not os.path.isfile(filename): - print('remove entry from ini files section %s: %s (%s)'% (section, f, filename)) + self.info('remove entry from ini files section %s: %s (%s)', section, f, filename) self.ini.delete(section, f) self.ini.writeIfChanged() @@ -690,7 +719,7 @@ def removeObsoleteIniSections(self, prefix, validPostfixes): if section == prefix + postfix: break else: - print('_folders grammar, deleting ini file section: %s'% section) + self.info('_folders grammar, deleting ini file section: %s', section) self.ini.delete(section) self.ini.writeIfChanged() @@ -763,10 +792,10 @@ def manageRecentFolders(self, Spoken, Folder): """ # first see if the buffer needs to be shrinked: buffer = max(10, self.maxRecentFolders//10) - print(f'manageRecentFolders buffer: {buffer}, self.maxRecentFolders: {self.maxRecentFolders}, len(recentfoldersDict): {len(self.recentfoldersDict)}') + self.info(f'manageRecentFolders buffer: {buffer}, self.maxRecentFolders: {self.maxRecentFolders}, len(recentfoldersDict): {len(self.recentfoldersDict)}') if self.recentfoldersDict: if len(self.recentfoldersDict) > self.maxRecentFolders + buffer: - print("shrink recentfoldersDict with %s items to %s"% (buffer, self.maxRecentFolders)) + self.info("shrink recentfoldersDict with %s items to %s", buffer, self.maxRecentFolders) while len(self.recentfoldersDict) >= self.maxRecentFolders: keysList = list(self.recentfoldersDict.keys()) _removeItem = self.recentfoldersDict.pop(keysList[0]) @@ -780,7 +809,7 @@ def manageRecentFolders(self, Spoken, Folder): if Spoken in self.recentfoldersDict: spokenFolder = self.recentfoldersDict[Spoken] if spokenFolder == Folder: - print(f'readd {Spoken} to recentfoldersDict') + self.info(f'readd {Spoken} to recentfoldersDict') del self.recentfoldersDict[Spoken] self.recentfoldersDict[Spoken] = Folder self.dumpRecentFoldersDict() @@ -802,10 +831,10 @@ def startRecentFolders(self): self.fillList('recentfolders') timerInterval = self.trackFoldersTimerInterval if timerInterval: - print(f'start timer interval {timerInterval} milliseconds') + self.info(f'start timer interval {timerInterval} milliseconds') else: timerInterval = 1000 - print(f'start timer with interval {timerInterval} milliseconds, for this session only') + self.info(f'start timer with interval {timerInterval} milliseconds, for this session only') natlinktimer.setTimerCallback(self.catchTimerRecentFolders, self.trackFoldersTimerInterval) # should have milliseconds def stopRecentFolders(self): @@ -814,10 +843,8 @@ def stopRecentFolders(self): self.dumpRecentFoldersDict() self.recentfoldersDict = {} self.emptyList('recentfolders') - if self.trackFoldersTimerInterval: - print("the track recent folders timer is stopped, for this session") - else: - print("the track recent folders timer is stopped.") + self.info("the track recent folders timer is stopped, for this session" if self.trackFoldersTimerInterval \ + else "the track recent folders timer is stopped.") def resetRecentFolders(self): @@ -834,17 +861,17 @@ def displayRecentFolders(self): if not self.recentfoldersDict: message = 'recent folders list is empty at the moment' self.prevDisplayRecentFolders = message - print(message) + self.info(message) return for name, value in reversed(self.recentfoldersDict.items()): mess_list.append('- %s: %s'% (name, value)) mess_list.append('-'*20) message = '\n'.join(mess_list) if message == self.prevDisplayRecentFolders: - print("recent folders, no change") + self.info("recent folders, no change") else: self.prevDisplayRecentFolders = message - print(message) + self.info(message) # def gotoRecentFolder(self, chooseNum): @@ -890,7 +917,7 @@ def gotResults_thiswebsite(self,words,fullResults): self.wantedWebsite = unimacroutils.getClipboard() self.wantedWebsite = self.wantedWebsite.rstrip("/") self.catchRemember = "website" - print('this website: %s'% self.wantedWebsite) + self.info('this website: %s', self.wantedWebsite) unimacroutils.restoreClipboard() if self.hasCommon(words, "remember"): ## dgndictation is not used at the moment!! @@ -900,11 +927,11 @@ def gotResults_thiswebsite(self,words,fullResults): self.checkForChanges = True spokenWebsite = self.getWebsiteBasenameRemember(self.wantedWebsite) if not spokenWebsite: - print("_folders, could not extract a nice spoken website from %s\nTry "% self.wantedWebsite) - print('Try "this website remember as "') + self.info("_folders, could not extract a nice spoken website from %s\nTry ", self.wantedWebsite) + self.info('Try "this website remember as "') return self.ini.set("websites", spokenWebsite, self.wantedWebsite) - print('with "website %s" you can now open %s'% (spokenWebsite, self.wantedWebsite)) + self.info('with "website %s" you can now open %s', spokenWebsite, self.wantedWebsite) self.ini.write() def getWebsiteBasenameRemember(self, url): @@ -926,7 +953,7 @@ def getFileBasenameRemember(self, filePath): if not spokenList: return namePart if len(spokenList) > 1: - print('getFileBasenameRemember, more spoken alternatives found: %s, return first item'% spokenList) + self.info('getFileBasenameRemember, more spoken alternatives found: %s, return first item', spokenList) return spokenList[0] # def checkSubfolderRecent(self, name, folder): @@ -979,7 +1006,7 @@ def getFolderBasenameRemember(self, folderPath): if not spokenList: return namePart if len(spokenList) > 1: - print('getFolderBasenameRemember, more spoken alternatives found: %s'% spokenList) + self.info('getFolderBasenameRemember, more spoken alternatives found: %s', spokenList) return spokenList[0] @@ -991,7 +1018,7 @@ def gotResults_websitecommands(self,words,fullResults): open with list in inifile, expected right hand sides to be browsers """ if not self.wantedWebsite: - print('websitecommands, no valid self.wantedWebsite: %s'% self.wantedWebsite) + self.info('websitecommands, no valid self.wantedWebsite: %s', self.wantedWebsite) nextOpenWith = False @@ -1002,7 +1029,7 @@ def gotResults_websitecommands(self,words,fullResults): self.Open = self.getFromInifile(w, 'websiteopenprograms') nextOpenWith = False else: - print("unexpected website option: %s"% w) + self.warning("unexpected website option: %s", w) def gotResults_subfolder(self, words, fullResults): """collects the given command words and try to find the given subfolder @@ -1017,8 +1044,8 @@ def gotResults_subfolder(self, words, fullResults): subfolder = self.subfoldersDict[folderWord] folder = os.path.join(self.activeFolder, subfolder) else: - print('cannot find subfolder: %s'% folderWord) - print('subfoldersDict: %s'% self.subfoldersDict) + self.info('cannot find subfolder: %s', folderWord) + self.info('subfoldersDict: %s', self.subfoldersDict) return # subfolder = None # folder1 = self.foldersDict[words[1]] @@ -1037,7 +1064,7 @@ def gotResults_recentfolder(self,words,fullResults): return if self.hasCommon("RESET", words[-1]): self.resetRecentFolders() - print("Reset recent folders list") + self.info("Reset recent folders list") return if self.hasCommon("START", words[-1]): self.startRecentFolders() @@ -1047,11 +1074,11 @@ def gotResults_recentfolder(self,words,fullResults): return if not self.recentfoldersDict: - print("no recentfolders yet") + self.info("no recentfolders yet") return name = words[-1] folder = self.recentfoldersDict[name] - print("recentfolder, name: %s, folder: %s"% (name, folder)) + self.info("recentfolder, name: %s, folder: %s", name, folder) self.wantedFolder = folder def findFolderWithIndex(self, root, allowed, ignore=None): @@ -1099,53 +1126,53 @@ def gotResults_foldercommands(self, words, fullResults): different) """ if not self.wantedFolder: - print('rule foldercommands, no wantedFolder, return') + self.info('rule foldercommands, no wantedFolder, return') return nextRemote = False for w in words: if self.hasCommon(w, 'here'): - print("got Here: ", w) + self.info(f"got Here: {w}") self.Here = True elif self.hasCommon(w, 'new'): - print("got New: ", w) + self.info(f"got New: {w}") self.New = True elif self.hasCommon(w, 'paste'): - print("got Paste, set PastePath: ", w) + self.info(f"got Paste, set PastePath: {w}") self.PastePath = True elif self.hasCommon(w, 'on'): - print("got Remote: ", w) + self.info("got Remote: {w}") nextRemote = True elif nextRemote: remoteLetter = self.getFromInifile(w, 'letters', noWarning=1) remoteVirtualDrive = self.getFromInifile(w, 'virtualdrivesspoken', noWarning=1) if remoteLetter: - print('remoteLetter: %s'% remoteLetter) + self.info('remoteLetter: %s', remoteLetter) self.Remote = remoteLetter.upper() + ":" elif remoteVirtualDrive: self.Remote = self.virtualDriveDict[remoteVirtualDrive] - print('remoteVirtualDrive: %s, resolves to: %s'% (remoteVirtualDrive, self.Remote)) + self.info('remoteVirtualDrive: %s, resolves to: %s', remoteVirtualDrive, self.Remote) nextRemote = False else: opt = self.getFromInifile(w, 'foldercommands') - print("got FolderOptions: ", opt) + self.info("got FolderOptions: %s", opt) self.FolderOptions.append(opt) def gotResults_namepathcopy(self, words, fullResults): """copy the name or the path of a folder, file or website """ if not self.catchRemember: - print("_folders, namepathcopy, do not know what to copy, folder, file or website") + self.info("_folders, namepathcopy, do not know what to copy, folder, file or website") return if self.hasCommon(words, "name"): what = "name" elif self.hasCommon(words, "path"): what = "path" else: - print("_folders, namepathcopy, choose copy name or path, not: %s"% repr(words)) + self.info("_folders, namepathcopy, choose copy name or path, not: %s", repr(words)) return if self.catchRemember == "folder": if not self.wantedFolder: - print("_folders, namepathcopy, no valid folder") + self.info("_folders, namepathcopy, no valid folder") return self.wantedFolder = self.wantedFolder.rstrip("/\\") if what == "name": @@ -1154,7 +1181,7 @@ def gotResults_namepathcopy(self, words, fullResults): result = self.wantedFolder elif self.catchRemember == "file": if not self.wantedFile: - print("_folders, namepathcopy, no valid file") + self.info("_folders, namepathcopy, no valid file") return if what == "name": result = os.path.split(self.wantedFile)[-1] @@ -1163,20 +1190,20 @@ def gotResults_namepathcopy(self, words, fullResults): elif self.catchRemember == "website": if not self.wantedWebsite: - print("_folders, namepathcopy, no valid website") + self.info("_folders, namepathcopy, no valid website") return if what == 'name': result = self.wantedWebsite.split("/")[-1] else: result = self.wantedWebsite.split()[-1] - print('namepathcopy, result: %s (type: %s)'% (result, type(result))) + self.info('namepathcopy, result: %s (type: %s)', result, type(result)) unimacroutils.setClipboard(result, 13) # 13 unicode!! def gotResults_remember(self, words, fullResults): """treat the remember function, filling items in ini files """ if not self.catchRemember: - print('_folders, in remember rule, but nothing to remember') + self.info('_folders, in remember rule, but nothing to remember') return if self.catchRemember == "folder": self.rememberBase = self.getFolderBasenameRemember(self.wantedFolder) @@ -1207,7 +1234,7 @@ def gotResults_remember(self, words, fullResults): default = self.rememberBase section = 'files' else: - print('_folders, invalid value for catchRemember: %s'% self.catchRemember) + self.info('_folders, invalid value for catchRemember: %s', self.catchRemember) return prompt = "Remember in Unimacro _folders grammar" inifile = self.ini._file @@ -1218,11 +1245,11 @@ def gotResults_remember(self, words, fullResults): pausetime = 3 # reset variables, no action in gotResults: self.wantedFile = self.wantedFolder = self.wantedWebsite = "" - print(f'thisDir: {thisDir}') + self.info(f'thisDir: {thisDir}') UnimacroDirectory = extenvvars.expandEnvVariableAtStart('%Unimacro%') - print(f'UnimacroDirectory: {UnimacroDirectory}') + self.info(f'UnimacroDirectory: {UnimacroDirectory}') UnimacroGrammarsDirectory = extenvvars.expandEnvVariableAtStart('%UnimacroGrammars%') - print(f'UnimacroGrammarsDirectory: {UnimacroGrammarsDirectory}') + self.info(f'UnimacroGrammarsDirectory: {UnimacroGrammarsDirectory}') makeFromTemplateAndExecute(UnimacroDirectory, "unimacrofoldersremembertemplate.py", UnimacroGrammarsDirectory, "rememberdialog.py", prompt, text, default, inifile, section, value, pausetime=pausetime) @@ -1237,7 +1264,7 @@ def get_active_explorer(self, hndle=None): for window in shell.Windows(): if int(window.HWND) == int(hndle): return window - print("_folders: no active explorer.") + self.info("_folders: no active explorer.") return None def get_current_directory(self, hndle=None): @@ -1255,7 +1282,7 @@ def get_current_directory(self, hndle=None): def get_selected_paths(self): window = self.get_active_explorer() if window is None: - print('get_selected_paths, cannot find application') + self.info('get_selected_paths, cannot find application') return None items = window.Document.SelectedItems() paths = [] @@ -1280,12 +1307,12 @@ def gotResults_thisfile(self, words, fullResults): ## wait for the mouse to have stoppede moving button, nClick = 'left', 1 if not self.doWaitForMouseToStop(): - print('_folders, thisfile, mouse did not stop, cannot click') + self.info('_folders, thisfile, mouse did not stop, cannot click') return unimacroutils.buttonClick(button, nClick) unimacroutils.visibleWait() - # print 'filenames: %s'% self.get_selected_filenames() + # self.info 'filenames: %s'% self.get_selected_filenames() self.wantedFile = None # paths = self.get_selected_paths() # if paths: @@ -1294,7 +1321,7 @@ def gotResults_thisfile(self, words, fullResults): # self.wantedFile = p # break # else: - # print "warning, thisfile: no valid file found" + # self.info "warning, thisfile: no valid file found" # # else: unimacroutils.saveClipboard() @@ -1308,32 +1335,32 @@ def gotResults_thisfile(self, words, fullResults): paths1 = [p for p in paths1 if os.path.isfile(p)] paths2 = get_selected_files(folders=False) - print('get_system_folderinfo: %s'% paths1) - print('get_selected_files: %s'% paths2) + self.info('get_system_folderinfo: %s', paths1) + self.info('get_selected_files: %s', paths2) if paths1 and paths2: if paths1 == paths2: paths = paths1 else: - print('_thisfile, different info for both methods:\nVia Clipboard %s\nVia this module functions: %s'% \ - (repr(paths1), repr(paths2))) + self.info('_thisfile, different info for both methods:\nVia Clipboard %s\nVia this module functions: %s', \ + repr(paths1), repr(paths2)) paths = paths2 elif paths1: - print('_thisfile, only paths1 (via clipboard) has data: %s'% repr(paths1)) + self.info('_thisfile, only paths1 (via clipboard) has data: %s', repr(paths1)) paths = paths1 elif paths2: paths = paths2 - print('_thisfile, only paths2 (this module functions) has data: %s'% repr(paths2)) + self.info('_thisfile, only paths2 (this module functions) has data: %s', repr(paths2)) else: - print('no paths info found with either methods') + self.info('no paths info found with either methods') paths = None if not paths: - print("no selected file found") + self.info("no selected file found") return self.wantedFile = paths[0] if len(paths) > 1: - print("warning, more files selected, take the first one: %s"% self.wantedFile) - print('wantedFile: %s'% self.wantedFile) + self.warning("warning, more files selected, take the first one: %s", self.wantedFile) + self.info('wantedFile: %s', self.wantedFile) self.catchRemember = "file" def gotResults_disc(self,words,fullResults): @@ -1342,7 +1369,7 @@ def gotResults_disc(self,words,fullResults): if letter: f = letter + ":\\" else: - print('_folders, ruls disc, no letter provided: %s'% words) + self.info('_folders, ruls disc, no letter provided: %s', words) return if self.nextRule in ['foldercommands']: @@ -1363,28 +1390,28 @@ def gotResults_file(self,words,fullResults): if extension: File, _old_extension = os.path.splitext (File) File = File +'.' + extension - print('got file: %s'% File) + self.info('got file: %s', File) File = os.path.join(self.activeFolder, File) if not os.path.isfile(File): - print('folders, file, from subfilesList, not a valid path: %s (return None)'% File) + self.info('folders, file, from subfilesList, not a valid path: %s (return None)', File) File = None else: - print('file from subfileslist: %s'% File) + self.info('file from subfileslist: %s', File) self.catchRemember = "file" if not File: try: File = self.filesDict[wantedFile] except KeyError: - print('file cannot be found in filesDict: %s (and not in subfilesDict either)'% wantedFile) + self.info('file cannot be found in filesDict: %s (and not in subfilesDict either)', wantedFile) return File = self.substituteFolder(File) - print("_folders, get file: actual filename (fixed fileslist): %s"% File) + self.info("_folders, get file: actual filename (fixed fileslist): %s", File) extension =self.getFromInifile(words, 'extensions', noWarning=1) if extension: File, _old_extension =os.path.splitext (File) File = File +'.' + extension if not os.path.isfile(File): - print('invalid file: %s'% File) + self.info('invalid file: %s', File) return if self.nextRule in ["filecommands", "remember"]: self.wantedFile = File @@ -1395,7 +1422,7 @@ def gotResults_file(self,words,fullResults): def gotResults_filecommands(self, words, fullResults): if not self.wantedFile: - print('rule filecommands, no wantedFile, return') + self.info('rule filecommands, no wantedFile, return') return # print 'filecommands: %s'% words OpenWith = Remote = False @@ -1411,15 +1438,15 @@ def gotResults_filecommands(self, words, fullResults): remoteLetter = self.getFromInifile(w, 'letters', noWarning=1) remoteVirtualDrive = self.getFromInifile(w, 'virtualdrivesspoken', noWarning=1) if remoteLetter: - print('remoteLetter: %s'% remoteLetter) + self.info('remoteLetter: %s', remoteLetter) self.Remote = remoteLetter.upper() + ":" elif remoteVirtualDrive: self.Remote = self.virtualDriveDict[remoteVirtualDrive] - print('remoteVirtualDrive: %s, resolves to: %s'% (remoteVirtualDrive, self.Remote)) + self.info('remoteVirtualDrive: %s, resolves to: %s', remoteVirtualDrive, self.Remote) Remote = False else: act = self.getFromInifile(w, 'foldercommands') - print("got FileCommand: ", act) + self.info("got FileCommand: ", act) self.FileOptions.append(act) def gotResults_thisfolder(self,words,fullResults): @@ -1435,7 +1462,7 @@ def gotResults_thisfolder(self,words,fullResults): ## wait for the mouse to have stoppede moving button, nClick = 'left', 1 if not self.doWaitForMouseToStop(): - print("_folders, command thisfolder: doWaitForMouseToStop fails") + self.info("_folders, command thisfolder: doWaitForMouseToStop fails") return unimacroutils.buttonClick(button, nClick) unimacroutils.visibleWait() @@ -1456,35 +1483,35 @@ def gotResults_thisfolder(self,words,fullResults): if paths1 == paths2: paths = paths1 else: - print('_thisfolder, different info for both methods:\nVia Clipboard %s\nVia this module functions: %s'% \ - (repr(paths1), repr(paths2))) + self.info('_thisfolder, different info for both methods:\nVia Clipboard %s\nVia this module functions: %s', \ + repr(paths1), repr(paths2)) paths = paths2 elif paths1: - print('_thisfolder, only paths1 (via clipboard) has data: %s'% repr(paths1)) + self.info('_thisfolder, only paths1 (via clipboard) has data: %s', repr(paths1)) paths = paths1 elif paths2: # paths = paths2 - print('_thisfolder, only paths2 (this module functions) has data: %s'% repr(paths2)) + self.info('_thisfolder, only paths2 (this module functions) has data: %s', repr(paths2)) else: - print('no paths info found with either methods') + self.info('no paths info found with either methods') paths = None - print('paths:::: %s'% paths) # + self.info('paths:::: %s', paths) # if paths: self.wantedFolder = paths[0] if len(paths) > 1: - print("warning, more items selected, take the first one: %s"% self.wantedFolder) + self.info("warning, more items selected, take the first one: %s", self.wantedFolder) elif self.activeFolder: - print('take activeFolder: %s'% self.activeFolder) + self.info('take activeFolder: %s', self.activeFolder) self.wantedFolder = self.activeFolder else: - print('"this folder" no selected folder found') + self.info('"this folder" no selected folder found') return if os.path.isdir(self.wantedFolder): - # print '_folders, this folder, wantedFolder: %s'% self.wantedFolder + # self.info '_folders, this folder, wantedFolder: %s'% self.wantedFolder self.catchRemember = "folder" # in case remember follows else: - print('_folders, wantedFolder not a valid folder: %s'% self.wantedFolder) + self.info('_folders, wantedFolder not a valid folder: %s', self.wantedFolder) # def gotResults_folderup(self,words,fullResults): @@ -1498,16 +1525,16 @@ def gotResults_folderup(self,words,fullResults): # print 'iambrowser: %s Iamexplorer: %s'% (browser, IamExplorer) istop = self.getTopOrChild(self.progInfo, childClass="#32770") # True if top window if IamChild32770: - print("IamChild32770: ", self.activeFolder) + self.info(f"IamChild32770: {self.activeFolder}") if not self.activeFolder: self.activeFolder = mess.getFolderFromDialog(hndle, classname) - print("IamChild32770 getFolderFromDialog: ", self.activeFolder) + self.info(f"IamChild32770 getFolderFromDialog: {self.activeFolder}") if self.activeFolder: newfolder = self.goUpInPath(self.activeFolder, upn) - #print 'newfolder (up %s): %s'% (upn, newfolder) + #self.info 'newfolder (up %s): %s'% (upn, newfolder) self.gotoInThisDialog(newfolder, hndle, classname) else: - print('method not working (any more) for #32770: %s'% title) + self.info('method not working (any more) for #32770: %s', title) elif not istop: # child window actions @@ -1533,18 +1560,18 @@ def gotResults_folderup(self,words,fullResults): self.activeFolder = mess.getFolderFromCabinetWClass(hndle) if self.activeFolder: newfolder = self.goUpInPath(self.activeFolder, upn) - print('newfolder (up %s): %s'% (upn, newfolder)) + self.info('newfolder (up %s): %s', upn, newfolder) self.gotoInThisComputer(newfolder) else: - print('method not working any more, going folder up') + self.info('method not working any more, going folder up') action("MP 1, 50, 10, 0") for _i in range(upn): action("{backspace} VW") else: - print('yet to implement, folder up for %s'% prog) + self.info('yet to implement, folder up for %s', prog) - #print 'had folder up: %s'% words + #self.info 'had folder up: %s'% words def substituteFolder(self, folder): @@ -1686,9 +1713,9 @@ def gotoWebsite(self, f): """ if self.Open: - print("gotoWebsite %s with: %s", (f, self.Open)) + self.info("gotoWebsite %s with: %s", f, self.Open) else: - print("gotoWebsite: ", f) + self.info("gotoWebsite: %s", f) self.openWebsiteDefault(f, openWith=self.Open) def gotoFile(self, f): @@ -1709,7 +1736,7 @@ def gotoFile(self, f): istop = self.getTopOrChild( self.progInfo, childClass="#32770") # True if top if self.Remote: - print('Remote: %s'% self.Remote) + self.info('Remote: %s', self.Remote) f = self.getValidFile(f, self.Remote) if not f: @@ -1729,7 +1756,7 @@ def gotoFile(self, f): if not istop: # child window actions # put the mouse in the left top corner of the window: - print("Open file from child window: %s"% f) + self.info("Open file from child window: %s", f) action("RMP 1, 0.02, 0.05, 0") action('<>') unimacroutils.saveClipboard() @@ -1752,10 +1779,10 @@ def openFileDefault(self, filename, mode=None, windowStyle=None, name=None, open """ ## action('CW') if not os.path.isfile(filename): - print('file does not exist, cannot open: %s'% filename) + self.info('file does not exist, cannot open: %s', filename) return if not ancestor.openFileDefault(self, filename, mode=mode, openWith=openWith): - print('could not open %s (mode: %s, openWith: %s)'% (filename, mode, openWith)) + self.info('could not open %s (mode: %s, openWith: %s)', filename, mode, openWith) return try: # try is needed in case function is called from another class (dirty trick with _control, sorry) @@ -1775,12 +1802,12 @@ def openFolderDefault(self, foldername, mode=None, windowStyle=None, openWith=No #print 'going to open folder: %s'% foldername if not ancestor.openFolderDefault(self, foldername, mode=mode, openWith=openWith): - print('failed to open folder: %s'% foldername) + self.info('failed to open folder: %s', foldername) return for act in self.FolderOptions: if act: - print("openFolderDefault, action: %s"% act) + self.info("openFolderDefault, action: %s", act) action(act) # This is the function which does the real work, depending on the @@ -1857,23 +1884,23 @@ def gotoFolder(self, f): return if prog == 'cmd': - print("_folder, for cmd: %s"% f) + self.info("_folder, for cmd: %s", f) # t = " " + f action('SCLIP(%s)'% f) return if self.Remote: - print('Remote: %s'% self.Remote) + self.info('Remote: %s', self.Remote) f = self.getValidDirectory(f, self.Remote) - print('Remote: %s'% f) + self.info('Remote: %s', f) if not f: return if self.PastePath: action("SCLIP(%s)"%f) - print("PastePath: %s"% f) + self.info("PastePath: %s", f) return # if self.CopyNamePath: - print('put path on clipboard: "%s"'% f) + self.info('put path on clipboard: "%s"', f) unimacroutils.setClipboard(f) return @@ -1881,7 +1908,7 @@ def gotoFolder(self, f): prog = self.progInfo.prog hndle = self.progInfo.hndle if not hndle: - print('_folders, gotoFolder: no window handle found, return') + self.info('_folders, gotoFolder: no window handle found, return') # Iam2x = prog == '2xexplorer' IamExplorer = prog == 'explorer' _browser = prog in ['iexplore', 'firefox','opera', 'netscp', 'brave'] @@ -1898,13 +1925,13 @@ def gotoFolder(self, f): # print("go from here activeFolder: %s"% self.activeFolder) self.gotoInThisDialog(f, hndle, self.className) return - print("no files/folder dialog, treat as top window") + self.info("no files/folder dialog, treat as top window") self.openFolderDefault(f) return if not istop: # child window actions # put the mouse in the left top corner of the window: - print("_folders, child window, comes ever here???") + self.info("_folders, child window, comes ever here???") action("RMP 1, 0.02, 0.05, 0") action('<>') unimacroutils.saveClipboard() @@ -1958,11 +1985,11 @@ def gotoFolder(self, f): if exactList: ## print 'exactList %s' % (exactList) if len(exactList) > 1: - print('warning, 2 matching windows: %s'% exactList) + self.info('warning, 2 matching windows: %s', exactList) t, h = exactList[0] unimacroutils.SetForegroundWindow(h) elif overList: -## print 'over List %s' % (overList) +## self.info 'over List %s' % (overList) # eg f = d:\\a\\b # and elements of overList are d:\\a\\b\\c and d:\\a\\b\\c\\d # goto shortest element @@ -1988,7 +2015,7 @@ def gotoFolder(self, f): # eg f = d:\\a\\b\\c # elementes of underList are d:\\a d:\\a\\b etc. # go to longest element and switch in that window to folder - print('under list, go to first folder') + self.info('under list, go to first folder') lenMax = 0 for t, h in underList: @@ -2010,7 +2037,7 @@ def gotoFolder(self, f): if unimacroutils.SetForegroundWindow(h): self.gotoInThisComputer(f) else: - print('could not set foregroundwindow: %s'% h) + self.info('could not set foregroundwindow: %s', h) self.openFolderDefault(f) else: @@ -2018,7 +2045,7 @@ def gotoFolder(self, f): self.openFolderDefault(f) else: # no this computer windows (yet) - print("grammar folders shouldn't be here!") + self.info("grammar folders shouldn't be here!") def getValidDirectory(self, f, remote): @@ -2048,7 +2075,7 @@ def getValidDirectory(self, f, remote): if os.path.isdir(tryF): return tryF fparts.pop(0) - print('_folders, no valid remote folder found for %s and remote: %s'% (f, remote)) + self.info('_folders, no valid remote folder found for %s and remote: %s', (f, remote)) return '' def getValidFile(self, f, remote): @@ -2060,7 +2087,7 @@ def getValidFile(self, f, remote): if os.path.isfile(tryF): return tryF fparts.pop(0) - print('_folders, no valid remote file found for %s and remote: %s'% (f, remote)) + self.info('_folders, no valid remote file found for %s and remote: %s', f, remote) return '' @@ -2095,7 +2122,7 @@ def gotoInThisDialog(self, f, hndle, className): elif os.path.isfile(f): folder, filename = os.path.split(f) else: - print('invalid target for gotoInThisDialog: %s'% f) + self.info('invalid target for gotoInThisDialog: %s', f) return if folder != activeFolder: @@ -2106,7 +2133,7 @@ def gotoInThisDialog(self, f, hndle, className): action('W') keystroke('{shift+tab}') if filename: - action("SCLIP %s"% filename) + action("SCLIP %s", filename) # keystroke(filename) def gotoInOtherExplorer(self, f): @@ -2118,7 +2145,7 @@ def gotoInOtherExplorer(self, f): if self.useOtherExplorer == "xplorer2": keystroke("{shift+tab}%s{enter}{down}{up}"% f) else: - print('_folders, please specify in function "gotoInOtherExplorer" for "use other explorer": "%s"'% self.useOtherExplorer) + self.info('_folders, please specify in function "gotoInOtherExplorer" for "use other explorer": "%s"', self.useOtherExplorer) def goUpInPath(self, PATH, nsteps=None): """return a new path, n steps up in hierarchy, default 1 @@ -2145,9 +2172,9 @@ def doStartWindowsExplorer(self): try: unimacroutils.waitForNewWindow(50, 0.05) # 2,5 seconds max except unimacroutils.NatlinkCommandTimeOut: - print('Error with action "start windows explorer" (%s) from command in grammar + "_folders".' % \ + self.info('Error with action "start windows explorer" (%s) from command in grammar + "_folders".' , \ startExplorer) - print('Correct in ini file by using the command: ' + {'enx': "Edit Folders", + self.info('Correct in ini file by using the command: ' + {'enx': "Edit Folders", 'nld': "Bewerk folders"}[self.language]) return None return 1 @@ -2193,7 +2220,7 @@ def getExplorerTitles(): """ TitlesHandles = [] ## Classes come from global variable at top of this module - ##print 'Classes:', Classes + ##self.info 'Classes:', Classes ## Classes = None win32gui.EnumWindows(getExplWindowsWithText, (TitlesHandles, Classes)) return TitlesHandles @@ -2261,7 +2288,7 @@ def get_clipboard_formats(): while f: formats.append(f) f = win32clipboard.EnumClipboardFormats(f) - # print '_folders, clipboard formats: %s'% formats + # self.info '_folders, clipboard formats: %s'% formats return formats def get_selected_files(folders=False): @@ -2275,7 +2302,7 @@ def get_selected_files(folders=False): time.sleep(0.1) files = get_clipboard_files(folders) # cb.copy_to_system() - # print 'files: %s'% files + # self.info 'files: %s'% files return files def get_clipboard_files(folders=False): @@ -2289,7 +2316,7 @@ def get_clipboard_files(folders=False): if win32clipboard.CF_HDROP in f: files = win32clipboard.GetClipboardData(win32clipboard.CF_HDROP) else: - # print 'get_clipboard_files, not expected clipboard format CF_HDROP, but %s'% f + # self.info 'get_clipboard_files, not expected clipboard format CF_HDROP, but %s'% f if win32clipboard.CF_UNICODETEXT in f: files = [win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)] elif win32clipboard.CF_TEXT in f: @@ -2297,7 +2324,7 @@ def get_clipboard_files(folders=False): elif win32clipboard.CF_OEMTEXT in f: files = [win32clipboard.GetClipboardData(win32clipboard.CF_OEMTEXT)] if not files: - # print "get_clipboard_files, no files found from clipboard" + # self.info "get_clipboard_files, no files found from clipboard" return None if folders: files = [f for f in files if os.path.isdir(f)] if files else None @@ -2313,7 +2340,7 @@ def makeFromTemplateAndExecute(unimacrofolder, templatefile, unimacrogrammarsfol meant for setting up a inputbox dialog """ rwfile = readwritefile.ReadWriteFile() - print(f'unimacrofolder: {unimacrofolder}') + logger.info(f'unimacrofolder: {unimacrofolder}') Text = rwfile.readAnything(os.path.join(unimacrofolder, templatefile)) # print(f'OldText: {Text}') for orig, toreplace in [('$prompt$', prompt), ('$default$', default), ('$text$', text), @@ -2349,9 +2376,9 @@ def connectOutlook(): pass #outlookApp = win32com.client.Dispatch('Outlook.Application') if outlookApp: - print('outlook application collected') + logger.info('outlook application collected') return outlookApp - print('outlook not connected') + logger.info('outlook not connected') outlookApp = None outlookAppProgram = None return outlookApp @@ -2396,30 +2423,29 @@ def unload(): # print("unloading folders grammar") thisGrammar.stopRecentFolders() # stopping the timer callback thisGrammar.unload() - print("unloaded folders grammar") + logger.info("unloaded folders grammar") thisGrammar = None if __name__ == "__main__": ## interactive use, for debugging: - natlink.natConnect() - try: - thisGrammar = ThisGrammar(inifile_stem="_folders") - # thisGrammar.startInifile() - thisGrammar.initialize() + with natlink.natConnect(): + try: + thisGrammar = ThisGrammar(inifile_stem="_folders") + # thisGrammar.startInifile() + thisGrammar.initialize() - # get hndle of a explore window (via _general "give window info") and try interactive - # thisGrammar.catchTimerRecentFolders(132524, "CabinetWClass") - thisGrammar.getActiveFolder(198518) + # get hndle of a explore window (via _general "give window info") and try interactive + # thisGrammar.catchTimerRecentFolders(132524, "CabinetWClass") + thisGrammar.getActiveFolder(198518) - # # Words = ['folder', 'dtactions'] - # Fr = {} - # Words = ['subfolder', 'hello'] - # thisGrammar.gotResultsInit(Words, Fr) - # thisGrammar.gotResults_subfolder(Words, Fr) - finally: - thisGrammar.unload() - natlink.natDisconnect() + # # Words = ['folder', 'dtactions'] + # Fr = {} + # Words = ['subfolder', 'hello'] + # thisGrammar.gotResultsInit(Words, Fr) + # thisGrammar.gotResults_subfolder(Words, Fr) + finally: + thisGrammar.unload() elif __name__.find('.') == -1: # standard startup when Dragon starts: thisGrammar = ThisGrammar() diff --git a/src/unimacro/__init__.py b/src/unimacro/__init__.py index 879c3a2..be84d55 100644 --- a/src/unimacro/__init__.py +++ b/src/unimacro/__init__.py @@ -10,6 +10,10 @@ #these functions are in this module so that they can be loaded without loading a lot of unimacro code. #they could be in a seperate .py file in unimacro to achieve the same (ie not in the control grammar). +#these will possilby be removed since we may not need them to enumerate the grammars and ask for log names. + +def folders_logger_name() -> str: + return "natlink.unimacro.folders" def control_logger_name() -> str : return "natlink.unimacro.control" diff --git a/src/unimacro/_control.py b/src/unimacro/_control.py index 4c975c6..0ca37f8 100644 --- a/src/unimacro/_control.py +++ b/src/unimacro/_control.py @@ -39,7 +39,7 @@ #for some reason, importing amodule which does this doesn't work. Likely because natlinkmain must be started first for #this sublogger natlink.unimacro to work correctly. import unimacro as unimacro_l #bring in so we can add a variable ulogger to the namespace. -ulogger : l.Logger = l.getLogger("natlink.unimacro") +ulogger : l.Logger = l.getLogger(unimacro_l.logname()) unimacro_l.__dict__['ulogger']=ulogger ulogger.debug("natlink.unimacro logger available") @@ -87,7 +87,7 @@ def natlink_loggers() ->dict: loggers=dict() for ep in discovered_eps: try: - (name,_)=ep + name=ep.name module=ep.module module_loaded=module in sys.modules @@ -188,9 +188,17 @@ def initialize(self): ## if unimacroutils.getUser() == 'martijn': ## print 'martijn, set exclusive %s'% self.name ## self.setExclusive(1) - control_logger.info('---now starting other Unimacro grammars:') + self.info('---now starting other Unimacro grammars:') + def loggerName(self) ->str: + """Returns the name of a logger. Replace this and loggerShortName to create a logger for an inherited grammar. """ + return unimacro_l.control_logger_name() + + def loggerShortName(self) ->str: + """A key for use as a spoken form or user interface item. """ + return "control" + def unload(self): self.UnregisterControlObject() ancestor.unload(self) @@ -247,7 +255,7 @@ def gotResults_checkalphabet(self,words,fullResults): for letter in string.ascii_lowercase: spoken = ini.get(alph, letter, '') if not spoken: - control_logger.info('fill in in "%s_spokenform.ini", [alphabet] spoken for: "%s"'% (self.language, letter)) + self.info('fill in in "%s_spokenform.ini", [alphabet] spoken for: "%s"'% (self.language, letter)) continue if version < 11: normalform = '%s\\%s'% (letter.upper(), spoken) @@ -256,7 +264,7 @@ def gotResults_checkalphabet(self,words,fullResults): try: natlink.recognitionMimic([normalform]) except natlink.MimicFailed: - control_logger.info('invalid spoken form "%s" for "%s"'% (spoken, letter)) + self.info('invalid spoken form "%s" for "%s"'% (spoken, letter)) if spoken == spoken.lower(): spoken = spoken.capitalize() trying = 'try capitalized variant' @@ -272,15 +280,15 @@ def gotResults_checkalphabet(self,words,fullResults): try: natlink.recognitionMimic([normalform]) except natlink.MimicFailed: - control_logger.info('%s fails also: "%s" for "%s"'% (trying, spoken, letter)) + self.info('%s fails also: "%s" for "%s"'% (trying, spoken, letter)) else: - control_logger.info('alphabet section is corrected with: "%s = %s"'% (letter, spoken)) + self.info('alphabet section is corrected with: "%s = %s"'% (letter, spoken)) ini.set(alph, letter, spoken) ini.writeIfChanged() def gotResults_trace(self,words,fullResults): - control_logger.info('control, trace: %s'% words) + self.info('control, trace: %s'% words) traceNumList = self.getNumbersFromSpoken(words) # returns a string or None if traceNumList: traceNum = int(traceNumList[0]) @@ -299,7 +307,7 @@ def gotResults_trace(self,words,fullResults): else: actions.debugActions(1) elif self.hasCommon(words, 'spoken forms'): - control_logger.info("no tracing possible for spoken forms") + self.info("no tracing possible for spoken forms") #def gotResults_voicecode(self,words,fullResults): # """switch on if requirements are fulfilled @@ -343,7 +351,7 @@ def gotResults_switch(self,words,fullResults): self.switch(gram, gname, switchOn) # self never needs switching on else: - control_logger.info('_control switch, no valid grammar found, command: %s'% words) + self.info('_control switch, no valid grammar found, command: %s'% words) def switch(self, gram, gname, switchOn): """switch on or off grammar, and set in inifile, @@ -352,7 +360,7 @@ def switch(self, gram, gname, switchOn): switchOn is True or False """ if gram == self: - control_logger.error(f'should not be here, do not switch on of off _control {gram}') + self.error(f'should not be here, do not switch on of off _control {gram}') return None if switchOn: if gram.ini: @@ -361,16 +369,16 @@ def switch(self, gram, gname, switchOn): gram.ini.write() unimacroutils.Wait(0.1) else: - control_logger.error(f'--- ini file of grammar {gname} is invalid, please try "edit {gname}"...') + self.error(f'--- ini file of grammar {gname} is invalid, please try "edit {gname}"...') gramName = gram.getName() unimacro_grammars_paths = self.getUnimacroGrammarNamesPaths() try: filepath = Path(unimacro_grammars_paths[gramName]) except KeyError: - control_logger.error(f'_control, grammar not in unimacro_grammars_paths dict: {gramName}, cannot switchOn') + self.error(f'_control, grammar not in unimacro_grammars_paths dict: {gramName}, cannot switchOn') return None # now reload with force option. - control_logger.info(f'_control, now reload grammar "{gramName}":') + self.info(f'_control, now reload grammar "{gramName}":') natlinkmain.seen.clear() natlinkmain.load_or_reload_module(filepath, force_load=True) @@ -382,13 +390,13 @@ def switch(self, gram, gname, switchOn): gram.cancelMode() gram.deactivateAll() # gram.unload() - control_logger.info('grammar "%s" switched off'% gram.getName()) + self.info('grammar "%s" switched off'% gram.getName()) return 1 def gotResults_setlogging(self,words, fullresults): """ """ - control_logger.debug(f"unimacro logger gotResults_logging_level words: {words} fullResults: {fullresults}") + self.debug(f"unimacro logger gotResults_logging_level words: {words} fullResults: {fullresults}") loglevel_for = words[0] # something like natlink, unimacro,... new_level_str_mc,_=fullresults[-1] @@ -397,7 +405,7 @@ def gotResults_setlogging(self,words, fullresults): logger_name=self.loggers[loglevel_for] new_log_level=l.__dict__[new_log_level_str] - control_logger.debug(f"New Log Level {new_log_level_str} for logger {logger_name}") + self.debug(f"New Log Level {new_log_level_str} for logger {logger_name}") logger=l.getLogger(logger_name) logger.setLevel(new_log_level) @@ -407,7 +415,7 @@ def gotResults_setlogging(self,words, fullresults): #Hide numbers def gotResults_loglevel(self,words,fullresults): """ """ - control_logger.debug(f"gotResults_logging_level words: {words} fullResults: {fullresults}") + self.debug(f"gotResults_logging_level words: {words} fullResults: {fullresults}") return @@ -420,7 +428,7 @@ def gotResults_showexclusive(self,words,fullResults): else: Start=() # fix state at this moment (in case of Active grammars popup) - control_logger.info(f'_control, showexclusive, exclusiveGrammars: {natbj.exclusiveGrammars}') + self.info(f'_control, showexclusive, exclusiveGrammars: {natbj.exclusiveGrammars}') if natbj.exclusiveGrammars: Exclusive = 1 self.BrowsePrepare(Start, All, Exclusive) @@ -444,7 +452,7 @@ def gotResults_showexclusive(self,words,fullResults): def gotResults_resetexclusive(self,words,fullResults): - control_logger.info('reset exclusive') + self.info('reset exclusive') exclGrammars = natbj.getExclusiveGrammars() if exclGrammars: T = ['exclusive grammars:'] @@ -483,17 +491,21 @@ def gotResults_show(self,words,fullResults): if self.hasCommon(words, 'exclusive'): G = self.getExclusiveGrammars() exclNames = [gname for gname, gram in G.items() if gram.isExclusive()] - control_logger.info(f'exclusive grammars (+ control) are: {exclNames}') + self.info(f'exclusive grammars (+ control) are: {exclNames}') self.gotResults_showexclusive(words, fullResults) return if self.hasCommon(words,"loggers"): - control_logger.info(f"Available Loggers: {self.loggers}") + self.info(f"Available Loggers: {self.loggers}") return grammars = self.getUnimacroGrammars() gramNames = list(grammars.keys()) - control_logger.debug(f'_control, gramNames: {gramNames}') + print("gramNames") + print(f'{gramNames}') + print(f"self.debug {self.debug} self.info {self.info}") + self.info("info") + self.debug(f'_control, gramNames: {gramNames}') gramName = self.hasCommon(words, gramNames) if gramName: grammar = grammars[gramName] @@ -538,9 +550,9 @@ def gotResults_show(self,words,fullResults): activeGrammars = [g for g in G if G[g].isActive()] inactiveGrammars = [g for g in G if G[g].isLoaded() and not G[g].isActive()] switchedOffGrammars = [g for g in G if not G[g].isLoaded()] - control_logger.info(f'activeGrammars: {activeGrammars}') - control_logger.info(f'inactiveGrammars: {inactiveGrammars}') - control_logger.info(f'switchedOffGrammars: {switchedOffGrammars}') + self.info(f'activeGrammars: {activeGrammars}') + self.info(f'inactiveGrammars: {inactiveGrammars}') + self.info(f'switchedOffGrammars: {switchedOffGrammars}') # for grammar_name, gram in G.items(): # print(f'grammar_name: {grammar_name}, gram: {gram}') @@ -611,7 +623,7 @@ def gotResults_edit(self,words,fullResults): try: grammar = grammars[gramName] except KeyError: - control_logger.error(f'grammar {words[-1:]} not found in list of gramNames:\n{gramNames}') + self.error(f'grammar {words[-1:]} not found in list of gramNames:\n{gramNames}') return # print(f'grammar: {gramName}: {grammar}') if self.hasCommon(words, 'grammar'): @@ -620,9 +632,9 @@ def gotResults_edit(self,words,fullResults): try: filepath = unimacro_grammars_paths[gramName] except KeyError: - control_logger.error(f'grammar not in unimacro_grammars_paths dict: {gramName}') + self.error(f'grammar not in unimacro_grammars_paths dict: {gramName}') return - control_logger.info(f'open for edit file: "{filepath}"') + self.info(f'open for edit file: "{filepath}"') self.openFileDefault(filepath, mode="edit", name=f'edit grammar {gramName}') else: # edit the inifile @@ -636,7 +648,7 @@ def switchOff(self, **kw): """overload, this grammar never switches off """ - control_logger.info('remains switched on: %s' % self) + self.info('remains switched on: %s' % self) def switchOn(self, **kw): """overload, just switch on @@ -696,7 +708,7 @@ def getUnimacroGrammarNamesPaths(self): unimacro_modules[name] = try_file break else: - control_logger.info(f'not found in natlink_modules_files: {name}') + self.info(f'not found in natlink_modules_files: {name}') unimacro_modules[name] = name # not found return unimacro_modules @@ -777,16 +789,13 @@ def checkOriginalFileWithActualTxtPy(name, org_path, txt_path, py_path): # standard stuff Joel (adapted for python3, QH, unimacro): if __name__ == "__main__": ## interactive use, for debugging: - natlink.natConnect() - try: + with natlink.natConnect(): utilGrammar = UtilGrammar(inifile_stem='_control') utilGrammar.startInifile() utilGrammar.initialize() Words = ['edit', 'grammar', 'control'] FR = {} utilGrammar.gotResults_edit(Words, FR) - finally: - natlink.natDisconnect() elif __name__.find('.') == -1: # standard startup when Dragon starts: utilGrammar = UtilGrammar() diff --git a/src/unimacro/natlinkutilsbj.py b/src/unimacro/natlinkutilsbj.py index 9c3869d..521bd9d 100644 --- a/src/unimacro/natlinkutilsbj.py +++ b/src/unimacro/natlinkutilsbj.py @@ -45,6 +45,7 @@ import string from pathlib import Path import win32com +import logging import natlink from natlinkcore import loader @@ -69,7 +70,7 @@ from unimacro import D_ from unimacro import spokenforms # for numbers spoken forms, IniGrammar (and also then DocstringGrammar) - +from unimacro import logname status = natlinkstatus.NatlinkStatus() natlinkmain = loader.NatlinkMain() @@ -302,8 +303,40 @@ def __init__(self): self.want_on_or_off = None # True: on False: off None: no decision self.hypothesis = 0 self.allResults = 0 - + + + def loggerName(self) ->str: + """Returns the name of a logger. Replace this and loggerShortName to create a logger for an inherited grammar. """ + return logname() + + def loggerShortName(self) ->str: + """A key for use as a spoken form or user interface item.""" + return "unimacro" + + def getLogger(self) -> logging.Logger: + print(f"Get Logger , name is {self.loggerName()}") + return logging.getLogger(self.loggerName()) + #info,warning,error,exception,debug,log are forwarded the the appropriate logger.+ + + + def info(self,msg,*args,**kwargs): + return self.getLogger().info(msg,*args,**kwargs) + def debug(self,msg,*args,**kwargs): + return self.getLogger().debug(msg,*args,**kwargs) + def warning(self,msg,*args,**kwargs): + return self.getLogger().warning(msg,*args,**kwargs) + def error(self,msg,*args,**kwargs): + return self.getLogger().error(msg,*args,**kwargs) + def exception(self,msg,*args,**kwargs): + return self.getLogger().error(msg,*args,**kwargs) + def log(self,level,msg,*args,**kwargs): + return self.getLogger().error(level,msg,*args,**kwargs) + + + + + def getExclusiveGrammars(self): """return the dict of (name, grammarobject) of GrammarX objects that are exclusive """ @@ -1057,7 +1090,6 @@ def __init__(self, inifile_stem=None): # here the grammar is not loaded yet, but the ini file is present # this could have been done in the user grammar already... - self.debug = None # can be set in some grammar at initialize time #mod = sys.modules[self.__module__] ## version = getattr(mod, '__version__', '---') self.DNSVersion = status.getDNSVersion() @@ -1979,11 +2011,9 @@ def fillList(self, listName): # else: l = self.ini.get(n) if l: - #if self.debug: #print '%s: filling list %s with %s items'% (self.name, listName, len(l)) self.setList(n, l) return 1 - #if self.debug: #print '%s: not filling list %s, no items found'% (self.name, listName) self.emptyList(n) return None @@ -2848,17 +2878,14 @@ def getTopOrChild(self, progInfo=None, childClass=None): istop = False elif childClass and progInfo.classname == childClass: if actions.childWindowBehavesLikeTop( progInfo ): - if self.debug: - print('getTopOrChild: top mode, altough of class "%s", but because of "child behaves like top" in "actions.ini"'% childClass) + self.debug('getTopOrChild: top mode, altough of class "%s", but because of "child behaves like top" in "actions.ini"'% childClass) istop = True else: - if self.debug: - print('getTopOrChild: child mode, because of className "%s"'% childClass) + self.debug('getTopOrChild: child mode, because of className "%s"'% childClass) istop = False else: if actions.childWindowBehavesLikeTop( progInfo ): - if self.debug: - print('getTopOrChild: top mode, because but because of "child behaves like top" in "actions.ini"') + self.debug('getTopOrChild: top mode, because but because of "child behaves like top" in "actions.ini"') istop = True return istop From 1577ce340488d7e0b0134abd7b6616a463203ef0 Mon Sep 17 00:00:00 2001 From: Doug Ransom Date: Sun, 3 Mar 2024 12:00:31 -0800 Subject: [PATCH 24/26] natlinkutilsbj now has a mechansim for logging and for subclasses to create their own logger. --- src/unimacro/UnimacroGrammars/_folders.py | 2 +- src/unimacro/natlinkutilsbj.py | 35 ++++++++++++----------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index adef8d1..4793810 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -2075,7 +2075,7 @@ def getValidDirectory(self, f, remote): if os.path.isdir(tryF): return tryF fparts.pop(0) - self.info('_folders, no valid remote folder found for %s and remote: %s', (f, remote)) + self.info('_folders, no valid remote folder found for %s and remote: %s', f, remote) return '' def getValidFile(self, f, remote): diff --git a/src/unimacro/natlinkutilsbj.py b/src/unimacro/natlinkutilsbj.py index 521bd9d..e7c91bc 100644 --- a/src/unimacro/natlinkutilsbj.py +++ b/src/unimacro/natlinkutilsbj.py @@ -46,7 +46,7 @@ from pathlib import Path import win32com import logging - +from logging import Logger import natlink from natlinkcore import loader from natlinkcore import gramparser # for translation with GramScannerReverse @@ -314,27 +314,28 @@ def loggerShortName(self) ->str: return "unimacro" def getLogger(self) -> logging.Logger: - print(f"Get Logger , name is {self.loggerName()}") return logging.getLogger(self.loggerName()) - - #info,warning,error,exception,debug,log are forwarded the the appropriate logger.+ - - def info(self,msg,*args,**kwargs): - return self.getLogger().info(msg,*args,**kwargs) - def debug(self,msg,*args,**kwargs): - return self.getLogger().debug(msg,*args,**kwargs) - def warning(self,msg,*args,**kwargs): - return self.getLogger().warning(msg,*args,**kwargs) - def error(self,msg,*args,**kwargs): - return self.getLogger().error(msg,*args,**kwargs) - def exception(self,msg,*args,**kwargs): - return self.getLogger().error(msg,*args,**kwargs) - def log(self,level,msg,*args,**kwargs): - return self.getLogger().error(level,msg,*args,**kwargs) + #avoid copy and pasting methods that delegate to getLoger() + def wrapped_log(method): + """Delegates to {method} of a Logger object from self.getLogger()""" + def fn(self,*args,**kwargs): + logger=self.getLogger() + try: + return method(logger,*args,**kwargs) + except Exception as e: + print("Failure attempting to call {method} on {logger}, \nargs {args} \nkwargs {kwargs}\nException:\n{e}") + return False + + return fn + + #add methods to delegate calls to logger, so we wave info, warn, etc. + wrapped_logger=[Logger.info,Logger.setLevel,Logger.debug,Logger.warning,Logger.error,Logger.exception,Logger.critical,Logger.log] + for n in wrapped_logger: + locals()[n.__name__]=wrapped_log(n) def getExclusiveGrammars(self): From 2ea33002d0747f38169f4dbde0ffcb808917874d Mon Sep 17 00:00:00 2001 From: Doug Ransom Date: Sun, 10 Mar 2024 17:23:41 -0700 Subject: [PATCH 25/26] updated doc string for logging, rewrote Union & Intersect to use the set features now available. --- src/unimacro/natlinkutilsbj.py | 38 ++++++++++------------------------ 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/src/unimacro/natlinkutilsbj.py b/src/unimacro/natlinkutilsbj.py index e7c91bc..f95b12c 100644 --- a/src/unimacro/natlinkutilsbj.py +++ b/src/unimacro/natlinkutilsbj.py @@ -196,40 +196,21 @@ def letterUppercase(l): PythonServerExe=os.path.join(PythonwinPath, 'pserver1') # returns the union of two lists -def Union(L1,L2): +def Union(L1,L2) -> list: """old fashioned union function of two lists """ - if not isinstance(L1, list): - raise TypeError(f'function Union, first input variable not a list: {type(L1)}') - if not isinstance(L2, list): - raise TypeError(f'function Union, second input variable not a list: {type(L2)}') - L=L1[:] - for i in L2: - if not i in L1: - L.append(i) - return L + return list(set(L1).union(L2)) # returns the intersection of two lists def Intersect(L1,L2): """old fashioned intersection function of two lists """ - if not isinstance(L1, list): - raise TypeError(f'function Intersect, first input variable not a list: {type(L1)}') - if not isinstance(L2, list): - raise TypeError(f'function Intersect, second input variable not a list: {type(L2)}') - L=[] - for i in L2: - if i in L1: - L.append(i) - return L - -def reverseDict(Dict): + return set(L1).intersection(L2) + +def reverseDict(Dict : dict): """reverse a dict, ignoring multiple values of the original """ - revDict={} - for key in list(Dict.keys()): - revDict[Dict[key]]=key - return revDict + return {val: key for (key, val) in Dict.items()} def joinNestedStringLists(l): """nested lists of strings are "flattened" into one string @@ -321,9 +302,12 @@ def getLogger(self) -> logging.Logger: #avoid copy and pasting methods that delegate to getLoger() def wrapped_log(method): - """Delegates to {method} of a Logger object from self.getLogger()""" + """Creates a function that delegates to the 'method' of the Logger object from self.getLogger()""" def fn(self,*args,**kwargs): - logger=self.getLogger() + f""" + Forwards {method} to the Logger Object from self.getLogger(). + """ + logger : Logger=self.getLogger() try: return method(logger,*args,**kwargs) except Exception as e: From 9eb122d678b95cbb65ac7b163196eb41723a3fef Mon Sep 17 00:00:00 2001 From: Doug Ransom Date: Wed, 7 Aug 2024 10:14:05 -0700 Subject: [PATCH 26/26] Union, intersect take advantage of built ins now availalbe. Tweaks around logging. --- src/unimacro/UnimacroGrammars/_folders.py | 1 + src/unimacro/natlinkutilsbj.py | 19 +++++++++++--- .../sample_global_dictation/_control.py | 25 ++++++++++--------- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index 4793810..22c815f 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -231,6 +231,7 @@ def gotResultsInit(self,words,fullResults): self.progInfo = unimacroutils.getProgInfo() + def handleTrackFilesAndFolders(self, activeFolder): """set or empty lists for activeFolder and set/reset self.activeFolder """ diff --git a/src/unimacro/natlinkutilsbj.py b/src/unimacro/natlinkutilsbj.py index f95b12c..b1bee29 100644 --- a/src/unimacro/natlinkutilsbj.py +++ b/src/unimacro/natlinkutilsbj.py @@ -317,11 +317,22 @@ def fn(self,*args,**kwargs): return fn #add methods to delegate calls to logger, so we wave info, warn, etc. - wrapped_logger=[Logger.info,Logger.setLevel,Logger.debug,Logger.warning,Logger.error,Logger.exception,Logger.critical,Logger.log] - for n in wrapped_logger: - locals()[n.__name__]=wrapped_log(n) - + #this would be the better way to do it, but we haven't found a way to get code completion + #wrapped_logger=[Logger.info,Logger.setLevel,Logger.debug,Logger.warning,Logger.error,Logger.exception,Logger.critical,Logger.log] + #for n in wrapped_logger: + # locals()[n.__name__]=wrapped_log(n) + #instead, copy and paste. + + info=wrapped_log(Logger.info) + setLevel=wrapped_log(Logger.setLevel) + debug=wrapped_log(Logger.debug) + warning=wrapped_log(Logger.warning) + error=wrapped_log(Logger.error) + exception=wrapped_log(Logger.exception) + critical=wrapped_log(Logger.critical) + log=wrapped_log(Logger.log) + def getExclusiveGrammars(self): """return the dict of (name, grammarobject) of GrammarX objects that are exclusive """ diff --git a/src/unimacro/sample_global_dictation/_control.py b/src/unimacro/sample_global_dictation/_control.py index 0dfd2e0..a105fac 100644 --- a/src/unimacro/sample_global_dictation/_control.py +++ b/src/unimacro/sample_global_dictation/_control.py @@ -173,7 +173,7 @@ def gotBegin(self, moduleInfo): def gotResultsInit(self,words,fullResults): if self.mayBeSwitchedOn == 'exclusive': - print('recog controle, switch off mic: %s'% words) + self.warning('recog controle, switch off mic: %s', words) natbj.SetMic('off') if self.exclusive and self.doMessages: self.DisplayMessage('<%s>'% ' '.join(words)) @@ -227,7 +227,7 @@ def restoreMode(self): self.Mode = self.LastMode def gotResults_trace(self,words,fullResults): - print('control, trace: %s'% words) + self.info('control, trace: %s', words) if self.hasCommon(words, 'actions'): if self.hasCommon(words, 'show'): actions.debugActionsShow() @@ -250,15 +250,16 @@ def gotResults_voicecode(self,words,fullResults): 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) + self.debug("commandLine : %s",commandLine) os.system(commandLine) else: - print('not a file: %s'% wxmed) + self.info('not a file: %s',wxmed) def gotResults_switch(self,words,fullResults): - print('control, switch: %s'% words) + self.info('control, switch: %s', words) if self.hasCommon(words, 'on'): func = 'switchOn' elif self.hasCommon(words, 'off'): @@ -272,16 +273,16 @@ def gotResults_switch(self,words,fullResults): self.DisplayMessage(t) return if self.hasCommon(words, 'all grammars'): - print('%s all grammars:'% func) + self.info('%s all grammars:', func) natbj.CallAllGrammarObjects(func, ()) - print("-"*10) + self.info("-"*10) else: gramname = self.hasCommon(words, list(natbj.allUnimacroGrammars.keys())) if gramname: gram = natbj.allUnimacroGrammars[gramname] gram.callIfExists(func, ()) else: - print('no grammar name found: %s'% gramname) + self.warning('no grammar name found: %s'% gramname) def gotResults_showexclusive(self,words,fullResults): if natbj.exclusiveGrammars: @@ -335,7 +336,7 @@ def gotResults_show(self,words,fullResults): return if natbj.exclusiveGrammars: - print('exclusive (+ control) are: %s'% ' '.join(list(natbj.exclusiveGrammars.keys()))) + self.info('exclusive (+ control) are: %s', ' '.join(list(natbj.exclusiveGrammars.keys()))) grammars = natbj.allUnimacroGrammars gramNames = list(grammars.keys()) @@ -379,7 +380,7 @@ def gotResults_show(self,words,fullResults): Start=(' '.join(name),[]) else: Start=() - print('start browsing with: %s'% All) + self.info('start browsing with: %s', All) self.Browse(Start,All) @@ -410,7 +411,7 @@ def gotResults_edit(self,words,fullResults): self.DisplayMessage('grammar "%s" has no method "editInifile"'% gramName) return else: - print('no grammar name found') + self.info('no grammar name found') def switchOff(self, **kw): @@ -454,7 +455,7 @@ def __init__(self): natlinkutils.DictGramBase.__init__(self) def initialize(self): - print('initializing/loading DictGrammar!!') + self.info('initializing/loading DictGrammar!!') self.load() natbj.RegisterMessageObject(self) @@ -464,7 +465,7 @@ def unload(self): def gotResults(self, words): ## pass - print('messageDictGrammar: heard dictation: %s '% words) + self.info('messageDictGrammar: heard dictation: %s ', words) # standard stuff Joel (adapted for possible empty gramSpec, QH, unimacro)