Detecting a click on the turtle screen when the turtle isn't doinganything?

A

Adam Funk

I'm trying to get a program to do some plotting with turtle graphics,
then wait for the user to click on the graphics window, then do some
more plotting, &c. So far I have the following, which doesn't work:

#v+
waiting = False

def clicked(x, y):
global waiting
print('clicked at %f %f' % (x,y))
waiting = False
return

def wait_for_click(s):
global waiting
waiting = True
s.listen()
while waiting:
time.sleep(1)
return


....
t = turtle.Pen()
s = turtle.Screen()
....
traverse.plot(s, t, "black", scale, adjx, adjy)
wait_for_click(s)
bowditch.plot(s, t, "red", scale, adjx, adjy)
wait_for_click(s)
transit.plot(s, t, "blue", scale, adjx, adjy)
wait_for_click(s)
#v-


Each of my plot(..) calls does some turtle movement, and I want the
program to sit and wait for the user to click the graphics window,
then add the next plot. I've played around with some event handling
examples I found [1], and concluded that the onclick binding only
works while the turtle is doing something. Is that correct? Is there
a way to wait for the click & hear it while the turtle is not doing
anything?


[1] <http://csil-web.cs.surrey.sfu.ca/cmpt120fall2010/wiki/IntroToEventHandling/>


Thanks,
Adam
 
W

woooee

waiting = False
def clicked(x, y):

global waiting

print('clicked at %f %f' % (x,y))

waiting = False

return



def wait_for_click(s):

global waiting

waiting = True

s.listen()

while waiting:

time.sleep(1)

return





...

t = turtle.Pen()

s = turtle.Screen()

...

traverse.plot(s, t, "black", scale, adjx, adjy)

wait_for_click(s)

bowditch.plot(s, t, "red", scale, adjx, adjy)

wait_for_click(s)

transit.plot(s, t, "blue", scale, adjx, adjy)

wait_for_click(s)

Note that the code you posted does not call onclick(). Globals are confusing IMHO. Code becomes cleaner and easier to write and read when you become familiar with classes.

import turtle

class TurtleTest():
def __init__(self):
self.ctr=0

t = turtle.Turtle()
s = turtle.Screen()
s.onclick(self.clicked)
turtle.mainloop()

def clicked(self, x, y):
print self.ctr
if 0 == self.ctr:
self.first()
elif 1 == self.ctr:
self.second()
elif 2 == self.ctr:
self.third()

self.ctr += 1

def first(self):
print "first called"

def second(self):
print "second called"

def third(self):
print "third called"

TT = TurtleTest()
 
A

Adam Funk

I'm trying to get a program to do some plotting with turtle graphics,
then wait for the user to click on the graphics window, then do some
more plotting, &c. So far I have the following, which doesn't work:

#v+
waiting = False

def clicked(x, y):
global waiting
print('clicked at %f %f' % (x,y))
waiting = False
return

def wait_for_click(s):
global waiting
waiting = True
s.listen()
while waiting:
time.sleep(1)
return


...
t = turtle.Pen()
s = turtle.Screen()

Oops, I snipped out two important lines:

#v+
s.onclick(clicked, btn=1)
wait_for_click(s, t)
#v-
...
traverse.plot(s, t, "black", scale, adjx, adjy)
wait_for_click(s)
bowditch.plot(s, t, "red", scale, adjx, adjy)
wait_for_click(s)
transit.plot(s, t, "blue", scale, adjx, adjy)
wait_for_click(s)
#v-


Each of my plot(..) calls does some turtle movement, and I want the
program to sit and wait for the user to click the graphics window,
then add the next plot. I've played around with some event handling
examples I found [1], and concluded that the onclick binding only
works while the turtle is doing something. Is that correct? Is there
a way to wait for the click & hear it while the turtle is not doing
anything?


[1] <http://csil-web.cs.surrey.sfu.ca/cmpt120fall2010/wiki/IntroToEventHandling/>


Thanks,
Adam
 
A

Adam Funk

Note that the code you posted does not call onclick().

