But what fun is it?
It's funnier if the tcl/tk interperpreter talks back to python :D as an hommage to the 25 years awaited TK9 versions that solves a lot of unicode trouble.
First and foremost, the Popen now use p.stdout=PIPE enabling the channel on which tcl will talk. As a joke puts/gets are named from tcl/tk functions and are used in python to push/get strings from tcl.
Instead of using multithreading having one thread listen to the output and putting the events in a local queue that the main thread will consume I chose the funniest technique of setting tcl/tk output non blocking which does not work on windows. This is the fnctl part of the code.
Then, I chose not to parse the output of tcl/tk but exec it, making tcl/tk actually push python commands back to python. That's the exec part of the code.
For this I needed an excuse : so I added buttons to change minutes/hours back and forth.
That's the moment we all are gonna agree that tcl/tk that tcl/tk biggest sin is its default look. Don't worry, next part is about using themes.
Compared to the first post, changes are minimal :D This is how it should look : And here is the code, largely still below 100 sloc (by 3 lines).
#!/usr/bin/env python
from subprocess import Popen, PIPE
from time import sleep, time, localtime
import select
# import fcntl
import os
# let's talk to tk/tcl directly through p.stdin
p = Popen(['wish'], stdin=PIPE, stdout=PIPE)
# best non portable answer on stackoverflow
#fd = p.stdout.fileno()
#flag = fcntl.fcntl(fd, fcntl.F_GETFL)
#fcntl.fcntl(fd, fcntl.F_SETFL, flag | os.O_NONBLOCK)
# ^-- this 3 lines can be replaced with this one liner --v
# portable non blocking IO
os.set_blocking(p.stdout.fileno(), False)
def puts(s):
for l in s.split("\n"):
select.select([], [p.stdin], [])
p.stdin.write((l + "\n").encode())
p.stdin.flush()
def gets():
ret=p.stdout.read()
p.stdout.flush()
return ret
WIDTH=HEIGHT=400
puts(f"""
canvas .c -width {WIDTH} -height {HEIGHT} -bg white
pack .c
. configure -background white
ttk::button .ba -command {{ puts ch-=1 }} -text <<
pack .ba -side left -anchor w
ttk::button .bb -command {{ puts cm-=1 }} -text <
pack .bb -side left -anchor w
ttk::button .bc -command {{ puts ch+=1 }} -text >>
pack .bc -side right -anchor e
ttk::button .bd -command {{ puts cm+=1 }} -text >
pack .bd -side right -anchor e
""")
# Constant are CAPitalized in python by convention
from cmath import pi as PI, e as E
ORIG=complex(WIDTH/2, HEIGHT/2)
# correcting python notations j => I
I = complex("j")
rad_per_sec = 2.0 * PI /60.0
rad_per_min = rad_per_sec / 60
rad_per_hour = rad_per_min / 12
origin_vector_hand = WIDTH/2 * I
size_of_sec_hand = .9
size_of_min_hand = .8
size_of_hour_hand = .65
rot_sec = lambda sec : -E ** (I * sec * rad_per_sec )
rot_min = lambda min : -E ** (I * min * rad_per_min )
rot_hour = lambda hour : -E ** (I * hour * rad_per_hour )
to_real = lambda c1,c2 : "%f %f %f %f" % (c1.real,c1.imag,c2.real, c2.imag)
for n in range(60):
direction= origin_vector_hand * rot_sec(n)
start=.9 if n%5 else .85
puts(f".c create line {to_real(ORIG+start*direction,ORIG+.95*direction)}")
sleep(.01)
diff_offset_in_sec = (time() % (24*3600)) - \
localtime()[3]*3600 -localtime()[4] * 60.0 \
- localtime()[5]
ch=cm=0
while True:
# eventually parsing tcl output
back = gets()
# trying is more concise than checking
try:
back = back.decode()
exec(back)
except Exception as e:
pass
t = time()
s= t%60
m = m_in_sec = t%(60 * 60) + cm * 60
h = h_in_sec = (t- diff_offset_in_sec)%(24*60*60) + ch * 3600 + cm * 60
puts(".c delete second")
puts(".c delete minute")
puts(".c delete hour")
c0=ORIG+ -.1 * origin_vector_hand * rot_sec(s)
c1=ORIG+ size_of_sec_hand * origin_vector_hand * rot_sec(s)
puts( f".c create line {to_real(c0,c1)} -tag second -fill blue -smooth true")
c1=ORIG+size_of_min_hand * origin_vector_hand * rot_min(m)
puts(f".c create line {to_real(ORIG, c1)} -tag minute -fill green -smooth true")
c1=ORIG+size_of_hour_hand * origin_vector_hand * rot_hour(h)
puts(f".c create line {to_real(ORIG,c1)} -tag hour -fill red -smooth true")
puts("flush stdout")
sleep(.1)
Some history about this code.
I have been mentored in a physical lab where we where doing the pipe, fork, dup2 dance to tcl/tk from C to give a nice output to our simulations so we could control our instuition was right and could extract pictures for the publications. This is a trick that is almost as new as my arteries.
My mentor used to say : we are not coders, we need stuff to work fast and neither get drowned in computer complexity or endless quest for « the one best way » nor being drowned in bugs, we aim for the Keep It Simple Stupid Ways.
Hence, this is a Keep It Simple Stupid approach that I revived for the sake of seeing if it was still robust after 35 years without using it.
Well, if it's robust and it's working: it ain't stupid even if it isn't the « one best idiomatic way ». :P

No comments:
Post a Comment