Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

What is a mechanism equivalent to "trace variable w ..." in Tcl for Python?

48 views
Skip to first unread message

Les Cargill

unread,
Sep 30, 2016, 2:16:10 PM9/30/16
to

A really interesting design approach in Tcl is to install a callback
when a variable is written to. This affords highly event-driven
programming.

Example ( sorry; it's Tcl ) :


namespace eval events {
set preRPM -1
proc handleRPM { args } {
# do stuff to handle an RPM change here
variable ::motor::RPM
variable preRPM
puts "RPM changed from $preRPM to $RPM
set preRPM $RPM
}
}

...

trace variable ::motor::RPM w ::events::handleRPM

...

set ::motor::RPM 33.33

What is an equivalent mechanism in Python?

Thanks in advance.

--
Les Cargill

Ned Batchelder

unread,
Sep 30, 2016, 2:43:18 PM9/30/16
to
On Friday, September 30, 2016 at 2:16:10 PM UTC-4, Les Cargill wrote:
> A really interesting design approach in Tcl is to install a callback
> when a variable is written to. This affords highly event-driven
> programming.
>
> Example ( sorry; it's Tcl ) :
>
>

(I can't read Tcl, sorry)

>
> What is an equivalent mechanism in Python?

There's no way* to hook into "x = 2", but you could hook into "x.a = 2"
if you wanted do, by defining __setattr__ on x's class.

*OK, there might be some crazy hack involving your own class as the
globals for a module or something, but it sounds ill-advised.

--Ned.

Random832

unread,
Sep 30, 2016, 3:51:39 PM9/30/16
to
On Fri, Sep 30, 2016, at 14:42, Ned Batchelder wrote:
> On Friday, September 30, 2016 at 2:16:10 PM UTC-4, Les Cargill wrote:
> > What is an equivalent mechanism in Python?
>
> There's no way* to hook into "x = 2", but you could hook into "x.a = 2"
> if you wanted do, by defining __setattr__ on x's class.

That or set cls.a to be a descriptor (using either @property or a
custom-made descriptor class)

class motor:
@property
def RPM(self): return self._RPM
@RPM.setter
def RPM(self, value):
print("RPM changed from", self._RPM, "to", value)
self._RPM = value

If you really wanted to get crazy, you could use self.__dict__['RPM']
instead of self._RPM, which would allow you to inject the descriptor
separately after the class has already been defined and used. Making it
work right if the definition of motor already has an RPM descriptor [for
example, if it uses __slots__] is an exercise for the reader.

> *OK, there might be some crazy hack involving your own class as the
> globals for a module or something, but it sounds ill-advised.

You can make your own class to be *the module itself* to support
external code's "mod.x = 2", but AIUI that won't apply to plain
assignment, which bypasses most of the relevant machinery.

bream...@gmail.com

unread,
Sep 30, 2016, 4:14:59 PM9/30/16
to
Perhaps you could pinch the idea, or even the code, from tkinter? E.g. see the section "Variable tracing" at http://stupidpythonideas.blogspot.co.uk/2013/12/tkinter-validation.html

Kindest regards.

Mark Lawrence.

Lawrence D’Oliveiro

unread,
Sep 30, 2016, 7:58:06 PM9/30/16
to
On Saturday, October 1, 2016 at 7:16:10 AM UTC+13, Les Cargill wrote:
> A really interesting design approach in Tcl is to install a callback
> when a variable is written to. This affords highly event-driven
> programming.

Python being object-oriented, you could use properties instead.

Terry Reedy

unread,
Sep 30, 2016, 9:33:36 PM9/30/16
to
On 9/30/2016 2:23 PM, Les Cargill wrote:
>
> A really interesting design approach in Tcl is to install a callback
> when a variable is written to. This affords highly event-driven
> programming.
>
> Example ( sorry; it's Tcl ) :
>
>
> namespace eval events {
> set preRPM -1
> proc handleRPM { args } {
> # do stuff to handle an RPM change here
> variable ::motor::RPM
> variable preRPM
> puts "RPM changed from $preRPM to $RPM
> set preRPM $RPM
> }
> }
>
> ...
>
> trace variable ::motor::RPM w ::events::handleRPM

'trace variable' and the corresponding 'trace vdelete' and trace vinfo'
have been deprecated in favor of the newer 'trace add variable', 'trace
remove variable', and 'trace info variable. In 3.6, the tkinter
Variable class has corresponding new trace_add, trace_remove, and
trace_info methods.

> ...
>
> set ::motor::RPM 33.33

The cost of being able to trace variables is having to set them through
a method that can check for the existence of callbacks. The are used in
tk to connect the values of widgets.

> What is an equivalent mechanism in Python?

Python has a trace module for functions and lines, rather than
variables. I believe the primary intended use is for debugging.

The Variable class in tkinter.__init__, currently about line 290, wraps
the tcl mechanism so it literally is the 'equivalent mechanism' in
Python. It could be rewritten to do everything in Python instead of
calling into tk. However, it can also be used as it without displaying
a gui window. Use what you already know.

import tkinter as tk
root = tk.Tk() # Only needed to create Vars.
root.withdraw() # So keep it invisible.

avar = tk.StringVar(root)
avar.trace_add('write', lambda tk_id, unknown, mode:
print(avar.get(), tk_id, unknown, mode))
# See >>> help(tk.Variable.trace_add) for more.
avar.set('abc')
avar.set('def')

* prints
abc PY_VAR0 write # 'unknown' == ''
def PY_VAR0 write

Since you are familiar with tcl, you could potentially use root.call()
to call into tcl yourself.

There are other packages in Python that implement the idea of
broadcasting a change to a set of reistered listeners.

--
Terry Jan Reedy

0 new messages