It does, actually, but I accidentally snipped it when C&Ping code into
my original post. Sorry!
Globals are
confusing IMHO. Code becomes cleaner and easier to write and read
when you become familiar with classes.

I've already got a module with Course and Traverse classes (this is
for surveying problems). If I have to write a class just to create
one instance of it & call the main() method, I might as well use Java!
;-)

But I'll work through the example you posted --- thanks.
 
D

Dennis Lee Bieber

I'll echo the "Ugh" about the use of global AND ADD a dislike of the
busy loop that only exits if some other return sets a value. If the busy
loop were performing some action that changed the test state within the
loop itself, okay...

-=-=-=-
import threading

evtFlag = threading.Event()

def clicked(x, y):
print("clicked at %f %f" % (x, y))
evtFlag.set()
#don't need "global" as not binding to the name evtFlag
#don't need "return" as falling off the end of the function
# implements return

def wait_for_clicks(s):
evtFlag.clear()
s.listen()
evtFlag.wait()
#don't need "global" or "return"
#don't need busy loop; .wait() blocks until some other thread
# (GUI callback) sets the Event
 
D

Dave Angel

If I have to write a class just to create
one instance of it & call the main() method, I might as well use Java!
;-)

I'm no fan of Java. But it's not about a "main" method, it's about
sharing data between functions. Most of the time non-constant globals
are a mistake. If the data can't be passed as an argument, then it
should probably be part of the instance data of some class. Which class
is a design decision, and unlike Java, I wouldn't encourage writing a
class for unrelated functions, just to bundle them together.


Anyway, back to your problem. Since your code doesn't have threads, it
must have an event loop somewhere. Event loops don't coexist at all
well with calls to sleep().

while waiting:
time.sleep(1)

If you start that code with waiting being true, it will never terminate.

I don't know turtle graphics, but if it's got an event loop, then you
can't write your program procedurally. A function called
"wait_for_click()" is nonsensical.
 
A

Adam Funk

I'm no fan of Java. But it's not about a "main" method, it's about
sharing data between functions. Most of the time non-constant globals
are a mistake. If the data can't be passed as an argument, then it
should probably be part of the instance data of some class. Which class
is a design decision, and unlike Java, I wouldn't encourage writing a
class for unrelated functions, just to bundle them together.

Well, I understand the OO principle there, but it seems practical to
accept a few global variables in the "main" code of a program.
Anyway...

Anyway, back to your problem. Since your code doesn't have threads, it
must have an event loop somewhere. Event loops don't coexist at all
well with calls to sleep().

while waiting:
time.sleep(1)

If you start that code with waiting being true, it will never terminate.

Right. But the following *does* work (although it's probably
offensive):

#v+
def wait_for_click(s, t):
global waiting
waiting = True
s.listen()
t.hideturtle()
t.penup()
while waiting:
t.forward(5)
t.right(5)
return
#v-
 
A

Adam Funk

I'll echo the "Ugh" about the use of global AND ADD a dislike of the
busy loop that only exits if some other return sets a value. If the busy
loop were performing some action that changed the test state within the
loop itself, okay...

TBH, I was originally going to use

input('press RETURN to continue')

but I thought it would be nicer to click on the plot window.

-=-=-=-
import threading

evtFlag = threading.Event()

This is a global variable, but here we're calling a method on it
rather than changing its value (boolean in my original case). Why is
that better in principle?

def clicked(x, y):
print("clicked at %f %f" % (x, y))
evtFlag.set()
#don't need "global" as not binding to the name evtFlag
#don't need "return" as falling off the end of the function
# implements return

Does it do any harm to put an empty "return" at the end of a method?
(It looks tidier to me with the return.)

def wait_for_clicks(s):
evtFlag.clear()
s.listen()
evtFlag.wait()
#don't need "global" or "return"
#don't need busy loop; .wait() blocks until some other thread
# (GUI callback) sets the Event

I tried these and got even worse results --- even Ctrl-C in the xterm
I was running the program from wouldn't kill it; I had to use Ctrl-Z
and kill %1.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top