''[Andreas Drollinger] 2021-05-23 - This page shows how using the Tcl interpreter that is part of Python Tkinter.'' The ''de factor'' standard GUI library for Python, Tkinter, contains a full-featured Tcl interpreter, together with the Tk GUI toolkit. This allows running Tcl commands from Python, as well as Python commands from Tcl after performing the some setup. Tkinter is included with standard Linux, Windows and Mac OS X installations of Python. *** Call Tcl from Python *** A Tcl interpreter instance can be created from the Python interpreter after importing the Tkinter module: ======none import tkinter tcl_intrpr = tkinter.Tcl() ====== The created Tcl interpreter instance exposes the methods ''eval'' and ''call'' to run Tcl commands from the Python program. ''Eval'' takes as argument a single string with the concatenated Tcl command and arguments, and the result evaluated by the Tcl interpreter is returned as string to the Python interpreter: ======none res = tcl_intrpr.eval('expr 12+23') res => '35' ====== ''Call'' takes separate arguments for the Tcl command and arguments, and the result returned to the Python interpreter corresponds to the value representation in the Tcl interpreter: ======none res = tcl_intrpr.call('expr', '123') res => 123 type(res) => int ====== *** Call Python from Tcl *** The Python functions that should be accessible from the Tcl interpreter have first to be registered as Tcl commands. For demonstration purposes, lets create a Python function that takes an unspecified number of arguments, and register it then as Tcl command. The registration function returns the Tcl function name: ======none def my_python_function(*argv): return 'my_python_function: ' + (' '.join(argv)) tcl_cmd = tcl_intrpr.register(my_python_function) ====== The Tcl function name may be different from the Python function, so it is important to remember it: ======none tcl_cmd => '2013100028616my_python_function' ====== The exposed function can now be executed from the Tcl interpreter. Since ''my_python_function'' accepts an arbitrary number of arguments, various options are shown: ======none tcl_intrpr.call(tcl_cmd) => 'my_python_function: ' tcl_intrpr.call(tcl_cmd, '1') => 'my_python_function: 1' tcl_intrpr.call(tcl_cmd, 'Hello', 'I', 'am', 'Jeff') => 'my_python_function: Hello I am Jeff' ====== *** Call Python from Tcl, use a more elegant registration command *** The fact that the created Tcl command name differs from the Python name is a bit nasty. The following proposed custom registration command to register Tcl functions allows defining explicitly the name of the exposed Tcl function. The created Tcl function is simply renamed into the desired name. If the function exists already, it is first deleted. If no Tcl function name is provided, the Python function name is used: ======none def register(my_python_function, tcl_cmd_name=None): if tcl_cmd_name is None: tcl_cmd_name = my_python_function.__name__ tcl_cmd = tcl_intrpr.register(my_python_function) tcl_intrpr.eval('catch {rename ' + tcl_cmd_name + ' ""}') tcl_intrpr.call('rename', tcl_cmd, tcl_cmd_name) return tcl_cmd_name ====== Next, the Python function will again be registered, but this time with the custom registration command: ======none register(my_python_function) => 'my_python_function' ====== And now, the Tcl function can be executed using using the original python function name: ======none tcl_intrpr.eval('my_python_function') => 'my_python_function: ' tcl_intrpr.eval('my_python_function 1') => 'my_python_function: 1' tcl_intrpr.eval('my_python_function Hello I am Jeff') => 'my_python_function: Hello I am Jeff' ====== *** Variable access *** There is no mechanism in place to access from Python directly Tcl variables and vis-versa. Instead, functions have to be used to access variables ''in the other world''. So, Tcl variables can be accessed from the Python interpreter with the ''set'' command: ======none tcl_intrpr.call('set', 'my_var', 123) => 123 tcl_intrpr.call('set', 'my_var') => 123 ====== Python variables can be accessed from the Tcl interpreter by defining specific variable access functions: ======none # Python variable access function definitions def set_var(var_name, var_value): globals()[var_name] = var_value def get_var(var_name): return globals()[var_name] # Register the access functions register(set_var) register(get_var) # Define a Python variable from Tcl and read it then from Python tcl_intrpr.call('set_var', 'my_python_var', 234) my_python_var => '234' # Define a Python variable from Python and read it then from Tcl my_python_var = 'Hello world' tcl_intrpr.call('get_var', 'my_python_var') => 'Hello world' ====== *** Pythonic way to access Tcl variables *** A more elegant way to access Tcl variables from the Python side is by exposing the Tcl variables as attributes of a Python class. The ''__setattr__'' and ''__getattr__'' methods will be responsible to define and read the corresponding Tcl variables if a class instance variable is accessed: ======none class TclVars(object): def __init__(self, tcl): self.tcl = tcl def __setattr__(self, name, value): if name == 'tcl': object.__setattr__(self, name, value) else: self.tcl.call('set', '::' + name, value) def __getattr__(self, name): if name == 'tcl': return object.__getattr__(self, name) else: return self.tcl.call('set', '::' + name) tcl_vars = TclVars(tcl_intrpr) ====== Tcl variables can now be written and read in the following way: ======none tcl_vars.my_tcl_var = 123 tcl_vars.my_tcl_var => 123 ====== The following lines demonstrate that the defined variable is well known to the Tcl interpreter: ======none tcl_intrpr.call('set', 'my_tcl_var') => 123 ====== Manipulations of the Tcl variable performed by the Tcl interpreter are transparent to the Python interpreter: ======none tcl_intrpr.call('incr', 'my_tcl_var', 2) tcl_vars.my_tcl_var => 125 ====== And manipulations on the Python side are again visible on the Tcl side: ======none tcl_vars.my_tcl_var += 3 tcl_intrpr.call('set', 'my_tcl_var') => 128 ====== ---- *** Discussions *** Don't hesitate to launch some discussions here ... ---- *** See also *** * [Python] * [Accessing Tcl and Python from one another] * [How Tkinter can exploit Tcl/Tk extensions] <> Foreign Interfaces | Language