FAQ | This is a LIVE service | Changelog

Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • ssb22/gradint
  • st822/gradint
2 results
Show changes
Showing
with 777 additions and 91 deletions
# This file is part of the source code of
# gradint v0.9974 (c) 2002-2011 Silas S. Brown. GPL v3+.
# This file is part of the source code of Gradint
# (c) Silas S. Brown.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
......@@ -63,40 +63,48 @@ if winsound:
try: ctypes.cdll.commdlg
except: WMstandard = True
if macsound and __name__=="__main__": os.system("clear 1>&2") # so warnings etc start with a clear terminal (1>&2 just in case using stdout for something else)
if macsound and __name__=="__main__": os.system("clear >&2") # so warnings etc start with a clear terminal (>&2 just in case using stdout for something else)
if riscos_sound: sys.stderr.write("Loading Gradint...\n") # in case it takes a while
wsp = '\t\n\x0b\x0c\r ' # whitespace characters - ALWAYS use .strip(wsp) not .strip(), because someone added \xa0 (iso8859-1 no-break space) to string.whitespace on WinCE Python, and that can break processing of un-decoded UTF8 strings, e.g. a Chinese phrase ending "\xe5\x86\xa0"! (and assign to string.whitespace does not work around this.)
try: import androidhelper as android
except:
try: import android
except: android = 0
if android:
try: android = android.Android()
except:
print ("\n"*50+" *** Your SL4A server has crashed ***\n Please restart SL4A\n (or restart your phone)\n and try running Gradint again.\n\n\n")
raise SystemExit
wsp = '\t\n\x0b\x0c\r ' ; bwsp=B(wsp) # whitespace characters - ALWAYS use .strip(wsp) not .strip(), because someone added \xa0 (iso8859-1 no-break space) to string.whitespace on WinCE Python, and that can break processing of un-decoded UTF8 strings, e.g. a Chinese phrase ending "\xe5\x86\xa0"! (and assign to string.whitespace does not work around this.)
# As .split() can't take alternative characters (and re-writing in Python is probably slow), just be careful with using it on un-decoded utf-8 stuff. (split(None,1) is ok if 1st word won't end in an affected character)
warnings_printed = [] ; app = None
warnings_printed = [] ; app = False # False is a hack for "maybe later"
warnings_toprint = []
def show_warning(w):
if not app and not appuifw:
if w+"\n" in warnings_printed: return
if not app and not app==False and not appuifw and not android:
if winCEsound and len(w)>100: w=w[:100]+"..." # otherwise can hang winCEsound's console (e.g. a long "assuming that" message from justSynthesize)
sys.stderr.write(w+"\n")
warnings_printed.append(w+"\n")
if app==False: warnings_toprint.append(w) # may need to output them if app/appuifw/android turns out not to be created
def show_info(i,always_stderr=False):
# == sys.stderr.write(i) with no \n and no error if closed (+ redirect to app or appuifw if exists)
if (app or appuifw) and not always_stderr: return doLabel(i)
if not always_stderr and hasattr(sys.stderr,"isatty") and not sys.stderr.isatty(): return # be quiet if o/p is being captured by cron etc
if (app or appuifw or android) and not always_stderr: return doLabel(i)
if not riscos_sound and not always_stderr and hasattr(sys.stderr,"isatty") and not sys.stderr.isatty(): return # be quiet if o/p is being captured by cron etc (but isatty() might always return false on RISC OS
if winCEsound and len(i)>101: i=i[:100]+"..."+i[-1] # otherwise can hang winCEsound's console
if type(i)==type(u""): i=i.encode('utf-8')
try: sys.stderr.write(i)
try: writeB(sys.stderr,i)
except IOError: pass
# For pre-2.3 versions of Python (e.g. 2.2 on Symbian S60 and Mac OS 10.3):
try: True
except: exec("True = 1 ; False = 0")
# TODO make sure to avoid writing "string1 in string2" without thinking - if string1 is multiple characters it won't work on pre-2.3
# TODO check all lambda functions for Python2.2 compatibility
# (TODO: GUI_translations, if not set in advanced.txt, won't work properly on pre-2.3 - it'll take them as Latin-1)
# (TODO: and if it *IS* set in advanced.txt, will 2.2's exec() correctly exec a unicode string?)
# Check if we're on big-endian architecture (relevant to sox etc)
try: import struct
except: struct=0
if struct and struct.pack("h",1)[0]=='\x00': big_endian = 1
if struct and B(struct.pack("h",1)[0])==B('\x00'): big_endian = 1
else: big_endian = 0
# RISC OS has a different extension separator because "." is used as a directory separator (from the original 1982 BBC Micro DFS with 1-character directories)
......@@ -114,6 +122,14 @@ def list2dict(l):
return d
try: list2set = set
except NameError: list2set = list2dict
def checkIn(k,obj):
try: return k in obj # dict or set
except:
try: return obj.has_key(k) # Python 2.1 (may raise TypeError, AttributeError etc if try to use the "in" operator as above, but has_key rm'd from Python3)
except: return obj.find(k) > -1 # Python 2.1 strings
try: object
except:
class object: pass # Python 2.1
# settings.txt and advanced.txt
# (done here before the variables start to be used in
......@@ -162,16 +178,23 @@ if use_unicode_filenames:
def u8strip(d):
global last_u8strip_found_BOM ; last_u8strip_found_BOM = 0
if d.startswith('\xef\xbb\xbf'):
d = B(d)
if d.startswith(LB('\xef\xbb\xbf')):
last_u8strip_found_BOM = 1
return d[3:] # ignore Notepad's UTF-8 BOM's
else: return d
def bwspstrip(s):
try: return s.strip(bwsp)
except: return s.strip() # Python 2.1
def wspstrip(s):
try: return s.strip(wsp)
except: return s.strip() # Python 2.1
GUI_translations_old = GUI_translations
configFiles = map(lambda x:x+dottxt,["advanced","settings"]) # MUST have settings last so can have per-user override of scriptVariants
if not hasattr(sys,"argv"): sys.argv=" " # some Symbian versions
starting_directory = os.getcwd()
if not fileExists(configFiles[0]):
if macsound and "_" in os.environ:
if macsound and checkIn("_",os.environ):
s=os.environ["_"] ; s=s[:s.rfind(os.sep)]
os.chdir(s)
if not fileExists(configFiles[0]):
......@@ -180,9 +203,14 @@ if not fileExists(configFiles[0]):
os.chdir(s)
if not fileExists(configFiles[0]) and sys.argv and (os.sep in sys.argv[0] or (os.sep=='\\' and '/' in sys.argv[0])):
# try the sys.argv[0] directory, in case THAT works
if os.sep=="\\" and '/' in sys.argv[0] and fileExists(sys.argv[0].replace('/','\\')): sys.argv[0]=sys.argv[0].replace('/','\\') # hack for some Windows Python builds accepting / in command line but reporting os.sep as \
if os.sep=="\\" and '/' in sys.argv[0] and fileExists(sys.argv[0].replace('/','\\')): sys.argv[0]=sys.argv[0].replace('/','\\') # hack for some Windows Python builds accepting slash in command line but reporting os.sep as backslash
os.chdir(starting_directory)
os.chdir(sys.argv[0][:sys.argv[0].rfind(os.sep)])
if not fileExists(configFiles[0]): # argv[0] might be a symlink
os.chdir(starting_directory)
try: rp = os.path.realpath(sys.argv[0])
except: rp = 0 # e.g. no os.path, or no os.path.realpath
if rp: os.chdir(rp[:rp.rfind(os.sep)])
if not fileExists(configFiles[0]):
# Finally, try the module pathname, in case some other Python program has imported us without changing directory. Apparently we need to get this from an exception.
try: raise 0
......@@ -196,7 +224,7 @@ if not fileExists(configFiles[0]):
# directory should be OK by now
if sys.platform.find("ymbian")>-1: sys.path.insert(0,os.getcwd()+os.sep+"lib")
import time,sched,sndhdr,random,math,pprint,codecs
import time,sched,random,math,pprint,codecs
def exc_info(inGradint=True):
import sys # in case it's been gc'd
......@@ -208,32 +236,36 @@ def exc_info(inGradint=True):
while tbObj and hasattr(tbObj,"tb_next") and tbObj.tb_next: tbObj=tbObj.tb_next
if tbObj and hasattr(tbObj,"tb_lineno"): w += (" at line "+str(tbObj.tb_lineno))
if inGradint:
if tbObj and hasattr(tbObj,"tb_frame") and hasattr(tbObj.tb_frame,"f_code") and hasattr(tbObj.tb_frame.f_code,"co_filename") and not tbObj.tb_frame.f_code.co_filename.find("gradint"+extsep+"py")>-1: w += (" in "+tbObj.tb_frame.f_code.co_filename+"\n")
else: w += (" in "+program_name[:program_name.index("(c)")]+"\n")
if tbObj and hasattr(tbObj,"tb_frame") and hasattr(tbObj.tb_frame,"f_code") and hasattr(tbObj.tb_frame.f_code,"co_filename") and not tbObj.tb_frame.f_code.co_filename.find("gradint"+extsep+"py")>=0: w += (" in "+tbObj.tb_frame.f_code.co_filename)
else: w += (" in "+program_name[:program_name.index("(c)")])
w += " on Python "+sys.version.split()[0]+"\n"
del tbObj
return w
def read(fname): return open(fname,"rb").read()
def write(fname,data): open(fname,"wb").write(data)
def readSettings(f):
try: fdat = u8strip(read(f)).replace("\r","\n")
try: fdat = u8strip(read(f)).replace(B("\r"),B("\n"))
except: return show_warning("Warning: Could not load "+f)
try: fdat = unicode(fdat,"utf-8")
except: return show_warning("Problem decoding utf-8 in "+f)
try: exec(fdat) in globals()
try: exec(fdat,globals())
except: show_warning("Error in "+f+" ("+exc_info(False)+")")
dir1 = list2set(dir()+["dir1","f","last_u8strip_found_BOM"])
synth_priorities = "eSpeak MacOS SAPI Ekho" # old advanced.txt had this instead of prefer_espeak; we can still support it
dir1 = list2set(dir()+["dir1","f","last_u8strip_found_BOM","__warningregistry__"])
for f in configFiles: readSettings(f)
for d in dir():
if not d in dir1 and eval(d) and not type(eval(d))==type(lambda *args:0): # (ignore unrecognised options that evaluate false - these might be an OLD unused option with a newer gradint rather than vice versa; also ignore functions as these could be used in command-line parameters)
if not checkIn(d,dir1) and eval(d) and not type(eval(d))==type(lambda *args:0): # (ignore unrecognised options that evaluate false - these might be an OLD unused option with a newer gradint rather than vice versa; also ignore functions as these could be used in command-line parameters)
show_warning("Warning: Unrecognised option in config files: "+d)
del dir1
GUI_translations_old.update(GUI_translations) ; GUI_translations = GUI_translations_old # in case more have been added since advanced.txt last update
def cond(a,b,c):
# Python 2.4 can inline "b if a else c" but Python 2.3 can't
if a: return b
else: return c
unix = not (winsound or mingw32 or riscos_sound or appuifw or winCEsound)
unix = not (winsound or mingw32 or riscos_sound or appuifw or android or winCEsound)
if unix: os.environ["PATH"] = os.environ.get("PATH","/usr/local/bin:/usr/bin:/bin")+cond(macsound,":"+os.getcwd()+"/start-gradint.app:",":")+os.getcwd() # for qtplay and sox, which may be in current directory or may be in start-gradint.app if it's been installed that way, and for lame etc. Note we're specifying a default PATH because very occasionally it's not set at all when using 'ssh system command' (some versions of DropBear?)
# Any options in the environment?
......@@ -252,24 +284,44 @@ if paranoid_file_management:
# For ftpfs etc. Retry on errno 13 (permission denied), and turn append into a copy. Otherwise occasionally get vocab.txt truncated.
_old_open = open
def tryIO(func):
for tries in range(10)+["last"]:
for tries in list(range(10))+["last"]:
try: return func()
except IOError,err:
if tries=="last" or not err.errno in [5,13]: raise
except IOError:
err = sys.exc_info()[1]
if tries=="last" or not err.errno in [5,13,None]: raise
time.sleep(0.5)
def read(file): return tryIO(lambda x=file:_old_open(x,"rb").read())
def open(file,mode="r"):
def _write(fn,data):
tryIO(lambda x=fn,y=data:_old_open(x,"wb").write(y))
time.sleep(0.5)
if not filelen(fn)==len(data):
# might be a version of curlftpfs that can't shorten files - try delete and restart (although this can erase permissions info)
os.remove(fn)
tryIO(lambda x=fn,y=data:_old_open(x,"wb").write(y))
if not filelen(fn)==len(data): raise IOError("wrong length")
if not read(fn)==data: raise IOError("verification failure on "+repr(fn))
def write(fn,data): return tryIO(lambda x=fn,y=data:_write(x,y))
def open(file,mode="r",forAppend=0):
if "a" in mode:
try: dat = open(file,mode.replace("a","r")).read()
except IOError,err:
try: dat = open(file,"rb").read()
except IOError:
err = sys.exc_info()[1]
if err.errno==2: dat = "" # no such file or directory
else: raise
if len(dat) < filelen(file): raise IOError("short read")
try: os.rename(file,file+"~") # just in case!
except: pass
o=open(file,mode.replace("a","w"))
o=open(file,"wb",1)
o.write(dat)
return o
return tryIO(lambda x=file,m=mode:_old_open(x,m))
r=tryIO(lambda x=file,m=mode:_old_open(x,m))
if "w" in mode and not forAppend and filelen(file): # it's not truncating (see _write above)
r.close()
os.unlink(file)
r=tryIO(lambda x=file,m=mode:_old_open(x,m))
return r
if seedless: random.seed(0)
# Different extension separators again
if not extsep==".":
......@@ -291,11 +343,11 @@ for p in [progressFile,progressFileBackup,pickledProgressFile]:
if extsep in p[1]: p=(p[0],p[1][:p[1].rfind(extsep)]) # here rather than earlier to cover cases where extsep is in a directory name but not in the filename
if oldDir==None: oldDir=p
elif not oldDir==p:
sys.stderr.write("ERROR: progressFile, progressFileBackup and pickledProgressFile, if not None, must have same directory and major part of filename. Gradint will not run otherwise. This sanity-check was added in case some script sets progressFile to something special but forgets to set the others.\n")
sys.stderr.write("ERROR: progressFile, progressFileBackup and pickledProgressFile, if not None, must have same directory and major part of filename. Gradint will not run otherwise. This coherence check was added in case some script sets progressFile to something special but forgets to set the others.\n")
sys.exit(1)
# Check for RISC OS pre-1970 clock problem (actually quite likely if testing on the rpcemu emulator without setting the clock)
if riscos_sound and hex(int(time.time())).find("0xFFFFFFFF")>-1 and not outputFile:
if riscos_sound and hex(int(time.time())).find("0xFFFFFFFF")>=0 and not outputFile:
sys.stderr.write("ERROR: time.time() is not usable - gradint cannot run interactively.\n")
sys.stderr.write("This error can be caused by the RISC OS clock being at 1900 (the Unix time functions start at 1970).\nClose this task window, set the clock and try again.\n")
sys.exit()
......@@ -318,7 +370,7 @@ Tk_might_display_wrong_hanzi = wrong_hanzi_message = "" ; forceRadio=0
if macsound:
try: os.remove("_tkinter.so") # it might be an old patched version for the wrong OS version
except: pass
def tkpatch(): # patch Mac OS Tk to the included v8.6 (as v8.4 on OS10.5 has hanzi problem and v8.5 on 10.6 has fontsize problems etc)
def tkpatch(): # (called only on specific older versions of Mac OS X) patch Mac OS Tk to the included v8.6 (as v8.4 on OS10.5 has hanzi problem and v8.5 on 10.6 has fontsize problems etc)
f="/System/Library/Frameworks/Python.framework/Versions/"+sys.version[:3]+"/lib/python"+sys.version[:3]+"/lib-dynload/_tkinter.so"
if fileExists(f): # we might be able to patch this one up
if not isDirectory("Frameworks") and fileExists("Frameworks.tbz"): os.system("tar -jxvf Frameworks.tbz && rm Frameworks.tbz && chmod -R +w Frameworks")
......@@ -333,6 +385,7 @@ if macsound:
elif sys.version[:5] == "2.5.1": # 10.5
if not tkpatch(): Tk_might_display_wrong_hanzi="10.5"
elif sys.version[:5] == "2.6.1": tkpatch() # 10.6 (still has Tk8.5, hanzi ok but other problems)
elif sys.version[:5] == "2.7.5": tkpatch() # 10.9 (problems with "big print" button if don't do this)
if Tk_might_display_wrong_hanzi: wrong_hanzi_message = "NB: In Mac OS "+Tk_might_display_wrong_hanzi+", Chinese\ncan display wrongly here." # so they don't panic when it does
# Handle keeping progress file and temp directories etc if we're running from a live CD
......@@ -348,8 +401,8 @@ def progressFileOK():
except: return 0
if winsound: # will try these dirs in reverse order:
tryList = ["C:\\TEMP\\gradint-progress.txt", "C:\\gradint-progress.txt", "C:gradint-progress.txt"]
if "HOMEDRIVE" in os.environ and "HOMEPATH" in os.environ: tryList.append(os.environ["HOMEDRIVE"]+os.environ["HOMEPATH"]+os.sep+"gradint-progress.txt")
elif "HOME" in os.environ: tryList=[os.environ["HOME"]+os.sep+"gradint-progress.txt"]
if checkIn("HOMEDRIVE",os.environ) and checkIn("HOMEPATH",os.environ): tryList.append(os.environ["HOMEDRIVE"]+os.environ["HOMEPATH"]+os.sep+"gradint-progress.txt")
elif checkIn("HOME",os.environ): tryList=[os.environ["HOME"]+os.sep+"gradint-progress.txt"]
elif riscos_sound: tryList=["$.gradint-progress/txt"]
else: tryList = []
foundPF = okPF = 0 ; defaultProgFile = progressFile
......@@ -367,10 +420,11 @@ if need_say_where_put_progress:
progressFileBackup = progressFile[:-3]+"bak"
pickledProgressFile = progressFile[:-3]+"bin"
logFile = None # for now
if winsound or winCEsound or mingw32 or riscos_sound or not hasattr(os,"tempnam"):
tempdir_is_curdir = False
if winsound or winCEsound or mingw32 or riscos_sound or not hasattr(os,"tempnam") or android:
tempnam_no = 0
if os.sep in progressFile: tmpPrefix=progressFile[:progressFile.rindex(os.sep)+1]+"gradint-tempfile"
else: tmpPrefix="gradint-tempfile"
else: tmpPrefix,tempdir_is_curdir="gradint-tempfile",True
if winCEsound or ((winsound or mingw32) and not os.sep in tmpPrefix and not tmpPrefix.startswith("C:")):
# put temp files in the current directory, EXCEPT if the current directory contains non-ASCII characters then check C:\TEMP and C:\ first (just in case the non-ASCII characters create problems for command lines etc; gradint *should* be able to cope but it's not possible to test in advance on *everybody's* localised system so best be on the safe side). TODO check for quotes etc in pathnames too.
def isAscii():
......@@ -385,33 +439,40 @@ if winsound or winCEsound or mingw32 or riscos_sound or not hasattr(os,"tempnam"
open(t+"gradint-tempfile-test","w")
os.unlink(t+"gradint-tempfile-test")
except: continue
tmpPrefix = t ; break
tmpPrefix,tempdir_is_curdir = t,False ; break
if not tmpPrefix: tmpPrefix = os.getcwd()+os.sep
tmpPrefix += "gradint-tempfile"
def tempnam():
global tempnam_no ; tempnam_no += 1
return tmpPrefix+str(tempnam_no)
os.tempnam = os.tmpnam = tempnam
elif (macsound or sys.platform.lower().find("bsd")>0) and os.environ.get("TMPDIR",""): # BSD tempnam uses P_tmpdir instead, override
tempnam0 = os.tempnam
os.tempnam=lambda *args:tempnam0(os.environ["TMPDIR"])
if disable_once_per_day==1:
if once_per_day==3: sys.exit()
else: once_per_day=0
if once_per_day&2 and not hasattr(sys,"_gradint_innerImport"): # run every day
currentDay = None
# markerFile logic to avoid 2 background copies etc (can't rely on taskkill beyond WinXP)
# (however this doesn't protect against uninstall + immediate reinstall)
markerFile,toDel="background1"+dottxt,"background2"+dottxt
if fileExists(markerFile): markerFile,toDel=toDel,markerFile
try: os.remove(toDel)
except OSError: pass
open(markerFile,"w").write("(delete this file to make the background process quit on next check)\n")
while fileExists(markerFile):
# markerFile logic to avoid 2+ background copies (can't rely on taskkill beyond WinXP)
myID = str(time.time())
try: myID += str(os.getpid())
except: pass
markerFile="background"+dottxt
open(markerFile,"w").write(myID)
def reador0(f):
try: return read(f)
except: return 0
while reador0(markerFile)==myID:
if not currentDay == time.localtime()[:3]: # first run of day
currentDay = time.localtime()[:3]
if __name__=="__main__": # can do it by importing gradint
sys._gradint_innerImport = 1
try:
try: reload(gradint)
except NameError: import gradint
gradint.orig_onceperday = once_per_day
gradint.main()
try: reload(gradint)
except NameError: import gradint
gradint.orig_onceperday = once_per_day
try: gradint.main()
except SystemExit: pass
elif winsound and fileExists("gradint-wrapper.exe"): # in this setup we can do it by recursively calling gradint-wrapper.exe
s=" ".join(sys.argv[1:])
......@@ -424,6 +485,8 @@ if once_per_day&2 and not hasattr(sys,"_gradint_innerImport"): # run every day
show_warning("Not doing once_per_day&2 logic because not running as main program")
# (DO need to be able to re-init the module - they might change advanced.txt etc)
break
if len(sys.argv)>1: sys.argv.append(";")
sys.argv.append("disable_once_per_day=0") # don't let a disable_once_per_day=2 in argv result in repeated questioning
time.sleep(3600) # delay 1 hour at a time (in case hibernated)
if once_per_day&1 and fileExists(progressFile) and time.localtime(os.stat(progressFile).st_mtime)[:3]==time.localtime()[:3]: sys.exit() # already run today
try: orig_onceperday
......@@ -432,9 +495,9 @@ except: orig_onceperday=0
if winsound:
# check for users putting support files/folders in the desktop shortcuts folder and thinking it's the gradint folder
# We can't do much about detecting users on non-English Windows who have heeded the warning about moving the "Desktop" folder to the real desktop but then mistook this for the gradint folder when adding flite (but hopefully they'll be using ptts/espeak anyway, and yali has an installer)
if "HOMEDRIVE" in os.environ and "HOMEPATH" in os.environ: dr=os.environ["HOMEDRIVE"]+os.environ["HOMEPATH"]
if checkIn("HOMEDRIVE",os.environ) and checkIn("HOMEPATH",os.environ): dr=os.environ["HOMEDRIVE"]+os.environ["HOMEPATH"]
else: dr="C:\\Program Files" # as setup.bat (location for gradint on Win95 etc)
if "USERPROFILE" in os.environ: dr=os.environ["USERPROFILE"]
if checkIn("USERPROFILE",os.environ): dr=os.environ["USERPROFILE"]
if not dr[-1]=="\\": dr += "\\"
try: dirList = os.listdir(dr+"Desktop\\gradint\\") # trailing \ important, otherwise it can include gradint.zip etc on Desktop
except: dirList = []
......@@ -450,11 +513,42 @@ elif macsound:
os.system('open ../Gradint.app')
sys.exit(0)
elif fileExists_stat("../Gradint 2.app/deleteme"):
import thread ; thread.start_new_thread(lambda *x:(time.sleep(2),os.system('rm -rf "../Gradint 2.app"')),())
try: import thread
except ImportError: import _thread as thread
thread.start_new_thread(lambda *x:(time.sleep(2),os.system('rm -rf "../Gradint 2.app"')),())
def got_program(prog):
# Test to see if the program 'prog' is on the system, as portable as possible. NB some Unix 'which' output an error to stdout instead of stderr, so check the result exists.
return (winsound and fileExists(prog+".exe")) or (unix and fileExists_stat(os.popen("which "+prog+" 2>/dev/null").read().strip(wsp)))
if winsound:
if fileExists(prog+".exe"): return prog+".exe"
elif riscos_sound:
if prog[:1]=="*": # module
os.system("help "+prog[1:]+" { > _tstCmd_ }")
got = open("_tstCmd_").read().find(prog[1:].upper())>-1
os.unlink("_tstCmd_") ; return got
return checkIn("Alias$"+prog,os.environ) # works in Python 3.8 but not 2.7 (Alias$ vars hidden)
elif unix:
try:
try: from shutil import which as find_executable # PEP 632
except: from distutils.spawn import find_executable
if (":"+os.environ.get("PATH","")).find(":.")>-1:
prog = find_executable(prog)
else: # at least some distutils assume that "." is in the PATH even when it isn't, so do it ourselves without checking "."
oldCwd = os.getcwd()
pList = os.environ.get("PATH","").split(':')
if pList:
done=0
for p in pList:
try: os.chdir(p)
except: continue
done=1 ; break
if done:
prog = find_executable(prog)
os.chdir(oldCwd)
except ImportError:
# fall back to running 'which' in a shell (probably slower if got_program is called repeatedly)
prog = wspstrip(os.popen("which "+prog+" 2>/dev/null").read())
if not fileExists_stat(prog): prog=None # some Unix 'which' output an error to stdout instead of stderr, so check the result exists
return prog
def win2cygwin(path): # convert Windows path to Cygwin path
if path[1]==":": return "/cygdrive/"+path[0].lower()+path[2:].replace("\\","/")
......@@ -486,23 +580,17 @@ def check_for_interrupts(): # used on platforms where thread.interrupt_main won'
raise KeyboardInterrupt
# If forking, need to do so BEFORE importing any Tk module (we can't even verify Tk exists 1st)
if outputFile or justSynthesize or appuifw or not (winsound or winCEsound or mingw32 or macsound or riscos_sound or cygwin or "DISPLAY" in os.environ): useTK = 0
if useTK and runInBackground and not (winsound or mingw32) and hasattr(os,"fork") and not "gradint_no_fork" in os.environ:
import fcntl, termios
if outputFile or justSynthesize or appuifw or not (winsound or winCEsound or mingw32 or macsound or riscos_sound or cygwin or checkIn("DISPLAY",os.environ)): useTK = 0
if useTK and runInBackground and not (winsound or mingw32) and hasattr(os,"fork") and not checkIn("gradint_no_fork",os.environ):
if os.fork(): sys.exit()
os.setpgid(0,0)
os.setsid()
if os.fork(): sys.exit()
try: tty = os.open("/dev/tty", os.O_RDWR)
except: tty = None
if not tty==None:
fcntl.ioctl(tty, termios.TIOCNOTTY, 0)
os.close(tty)
devnull = os.open("/dev/null", os.O_RDWR)
for fd in range(3): os.dup2(devnull,fd)
else: runInBackground = 0
try: import readline # enable readline editing of raw_input()
except: pass
except: readline=0
try: import cPickle as pickle
except:
......@@ -520,5 +608,10 @@ try:
import locale
locale.setlocale(locale.LC_ALL, 'C')
except: pass
if not LB('\xc4').lower()==LB('\xc4'): # buggy setlocale (e.g. S60) can create portability issues with progress files
lTrans=B("").join([chr(c) for c in range(ord('A'))]+[chr(c) for c in range(ord('a'),ord('z')+1)]+[chr(c) for c in range(ord('Z')+1,256)])
def lower(s): return s.translate(lTrans) # (may crash if Unicode)
else:
def lower(s): return s.lower()
# -------------------------------------------------------
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# (Python 2 or Python 3, but more fully tested on 2)
program_name = "gradint v0.9974 (c) 2002-2011 Silas S. Brown. GPL v3+."
program_name = "gradint v3.095 (c) 2002-25 Silas S. Brown. GPL v3+."
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -21,8 +22,48 @@ appTitle = "Language lesson"
import sys,os
if not sys.version_info[0]==2: # oh panic, someone's probably trying to run us on Py3k
sys.stderr.write("Sorry, Gradint cannot run on Python "+repr(sys.version_info[0])+"\nPlease install a 2.x version of Python (must be 2.2+).\n")
sys.exit(1)
if sys.version_info[0]>2:
_map,_filter = map,filter
def map(*args): return list(_map(*args))
def filter(*args): return list(_filter(*args))
from functools import cmp_to_key
def sort(l,c): l.sort(key=cmp_to_key(c))
raw_input,unichr,xrange,long = input,chr,range,int
def chr(x): return unichr(x).encode('latin1')
from subprocess import getoutput
popenRB,popenWB = "r","w"
def unicode(b,enc):
if type(b)==str: return b
return b.decode(enc)
else: # Python 2
def sort(l,c): l.sort(c)
popenRB,popenWB = "rb","wb"
bytes = str
try: from commands import getoutput
except ImportError: pass
# For pre-2.3 versions of Python (e.g. 2.2 on Symbian S60 and Mac OS 10.3):
try: True
except: exec("True = 1 ; False = 0")
def readB(f,m=None):
if hasattr(f,"buffer"): f0,f=f,f.buffer # Python 3 non-"b" file
if m: return f.read(m)
else: return f.read() # no "None" in Python 2
def writeB(f,b):
if hasattr(f,"buffer"): f0,f=f,f.buffer # Python 3 non-"b" file
f.write(b)
def B(x):
if type(x)==bytes: return x
try: return x.encode('utf-8')
except: return x # maybe not a string
def LB(x):
if type(x)==bytes: return x
try: return x.encode('latin1')
except: return x
def S(x):
if type(x)==bytes and not bytes==str: return x.decode('utf-8')
return x
def S2(s):
try: return S(s)
except: return s # coding errors OK in unavail, leave as byte-string
# --------------------------------------------------------
# This file is part of the source code of
# gradint v0.9974 (c) 2002-2011 Silas S. Brown. GPL v3+.
# This file is part of the source code of Gradint
# (c) Silas S. Brown.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
......@@ -43,21 +43,21 @@ def setup_samplesDir_ifNec(d=0): # if the user doesn't have a samples directory,
if not d: d=samplesDirectory
if not isDirectory(d):
os.mkdir(d)
if fileExists(user0[0]+os.sep+"README"+dottxt): open(d+os.sep+"README"+dottxt,'wb').write(read(user0[0]+os.sep+"README"+dottxt))
if fileExists(user0[0]+os.sep+"README"+dottxt): write(d+os.sep+"README"+dottxt,read(user0[0]+os.sep+"README"+dottxt))
def get_userNames(): # list of unicode user names or []
ret=[]
u=userNameFile ; c=0
while fileExists(u):
ret.append(unicode(u8strip(read(u)).strip(wsp),'utf-8'))
ret.append(unicode(bwspstrip(u8strip(read(u))),'utf-8'))
c += 1 ; u=addUserToFname(userNameFile,c)
global lastUserNames ; lastUserNames = ret
return ret
def set_userName(N,unicodeName): open(addUserToFname(userNameFile,N),"w").write(unicodeName.encode("utf-8")+"\n") # implicitly adds if N=num+1
def set_userName(N,unicodeName): writeB(open(addUserToFname(userNameFile,N),"w"),unicodeName.encode("utf-8")+B("\n")) # implicitly adds if N=num+1
def wrapped_set_userName(N,unicodeName):
if unicodeName.strip(wsp): set_userName(N,unicodeName)
if wspstrip(unicodeName): set_userName(N,unicodeName)
else: app.todo.alert="You need to type the person's name in the box before you press "+localise("Add new name") # don't waitOnMessage because we're in the GUI thread
GUI_usersRow = lastUserNames = None
......@@ -91,7 +91,7 @@ def updateUserRow(fromMainMenu=0):
userBSM = ButtonScrollingMixin() ; userBSM.ourCanvas = c
else: userBSM = None
for i in range(len(names)):
if names[i].strip(wsp):
if wspstrip(names[i]):
r=Tkinter.Radiobutton(row, text=names[i], variable=app.userNo, value=str(i), takefocus=0)
r.grid(row=i+1,column=0,sticky="w")
r["command"]=cmd=lambda e=None,i=i: select_userNumber(i)
......@@ -107,41 +107,42 @@ def updateUserRow(fromMainMenu=0):
r=Tkinter.Frame(row) ; r.grid(row=i+1,column=0,columnspan=4)
text,entry = addTextBox(r)
if not fromMainMenu: entry.focus() # because user has just pressed the "add other students" button, or has just added a name and may want to add another
l=lambda *args:(wrapped_set_userName(i,asUnicode(text.get())),updateUserRow())
l=lambda e=None,wrapped_set_userName=wrapped_set_userName,i=i,text=text:(wrapped_set_userName(i,asUnicode(text.get())),updateUserRow())
addButton(r,localise("Add new name"),l)
entry.bind('<Return>',l)
if not i: Tkinter.Label(row,text="The first name should be that of the\nEXISTING user (i.e. YOUR name).").grid(row=i+2,column=0,columnspan=4)
if userBSM: userBSM.bindFocusIn(r) # for shift-tab from the bottom
if hasattr(row,"widgetsToDel"): row.widgetsToDel.append(r)
if not names[i]: break
if userBSM: c.after(cond(winCEsound,1500,300),lambda *args:c.config(scrollregion=c.bbox(Tkinter.ALL),width=c.bbox(Tkinter.ALL)[2],height=min(c["height"],c.winfo_screenheight()/2,c.bbox(Tkinter.ALL)[3]))) # hacky (would be better if it could auto shrink on resize)
else: row.widgetsToDel.append(addButton(row,localise("Family mode (multiple user)"),lambda *args:(set_userName(0,""),updateUserRow())))
if userBSM: c.after(cond(winCEsound,1500,300),lambda e=None,c=c:c.config(scrollregion=c.bbox(Tkinter.ALL),width=c.bbox(Tkinter.ALL)[2],height=min(c["height"],c.winfo_screenheight()/2,c.bbox(Tkinter.ALL)[3]))) # hacky (would be better if it could auto shrink on resize)
else: row.widgetsToDel.append(addButton(row,localise("Family mode (multiple user)"),lambda e=None:(set_userName(0,""),updateUserRow())))
def renameUser(i,radioButton,parent,cancel=0):
if hasattr(radioButton,"in_renaming"):
if hasattr(radioButton,"in_renaming"): # finish the rename
del radioButton.in_renaming
n=asUnicode(radioButton.renameText.get())
if cancel: pass
elif not n.strip(wsp) and len(lastUserNames)>1: tkMessageBox.showinfo(app.master.title(),"You can't have blank user names unless there is only one user. Keeping the original name instead.")
elif not wspstrip(n) and (len(lastUserNames)>1 and not (len(lastUserNames)==2 and not lastUserNames[-1])): tkMessageBox.showinfo(app.master.title(),"You can't have blank user names unless there is only one user. Keeping the original name instead.")
else:
set_userName(i,n)
lastUserNames[i] = n
radioButton["text"]=n
radioButton.renameEntry.grid_forget()
radioButton.grid(row=i+1,column=0,sticky="w")
else:
else: # start the rename
radioButton.in_renaming = 1
radioButton.grid_forget()
radioButton.renameText,radioButton.renameEntry = addTextBox(parent,"nopack")
radioButton.renameEntry.grid(row=i+1,column=0)
radioButton.renameText.set(lastUserNames[i])
radioButton.renameEntry.focus()
radioButton.after(10,lambda *args:radioButton.renameEntry.event_generate('<End>'))
radioButton.renameEntry.bind('<Return>',lambda *args:renameUser(i,radioButton,parent))
radioButton.renameEntry.bind('<Escape>',lambda *args:renameUser(i,radioButton,parent,cancel=1))
radioButton.after(10,lambda e=None,radioButton=radioButton:radioButton.renameEntry.event_generate('<End>'))
radioButton.renameEntry.bind('<Return>',lambda e=None,radioButton=radioButton,i=i,parent=parent:renameUser(i,radioButton,parent))
radioButton.renameEntry.bind('<Escape>',lambda e=None,i=i,radioButton=radioButton,parent=parent:renameUser(i,radioButton,parent,cancel=1))
def deleteUser(i):
for n in ["Are you sure","Are you REALLY sure","This is your last chance: Are you REALLY SURE"]:
if not tkMessageBox.askyesno(app.master.title(),u""+n+" you want to delete "+lastUserNames[i]+" permanently, including any vocabulary list and recordings?"): return
if not tkMessageBox.askyesno(app.master.title(),ensure_unicode(n)+" you want to delete "+lastUserNames[i]+" permanently, including any vocabulary list and recordings?"): return
numUsers=len(lastUserNames)
for fileOrDir in user0+(userNameFile,):
d=addUserToFname(fileOrDir,i)
......
#!/usr/bin/env python
# (works on either Python 2 or Python 3)
# program to "thin down" the gradint .py for low memory environments
# by taking out some of the code that's unused on that platform
# This file is part of the source code of Gradint
# (c) Silas S. Brown.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
import sys, re
tk_only = [ # we want these on WinCE but not S60:
# note: comments are stripped BEFORE checking against this list
"def words_exist():",
"def reviseCount(num):", # used only in Tk for now
"if mp3web:",
"class InputSourceManager(object):",
"class InputSource(object):",
"class MicInput(InputSource):",
"class PlayerInput(InputSource):",
"class ButtonScrollingMixin(object):",
"class RecorderControls(ButtonScrollingMixin):",
"def doRecWords():",
"if app:","elif app:",
"def addStatus(widget,status,mouseOnly=0):",
"def addButton(parent,text,command,packing=None,status=None):",
"def addLabel(row,label):",
"def CXVMenu(e):",
"def selectAll(e):",
"def selectAllButNumber(e):",
"def addTextBox(row,wide=0):",
"def addLabelledBox(row,wide=0,status=None):",
"def addRow(parent,wide=0):",
"def addRightRow(widerow):",
"def make_output_row(parent):",
"def select_userNumber(N,updateGUI=1):",
"def select_userNumber2(N):",
"def updateUserRow(fromMainMenu=0):","def get_userNames():",
"def set_userName(N,unicodeName):",
"def wrapped_set_userName(N,unicodeName):",
"def renameUser(i,radioButton,parent,cancel=0):",
"def deleteUser(i):",
"def addUserToFname(fname,userNo):",
"def setupScrollbar(parent,rowNo):",
"def focusButton(button):",
"def bindUpDown(o,alsoLeftRight=False):",
"class ExtraButton(object):",
"def make_extra_buttons_waiting_list():",
"def startTk():",
# "def guiVocabList(parsedVocab):", # now actually used on S60
"def synchronizeListbox(listbox,masterList):",
"if useTK:",
"if useTK and not tkSnack:",
"def openDirectory(dir,inGuiThread=0):",
"def gui_event_loop():",
"def makeButton(parent,text,command):",
"def vocabLinesWithLangs():",
"if Tk_might_display_wrong_hanzi:",
"def setup_samplesDir_ifNec(d=0):",
"def filename2unicode(f):",
"def unicode2filename(u):",
]
not_S60_or_android = [ # but may still need on winCE
"if winsound:",
"if winsound or mingw32:",
"class ESpeakSynth(Synth):","def espeak_volume_ok():",
'if winCEsound or ((winsound or mingw32) and not os.sep in tmpPrefix and not tmpPrefix.startswith("C:")):',
'def got_program(prog):', # as no non-winsound/unix
'if useTK and runInBackground and not (winsound or mingw32) and hasattr(os,"fork") and not "gradint_no_fork" in os.environ:',
'def maybe_warn_mp3():',
'elif (cygwin or ((winsound or mingw32) and winsound_also)) and os.sep in file:',
'elif (winsound and not (self.length>10 and wavPlayer)) or winCEsound:',
"elif wavPlayer.find('sndrec32')>=0:",
'elif wavPlayer:', # it'll take appuifw/android 1st
'if winsound or mingw32 or cygwin:',
'elif winsound or mingw32 or cygwin:',
'for s in synth_priorities.split():', # Ekho/eSpeak/MacOS/SAPI not available on S60/Android (well, not that we can yet call into)
'def import_recordings(destDir=None):', # TODO: document in advanced.txt that this option is non-functional on S60/Android? (code WOULD work if suitably configured, but unlikely to be used and we need to save size)
"elif msvcrt:",
]
not_android = [
"if not app and not app==False and not appuifw and not android:",
"elif not android:",
"def fileExists(f):", # assume we got os.path
"def fileExists_stat(f):",
"def isDirectory(directory):",
"for p in [progressFile,progressFileBackup,pickledProgressFile]:", # this coherence check is not likely to be a problem on Android, and we could do with saving the space
"if need_say_where_put_progress:", # ditto
'def check_for_interrupts():','if emulated_interruptMain:','if emulated_interruptMain or winCEsound:','def handleInterrupt():', # no current way to do this on Android (unlike S60/WinCE)
r"if not '\xc4'.lower()=='\xc4':", # this workaround is not needed on Android
r"if not fileExists(configFiles[0]) and sys.argv and (os.sep in sys.argv[0] or (os.sep=='\\' and '/' in sys.argv[0])):", # that logic not likely to work on Android (but we do need the rest of that block)
"def guiVocabList(parsedVocab):", # not yet available on Android (unlike S60, TODO?)
]
riscos_only = [
"if riscos_sound:",
"elif riscos_sound:",
'if riscos_sound and hex(int(time.time())).find("0xFFFFFFFF")>=0 and not outputFile:',
"class OldRiscosSynth(Synth):",
'if not extsep==".":', # RISC OS
]
mac_only = [
'if macsound and __name__=="__main__":',
'if macsound and "_" in os.environ:',
"if macsound:","elif macsound:",
'if hasattr(app,"isBigPrint") and macsound:',
'elif macsound and got_program("afconvert"):',
]
desktop_only = [ # Don't want these on either WinCE or S60:
'if hasattr(app,"isBigPrint") and winsound:',
"if unix:","elif unix:",
"def disable_lid(restore):",
'if unix and isDirectory("/dev/snd") and got_program("arecord"):',
"if unix and (';' in cmd or '<' in cmd):",
'elif wavPlayer=="sox":',
'elif wavPlayer=="aplay" and ((not fileType=="mp3") or madplay_path or gotSox):',
"def simplified_header(fname):",
"def win2cygwin(path):","elif cygwin:",
"if paranoid_file_management:",
"elif unix and not macsound:",
"elif unix and hasattr(os,\"popen\"):",
"def wavToMp3(directory):",
"def makeMp3Zips(baseDir,outDir,zipNo=0,direc=None):",
"def check_for_slacking():",
"def checkAge(fname,message):",
"def downloadLAME():",
"def decode_mp3(file):",
"class Mp3FileCache(object):",
"class OSXSynth_Say(Synth):",
"def aiff2wav(fname):", # (used only on Mac)
"class OSXSynth_OSAScript(Synth):",
"class PttsSynth(Synth):",
"def sapi_sox_bug_workaround(wavdata):",
"class FliteSynth(Synth):",
"def espeak_stdout_works():", # called only if unix
# (keep ESpeakSynth for WinCE)
"class EkhoSynth(Synth):",
"class FestivalSynth(Synth):",
"class GeneralSynth(Synth):", # (needs os.system, so not S60/WinCE)
"class GeneralFileSynth(Synth):", # (ditto)
"class ShellEvent(Event):",
# And the following are desktop only because they need sox:
"if gotSox and unix:",
"class SoundCollector(object):","if soundCollector:",
"def oggenc():",
"def outfile_writeBytes(o,bytes):",
"def outfile_close(o):",
"def outfile_writeFile(o,handle,filename):",
"class ShSoundCollector(object):",
"def outfile_write_error():",
"def lame_quiet():",
"def beepCmd(soxParams,fname):",
"def collector_time():",
"def collector_sleep(s):",
"def dd_command(offset,length):",
"def lame_endian_parameters():",
"if outputFile:",
"def setSoundCollector(sc):",
"def getAmplify(directory):",
"def doAmplify(directory,fileList,factor):",
"def gui_outputTo_end(openDir=True):",
"def gui_outputTo_start():",
"def warn_sox_decode():",
'if disable_once_per_day==1:',
'if once_per_day&2 and not hasattr(sys,"_gradint_innerImport"):',
'if once_per_day&1 and fileExists(progressFile) and time.localtime(os.stat(progressFile).st_mtime)[:3]==time.localtime()[:3]:',
'def optimise_partial_playing(ce):',
'def optimise_partial_playing_list(ceList):',
]
winCE_only = [
"if use_unicode_filenames:",
"if winCEsound:",'elif winCEsound:',
'if winCEsound and __name__=="__main__":',
'elif winCEsound and fileType=="mp3":',
"if WMstandard:",
]
not_winCE = [
"if not winCEsound:",
]
S60_only = [
'if sys.platform.find("ymbian")>-1:',
"class S60Synth(Synth):",
"if appuifw:","elif appuifw:",
"def s60_recordWord():",
"def s60_recordFile(language):",
"def s60_addVocab():",
"def s60_changeLang():",
"def s60_runLesson():",
"def s60_viewVocab():",
"def s60_main_menu():",
]
android_only = [
"if android:",
"elif android:",
"class AndroidSynth(Synth):",
"def android_recordWord():",
"def android_recordFile(language):",
"def android_main_menu():",
"def android_addVocab():",
"def android_changeLang():",
]
unix_only = [
"class CoquiSynth(Synth):",
"class PiperSynth(Synth):",
]
android_or_S60 = [
"def droidOrS60RecWord(recFunc,inputFunc):",
]
if "s60" in sys.argv: # S60 version
version = "S60"
to_omit = tk_only + desktop_only + winCE_only + not_S60_or_android + android_only + riscos_only + mac_only + unix_only
elif "android" in sys.argv: # Android version
version = "Android"
to_omit = tk_only + desktop_only + winCE_only + S60_only + not_S60_or_android + not_android + riscos_only + mac_only + unix_only
elif "wince" in sys.argv: # Windows Mobile version
version = "WinCE"
to_omit = desktop_only + S60_only + android_only + android_or_S60 + not_winCE + riscos_only + mac_only + unix_only
elif "core" in sys.argv: # experimental "core code only" for 'minimal embedded porting' starting point (no UI, no synth, limited file I/O; you'll probably have to load up the event data yourself)
version = "core"
to_omit = tk_only + not_S60_or_android + not_android + riscos_only + mac_only + desktop_only + winCE_only + S60_only + android_only + android_or_S60 + unix_only + [
"def main():",
"def rest_of_main():",
'if __name__=="__main__":',
"def transliterates_differently(text,lang):",
"def primitive_synthloop():",
"def appendVocabFileInRightLanguages():",
'def delOrReplace(L2toDel,L1toDel,newL2,newL1,action="delete"):',
"def generalCheck(text,language,pauseOnError=0):",
"def localise(s):",
"def singular(number,s):",
"def readText(l):",
"def asUnicode(x):",
"def updateSettingsFile(fname,newVals):",
"def clearScreen():",
"def startBrowser(url):",'def getYN(msg,defaultIfEof="n"):',"def waitOnMessage(msg):",
"def interrupt_instructions():",
"def parseSynthVocab(fname,forGUI=0):",
"def scanSamples_inner(directory,retVal,doLimit):",
"def getLsDic(directory):",
"def check_has_variants(directory,ls):",
"def exec_in_a_func(x):",
"def scanSamples(directory=None):",
"def synth_from_partials(text,lang,voice=None,isStart=1):",
"def partials_langname(lang):",
"if partialsDirectory and isDirectory(partialsDirectory):",
'for zipToCheck in ["yali-voice","yali-lower","cameron-voice"]:',
'def stripPuncEtc(text):',
'def can_be_synthesized(fname,dirBase=None,lang=None):',
'def synthcache_lookup(fname,dirBase=None,printErrors=0,justQueryCache=0,lang=None):',
'def textof(fname):',
'if synthCache and transTbl in synthCache_contents:',
'if synthCache:',
'class Partials_Synth(Synth):',
'def abspath_from_start(p):',
'class SynthEvent(Event):',
'def pinyin_uColon_to_V(pinyin):',
'def synth_event(language,text,is_prompt=0):',
'def get_synth_if_possible(language,warn=1,to_transliterate=False):',
'if wavPlayer_override or (unix and not macsound and not (oss_sound_device=="/dev/sound/dsp" or oss_sound_device=="/dev/dsp")):',
'def fix_compatibility(utext):',
'def read_chinese_number(num):',
'def preprocess_chinese_numbers(utext,isCant=0):',
'def intor0(v):',
'def fix_pinyin(pinyin,en_words):',
'def fix_commas(text):',
'def shell_escape(text):',
'class SimpleZhTransliterator(object):',
'def sort_out_pinyin_3rd_tones(pinyin):',
'def ensure_unicode(text):',
'def unzip_and_delete(f,specificFiles="",ignore_fail=0):',
'class Synth(object):',
'def quickGuess(letters,lettersPerSec):',"def changeToDirOf(file,winsound_also=0):",'if app or appuifw or android:',
'def subst_some_synth_for_synthcache(events):',
'def decide_subst_synth(cache_fname):',
'if winsound or winCEsound or mingw32 or riscos_sound or not hasattr(os,"tempnam") or android:',
'if len(sys.argv)>1:',
'def readSettings(f):',
'def exc_info(inGradint=True):',
'if not fileExists(configFiles[0]):',
'def u8strip(d):']
else: assert 0, "Unrecognised version on command line"
revertToIndent = lastIndentLevel = indentLevel = -1
lCount = -1 ; inTripleQuotes=0 ; orig = []
for l in sys.stdin:
orig.append(l)
lCount += 1
if lCount==2: print ("\n# NOTE: this version has been automatically TRIMMED for "+version+" (some non-"+version+" code taken out)\n")
l=l.rstrip()
assert not "\t" in l, "can't cope with tabs"
lastIndentLevel,indentLevel = indentLevel,-1
for i in range(len(l)):
if not l[i]==" ":
indentLevel = i ; break
was_inTripleQuotes = inTripleQuotes
if (len(l.split('"""'))%2) == 0: inTripleQuotes = not inTripleQuotes
if indentLevel<0 or indentLevel==len(l) or (revertToIndent>=0 and (indentLevel>revertToIndent or was_inTripleQuotes)): continue
justRevertedI,revertToIndent = revertToIndent,-1
code0 = (l+"#")[:l.find("#")].rstrip()
code = code0.lstrip()
if (code in to_omit or (':' in code and code[:code.index(':')+1] in to_omit)) and not was_inTripleQuotes:
if ':' in code and code[:code.index(':')+1] in to_omit: code = code[:code.index(':')+1]
if code.startswith("def "): code=re.sub(r"\([^)][^)][^)]+\)",r"(*_)",code)
if code.startswith("elif "): pass # can always remove those lines completely, even if will be followed by an 'else' (and will never be the only thing in its block)
else:
if code.startswith("if "): code="if 0:"
print (" "*indentLevel+code+" pass # trimmed")
revertToIndent = indentLevel
elif not code:
if "# " in l or lCount < 2: print (l) # keep start and GPL comments
elif ('"' in code and '"' in l[len(code):]) or ("'" in code and "'" in l[len(code):]): print (l) # perhaps # was in a string, keep it
else: print (code0)
orig = "".join(orig)
for o in to_omit:
if not o in orig: sys.stderr.write("Warning: line not matched: "+o)
......@@ -18,92 +18,11 @@
# file 'advanced.txt' as well.
#
# IMPORTANT: Before speech synthesis will work, make sure
# that your computer has the necessary speech synthesizers
# for BOTH languages. Gradint supports:
#
# 1. eSpeak, a very multilingual and multiplatform speech
# synthesizer available at http://espeak.sourceforge.net/
# and bundled with the Windows and Mac versions of Gradint.
#
# - Just install it and gradint will find it. Any of
# eSpeak's languages can be used as long as you use the
# same language abbreviations as eSpeak does, e.g. "en"
# for English, "zh" for Zhongwen (Mandarin).
#
# - By default eSpeak will be used for English too; if you want
# to use Windows's or Mac OS X's voices for English, set the variable
# 'synth_priorities' in advanced.txt.
#
# - You can improve eSpeak's English by installing
# Festival's dictionary and using lexconvert to convert
# it, see http://people.pwf.cam.ac.uk/ssb22/gradint/lexconvert.html
# (this has already been done in the bundled version).
#
# - eSpeak is not very natural-sounding, but it is very
# clear and accurate in English, Mandarin and some other
# languages (although some languages may be poor e.g. the
# ones marked "testing" or "feedback needed").
# that your computer has the necessary speech synthesizers.
# See advanced.txt if you need to change the speech synth setup.
#
# 2. Microsoft SAPI 5 (included with Windows XP, and can be
# added to earlier versions of Windows)
#
# - can be used for English (language abbreviation 'en')
#
# - can be used for Chinese if you have installed the
# NeoSpeech "Lily" Mandarin voice (language abbreviation
# must be 'zh', and use pinyin with tone numbers, 5 for
# neutral tone, v or u: for u-umlaut and if possible put spaces
# between meaningful words).
# You can also include hanzi, if you make sure to save the file in UTF-8 format.
# - Lily is very natural-sounding, but it has been known
# to get quite a few phrases subtly wrong,
# so make sure you can tell the difference.
#
# - can be used for other voices if you set
# sapiVoices in advanced.txt.
#
# The control panel's "Speech" option can choose the English
# voice (if you're using SAPI for English), and also the
# speed of all SAPI voices.
#
# 3. Other speech synthesizers:
#
# MANDARIN CHINESE - language abbreviation must be 'zh';
# text should use Hanyu Pinyin with a tone number after
# each syllable (use 5 for neutral) and v or u: for u-umlaut
# (and if possible put spaces between meaningful words but
# not between syllables within a word):
#
# - eSpeak or NeoSpeech Lily, as described above.
# (you can also include hanzi in UTF-8)
# + others (MeiLing, Loquendo, etc) if you set sapiVoices
#
# - Yali Cheng's Mandarin syllable recordings - see
# instructions on website. (You can also include
# hanzi in UTF-8 if eSpeak is present.) Recommended.
#
# ENGLISH (language abbreviation must be "en"):
#
# - Mac OS X comes with English speech as standard
# (you can choose the voice and speed in system preferences)
#
# - Festival Lite on Windows (if all else fails) :
# put flite.exe in the gradint folder
#
# - Linux: install Festival, or flite if you want a US
# accent, or (as already mentioned) eSpeak
#
# - RISC OS: if for some reason you can't install eSpeak
# as mentioned above, you can instead install an older
# version of Jonathan Duddington's !Speak, or the even
# older "Speech!" utility. These can be used only for
# playing in real-time, not for generating files.
# If using non-ASCII characters, please choose UTF-8 coding.
#
#
# 5. Other speech software can be used if you can tell
# gradint how to run it. See 'advanced.txt' for details.
#
#
# OTHER POINTS TO NOTE:
#
# If you want to specify that a group of words should be
......@@ -126,7 +45,7 @@
# quotes) on a line by itself, and the line immediately under it will be
# interpreted normally without being linked into the poem. This is useful
# for setting relevant vocabulary to be introduced part-way through learning
# the poem.
# the poem. (In recent versions of Gradint the colon can be omitted.)
#
# If you have recordings in one language and you want the
# equivalents in another language to be synthesized, you can
......@@ -136,7 +55,6 @@
# somefile_lang2.txt to make a version in lang2 (where lang1
# and lang2 are any language abbreviations). Each .txt file
# should contain only 1 phrase in 1 language and nothing else.
# (RISC OS users should replace '.' with '/'.)
# Verbal annotations, and -meaning files (see samples README
# file for details) can also be in .txt files.
......@@ -144,7 +62,8 @@
# on the top line of this file, Gradint's GUI will assume
# you don't need to be asked if you're sure when editing files)
#The three lines below are to help the Emacs editor.
# The three lines below are to help the Emacs editor.
# (XEmacs users might like to try winmgr instead of m4)
#Local Variables:
#mode: winmgr
#mode: m4
#End:
#!/bin/bash
# Convert a Windows gradint.exe or gradint-bundle.exe
# (optionally with bundled samples, partials etc) into
# a GNU/Linux installation of Gradint. Use this to
# support both Windows and GNU/Linux from one bundle.
# (c) 2013,2021-22 Silas S. Brown. License: GPL v3 (as Gradint)
# This script can be placed on a USB stick or whatever, in
# the same directory as:
# (1) gradint.exe or gradint-bundle.exe
# (2) 7za binary (for correct CPU type and libraries), if
# 7za is not installed on the system,
# (3) espeak binary (ditto)
# (4) any *.deb files to install with dpkg, e.g. python-tk
# and its dependencies (e.g. blt tk-8.5)
# (these will be installed with --force-depends, in case
# you're doing an offline install and can manage without
# having the most up-to-date dependencies - do this at
# your own risk)
# You can also put the 7za, espeak and deb files into a
# bin/ subdirectory.
DoneDeb=0
if ! test "$(echo *.deb)" == "*.deb"; then
echo "Installing *.deb (with --force-depends)"
sudo dpkg --force-depends -i *.deb
DoneDeb=1
elif ! test "$(echo bin/*.deb)" == "bin/*.deb"; then
echo "Installing bin/*.deb (with --force-depends)"
sudo dpkg --force-depends -i bin/*.deb
DoneDeb=1
fi
# TODO: if got internet, sudo apt-get update ; sudo apt-get -f install
if [ -f espeak ] || [ -f bin/espeak ]; then
echo "Copying espeak binary to /usr/local/bin"
if [ -f espeak ]; then sudo cp espeak /usr/local/bin/
else sudo cp bin/espeak /usr/local/bin/; fi
elif ! which espeak 2>/dev/null >/dev/null && ! which speak 2>/dev/null >/dev/null; then
echo "Warning: no espeak binary found on system, and none to install" # TODO: try to apt-get it? but might not have an Internet connection
echo -n "Press Enter: " ; read
fi
mkdir -p "$HOME/gradint0"
if [ -e gradint-bundle.exe ]; then
PATH="$PATH:.:./bin" 7za "-o$HOME/gradint0" x gradint-bundle.exe || exit 1
else PATH="$PATH:.:./bin" 7za "-o$HOME/gradint0" x gradint.exe || exit 1; fi
cd "$HOME/gradint0" || exit 1
mv gradint .. || exit 1
cd .. && rm -rf gradint0
cd gradint || exit 1
unzip library.zip gradint.py || exit 1
rm -rf tcl library.zip ./*.exe ./*.pyd ./*.dll
if python -c 'import tkinter'; then
if [ -e ~/Desktop ] && ! [ -e ~/Desktop/Gradint ]; then
echo "Creating symlink on Desktop"
ln -s "$(pwd)/gradint.py" ~/Desktop/Gradint
# TODO: what about the menus of various front-ends? (might need to make .desktop files in .local/share/applications or something)
else echo "Not creating symlink on desktop" # TODO: cater for more possibilities
fi
else echo "Warning: no tkinter on this system; gradint will be command-line only" ; echo -n "Press Enter: " ; read # TODO: if internet, try sudo apt-get install python-tk
fi
echo "Copying espeak-data to /usr/share/"
sudo cp -r espeak/espeak-data /usr/share/ && rm -rf espeak
echo "win2linux.sh finished"
if test $DoneDeb == 1; then
echo
echo "WARNING: Have installed packages with dpkg --force-depends."
echo "If you're connected to the Internet, you might now wish to do:"
echo " sudo apt-get update ; sudo apt-get -f install"
echo "or if the machine will later be connected to the"
echo "Internet without you, you might wish to do:"
echo " (echo apt-get update; echo apt-get -yf install) | sudo at -M midnight"
fi
File added
File moved
File added
File added
@echo off
rem This is Gradint's setup file for Windows.
rem If you are seeing this, when you expected
rem the Gradint installer to be running, then
rem it probably means your copy of Windows has
rem an incorrect file association. That will
rem affect other programs, not just Gradint.
rem To fix the incorrect association, try
rem running regedit and deleting the key:
rem Hkey_Current_User\Software\Microsoft\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.bat
rem then restart your system and the Gradint
rem installer should work normally.
rem This problem has been reported on some
rem (but not all) Windows 7 setups.
rem --------------------------------------------
rem Find a good place to put Gradint. On Windows 9x this can be C:\Program Files. On XP/NT/etc we'd better check for different home directories. Also check where the profile is.
if not exist "%HOMEDRIVE%%HOMEPATH%" set HOMEDRIVE=C:
......@@ -42,6 +58,9 @@ rem copy all program files, even the ones that have never been changed, in case
tskill gradint-wrapper 2>nul
taskkill /f /im gradint-wrapper.exe 2>nul >nul
cd gradint
rem clean up after old versions
if exist background1.txt del background1.txt
if exist background2.txt del background2.txt
rem support bundles
for /D %%g in (*_disabled) do xcopy /I %%g "%HOMEDRIVE%%HOMEPATH%\gradint\%%g" /S
rem support users who install yali BEFORE gradint
......@@ -119,6 +138,7 @@ rem (deliberately saying "press any key" ourselves not from 'pause', otherwise t
mkdir "%USERPROFILE%\Desktop\gradint"
copy /Y shortcuts\*.* "%USERPROFILE%\Desktop\gradint"
if exist "%AppData%\Microsoft\Windows\Start Menu\Programs\Startup" goto win8
mkdir "%USERPROFILE%\Start Menu\Programs\gradint"
copy /Y shortcuts\*.* "%USERPROFILE%\Start Menu\Programs\gradint"
......@@ -126,10 +146,18 @@ rem Install startup once-per-day thing
mkdir "%USERPROFILE%\Start Menu\Programs\Startup"
copy /Y startup\*.* "%USERPROFILE%\Start Menu\Programs\Startup"
goto win8skip
:win8
copy /Y startup\*.* "%AppData%\Microsoft\Windows\Start Menu\Programs\Startup"
mkdir "%AppData%\Microsoft\Windows\Start Menu\Programs\gradint"
copy /Y shortcuts\*.* "%AppData%\Microsoft\Windows\Start Menu\Programs\gradint"
:win8skip
cd /D "%USERPROFILE%\Desktop"
goto end
:PRC
rem This is a special case for Chinese (Simplified) Windows, configured to use the "Chinese (PRC)" locale for legacy apps (which means these strings should be gb2312 coded).
rem This is a special case for Chinese (Simplified) Windows (XP etc), configured to use the "Chinese (PRC)" locale for legacy apps (which means these strings should be gb2312 coded).
rem (You can get the names of Start Menu etc folders coded in the current locale by doing dir > file.txt at a command prompt and inspecting file.txt)
mkdir "%USERPROFILE%\戮충\gradint"
ren shortcuts\uninstall.bat "shortcuts\탤뇜.bat"
......@@ -147,4 +175,4 @@ rem start explorer gradint
rem (actually, since there's only 1 shortcut now,
rem we might as well just launch it directly)
cd /D "%HOMEDRIVE%%HOMEPATH%\gradint"
start gradint-wrapper.exe once_per_day=2
start gradint-wrapper.exe once_per_day=disable_once_per_day=2
......@@ -32,6 +32,19 @@ taskkill /f /im gradint-wrapper.exe 2>nul >nul
cd /D "%HOMEDRIVE%%HOMEPATH%"
rmdir /S /Q gradint
cd /D "%USERPROFILE%"
rmdir /S /Q "Desktop\gradint"
if exist "%AppData%\Microsoft\Windows\Start Menu\Programs\Startup" goto win8
del "Start Menu\Programs\Startup\Run gradint once per day.bat"
rmdir /S /Q "Start Menu\Programs\gradint" "Desktop\gradint"
rem (TODO - Chinese Windows shortcuts also - see setup.bat)
rmdir /S /Q "Start Menu\Programs\gradint"
rem This is a special case for Chinese (Simplified) Windows (XP etc), configured to use the "Chinese (PRC)" locale for legacy apps (which means these strings should be gb2312 coded).
if not exist "%USERPROFILE%\「역迦」꽉데" goto end
rmdir /S /Q "%USERPROFILE%\戮충\gradint" "%USERPROFILE%\「역迦」꽉데\넋埼\gradint"
del "%USERPROFILE%\「역迦」꽉데\넋埼\폘땡\Run gradint once per day.bat"
goto end
:win8
del "%AppData%\Microsoft\Windows\Start Menu\Programs\Startup\Run gradint once per day.bat"
rmdir /S /Q "%AppData%\Microsoft\Windows\Start Menu\Programs\gradint"
:end