diff --git a/pyproject.toml.orig b/pyproject.toml.orig new file mode 100644 index 0000000..f282a2e --- /dev/null +++ b/pyproject.toml.orig @@ -0,0 +1,71 @@ +[build-system] +requires = ["flit_core >=2,<4"] +build-backend = "flit_core.buildapi" + +[project] +name = "unimacro" +authors = [{name = "Quintijn Hoogenboom (maintainer)", email = "q.hoogenboom@antenna.nl"}] +dynamic = ["version", "description"] +requires-python = ">=3.10" +readme = "README.md" + + +dependencies = ["dtactions >= 1.5.7", + "debugpy >= 1.2.0", + "pywin32 >= 304", + "natlinkcore >= 5.3.7", + "pysimplegui"] + +classifiers=[ "Development Status :: 4 - Beta", + "Topic :: Multimedia :: Sound/Audio :: Speech", + "Topic :: Scientific/Engineering :: Human Machine Interfaces", + "Environment :: Win32 (MS Windows)", + "Intended Audience :: Developers", + "Operating System :: Microsoft :: Windows", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Multimedia :: Sound/Audio :: Speech", + "Topic :: Software Development :: Libraries :: Python Modules", + ] + +[project.optional-dependencies] +test = [ + "pytest >=7.1.2","flake8" +] +dev = [ + "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" + folders="unimacro:folders_logger_name" + +[tool.pytest.ini_options] +minversion = "7.1.2" +addopts = "--capture=tee-sys " +# very important +#the pythonpath lets pytest load code in your source area +#in addition to that in site-packages etc. +#you may want to run your tests without install natlinkcore with flit or pip +pythonpath = [ +] +testpaths= [ + "tests", + "src/unimacro/unimacro_test" +] +python_files = [ + "unittest*.py", + "test_*.py", + ] + +[project.scripts] + +[project.urls] +homepage = "https://qh.antenna.nl/unimacro/" +repository="https://github.com/dictation-toolbox/unimacro" +source="https://github.com/dictation-toolbox/unimacro" diff --git a/src/unimacro/UnimacroGrammars/_folders.py b/src/unimacro/UnimacroGrammars/_folders.py index b11c01d..0c6acb8 100644 --- a/src/unimacro/UnimacroGrammars/_folders.py +++ b/src/unimacro/UnimacroGrammars/_folders.py @@ -78,7 +78,6 @@ # manipulating file names with env variables etc... envvars = extenvvars.ExtEnvVars() - thisDir = str(Path(__file__).parent) status = natlinkstatus.NatlinkStatus() # for getting unicode explorer window titles: @@ -236,6 +235,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 """ @@ -2467,6 +2467,8 @@ def unload(): active_folder = thisGrammar.getActiveFolder(198434) print(f'active_folder: {active_folder}') thisGrammar.displayRecentFolders() + # get hndle of a explore window (via _general "give window info") and try interactive + # thisGrammar.catchTimerRecentFolders(132524, "CabinetWClass") # # Words = ['folder', 'dtactions'] # Fr = {} diff --git a/src/unimacro/UnimacroGrammars/rememberdialog.py b/src/unimacro/UnimacroGrammars/rememberdialog.py new file mode 100644 index 0000000..2f7c7b6 --- /dev/null +++ b/src/unimacro/UnimacroGrammars/rememberdialog.py @@ -0,0 +1,51 @@ +"""template for _folders grammar, remember function. +This template is filled with the actual values and fired. It asks for a spoken form to "remember a given file", this value +is put in the _folders.ini config file. +""" +#pylint:disable=W0621 +import time +import PySimpleGUI as sg +from dtactions.unimacro import inivars + +prompt = """Remember in Unimacro _folders grammar""" # readable text +text = """Remember folder "C:/Users/Gebruiker/Music" for future calling? + +Please give a spoken form for this folder and choose OK; or Cancel...""" # input text, the key of the +inifile = "C:/Users/Gebruiker/Documents/unimacro_clean_start/enx_inifiles/_folders.ini" +section = "folders" +value = "C:/Users/Gebruiker/Music" +title = "test" +default = "Music" +pausetime = 3 # should be replaced by 0 or a positive int value + + + +def InputBox(text, prompt, title, default): + """the dialog, which returns the wanted spoken form""" + layout = [[sg.Text(prompt)], + [sg.InputText(default)], + [sg.OK(), sg.Cancel()]] + + window = sg.Window(title, layout) + + _event, values = window.read() + window.close() + + return values[0] + +if __name__ == "__main__": + result = InputBox(text, prompt, title, default) + if result: + key = result + ini = inivars.IniVars(inifile) + ini.set(section, key, value) + ini.write() + print(f'Wrote "{key} = {value}" to inifile') + print('Call "edit folders" to edit or delete') + else: + print("Action canceled, no change of ini file") + # if pausetime is given, launch as .py, otherwise launch as .pyw: + if pausetime: + # print "sleep: %s"% pausetime + time.sleep(pausetime) + \ No newline at end of file diff --git a/src/unimacro/__init__.py b/src/unimacro/__init__.py index 47626c3..d61c19f 100644 --- a/src/unimacro/__init__.py +++ b/src/unimacro/__init__.py @@ -8,7 +8,7 @@ import os import sys -#these functions are in this module so that they can be loaded without loading a lot of unimacro code. +__version__ = '4.1.6' # requires the dtactions 1.5.5, with new sendkeys (from send_input, vocola) #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. @@ -23,5 +23,3 @@ def logname() -> str: return "natlink.unimacro" __version__ = '4.1.7' - - diff --git a/src/unimacro/__init__.py.orig b/src/unimacro/__init__.py.orig new file mode 100644 index 0000000..54a825c --- /dev/null +++ b/src/unimacro/__init__.py.orig @@ -0,0 +1,29 @@ +"""Unimacro __init__ + + +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. + +""" +import os +import sys + +<<<<<<< HEAD +__version__ = '4.1.2' # requires the dtactions 1.5.5, with new sendkeys (from send_input, vocola) +======= +#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" + +def logname() -> str: + """ Returns the name of the unimacro logger.""" + return "natlink.unimacro" +__version__ = '4.1.5' +>>>>>>> a1a5c814781da86d8d969a35b822123c35dce778 + diff --git a/src/unimacro/_control.py b/src/unimacro/_control.py index d9556e8..482205c 100644 --- a/src/unimacro/_control.py +++ b/src/unimacro/_control.py @@ -50,6 +50,7 @@ ulogger.debug("natlink.unimacro logger available") status = natlinkstatus.NatlinkStatus() natlinkmain = loader.NatlinkMain() +##control_logger=l.getLogger(unimacro_l.control_logger_name()) @@ -441,6 +442,7 @@ def gotResults_show(self,words,fullResults): self.gotResults_showexclusive(words, fullResults) return if self.hasCommon(words,"loggers"): + self.info(f"Available Loggers: {self.loggers}") L = ['\nAvailable Loggers apart from the root (natlink) logger:'] for key, loggerid in self.loggers.items(): logger=l.getLogger(loggerid) diff --git a/src/unimacro/natlinkutilsbj.py b/src/unimacro/natlinkutilsbj.py index 19cd8ab..3eeca94 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 @@ -333,11 +314,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)