Hi there, I was considering the possibility of adding something to facilitate package loading in Tkinter. If you have done a wrapper, or used one, you may have noticed that there is always some code like this:
global _TKTABLE_LOADED if not _TKTABLE_LOADED: tktable_lib = os.environ.get('TKTABLE_LIBRARY') if tktable_lib: master.tk.eval('global auto_path; ' 'lappend auto_path {%s}' % tktable_lib) master.tk.call('package', 'require', 'Tktable') _TKTABLE_LOADED = True This one was taken from the tktable wrapper (as you can see), some details will change from wrapper to wrapper (but apparently not by much). There is also the possibility that the wrapper modifies Tkinter.Tk (see ttk), or subclass it (see Tix), to inject this kind of code -- which may be problematic. I see it as problematic when you want to test, lets say, a module P that depends on X and modifies Tkinter.Tk, and another module Q that depends on Y (but no X). When P is executed it does whatever it needs to load X, if loading X succeeds then it is all good, but lets suppose it failed. Now Q executes and tries instantiating Tkinter.Tk, the problem is that it doesn't depend on X to execute but P already modified it to load X (but it failed), now Q fails too. Maybe I'm just bringing this up because I decided to modify Tkinter.Tk on ttk, expected some form of problems initially, but started having real problems just now. Anyway, even if the second reason looks bad, I still would like to have something to load packages easily. I was thinking something like: tcl_deps = {} tk_deps = {} def add_pkgdep(ident, pkg_loader, depends_on='tk'): """Add a new package to be loaded after the interpreter is created. ident is just any identifier (that can be used as a dict key), to identify your pkg_loader. If another pkg_loader is given with the same ident then a DuplicatePkgIdentError is raised. pkg_loader should be a callable which takes a single argument. It will be called with a Tk instance. If depends_on is set to 'tk', then the package loader will be invoked after tk loads and only if the interpreter loads tk. """ if depends_on == 'tk': d = tk_deps else: d = tcl_deps if ident in tk_deps or ident in tcl_deps: raise DuplicatePkgIdentError("%r is already taken" % ident) d[ident] = pkg_loader def pop_pkgdep(ident): """Remove and return the package loader associated with ident from the dependencies.""" if ident in tk_deps: return tk_deps.pop(ident) return tcl_deps.pop(ident) def clear_pkgdeps(): """Clear all dependencies currently set.""" for item in (tcl_deps, tk_deps): item.clear() def basic_dep_loader(master, pkg_name, env_var=None): dep_path = os.environ.get(env_var) if dep_path: # append custom dependence path to the the list of directories that # Tcl uses when attempting to resolve packages with the package # command master.tk.eval( 'global auto_path; ' 'lappend auto_path {%s}' % dep_path) # TclError may be raised now master.tk.eval('package require %s' % pkg_name) Some very small modifications (two lines actually) would be needed in Tkinter.Tk to invoke the loaders at the right time, and wouldn't affect the current usage of Tkinter. Applying it in ttk, for example, would reduce this code (comments stripped): def _loadttk(loadtk): def _wrapper(self): loadtk(self) if _REQUIRE_TILE: import os tilelib = os.environ.get('TILE_LIBRARY') if tilelib: self.tk.eval('global auto_path; ' 'lappend auto_path {%s}' % tilelib) self.tk.eval('package require tile') # TclError may be raised here return _wrapper __loadtk__ = Tkinter.Tk._loadtk Tkinter.Tk._loadtk = _loadttk(Tkinter.Tk._loadtk) To this (besides making it easier to test): def _tile_loader(master): # Load tile if needed, this will happen right after Tk loads. if _REQUIRE_TILE: Tkinter.basic_dep_loader(master, 'tile', 'TILE_LIBRARY') Tkinter.add_pkgdep('tile', _tile_loader) What do you think about this addition ? Should the order of packages matter ? Add some introspection ? -- -- Guilherme H. Polo Goncalves _______________________________________________ Tkinter-discuss mailing list Tkinter-discuss@python.org http://mail.python.org/mailman/listinfo/tkinter-discuss