Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: Python: Python

Why does this code crash python?

 

 

Python python RSS feed   Index | Next | Previous | View Threaded


Mythmon at gmail

Nov 11, 2006, 11:17 AM

Post #1 of 7 (408 views)
Permalink
Why does this code crash python?

I am trying to make a program that will basically simulate a chess
clock in python. To do this I have two threads running, one that
updates the currently running clock, and one that watches for a
keypress. I am using the effbot Console module, and that is where I get
the events for the keypresses. But when I press space it crashes
shortly after. The program stops, the Console closes, and windows says
that the program pythonw.exe has had an unexpected reason to shut down.
I am using python2.5.

If there is a simpler way to do this ( I tried without threads but had
some problems with the Console module giving me input right and still
letting the timer display count down ) I would like to know about it. I
am not a very good programmer, and don't have much experience with
python

note: A chess clock is two timers that count down from a set time, and
when you start one of the timer the other stops.

Code below:

import time
import Console
from threading import Thread

c = Console.getconsole()

turn = 0

class timer (Thread):
def run ( self ):
global turn
oldTime = [time.time(), time.time()]
timeLeft = [260, 260]

go = True
while go:
newTime = time.time()
timeLeft[turn] -= newTime - oldTime[turn]
oldTime[turn] = newTime
minutes = [str(int(timeLeft[0]//60)),
str(int(timeLeft[1]//60))]
seconds = [str(timeLeft[0]%60)[0:5],
str(timeLeft[1]%60)[0:5]]

if float(seconds[0]) < 10: seconds[0] = '0' +
seconds[0][:-1]
if float(seconds[1]) < 10: seconds[1] = '0' +
seconds[1][:-1]

c.text(3,3,minutes[0] + ':' + seconds[0])
c.text(12,3,minutes[1] + ':' + seconds[1])

time.sleep(.1)

class eventMonitor (Thread):
def run ( self ):
global turn
go = True
while go:
event = c.peek()
if event != None:
c.get()
if event.type == 'KeyPress':
if event.keycode == 32:
if turn == 1: turn = 0
if turn == 0: turn = 1
c.text(10,20,'1')

timer().start()
eventMonitor().start()

--
http://mail.python.org/mailman/listinfo/python-list


irmen.NOSPAM at xs4all

Nov 11, 2006, 11:28 AM

Post #2 of 7 (396 views)
Permalink
Re: Why does this code crash python? [In reply to]

Mythmon [at] gmail wrote:
> I am trying to make a program that will basically simulate a chess
> clock in python. To do this I have two threads running, one that
> updates the currently running clock, and one that watches for a
> keypress. I am using the effbot Console module, and that is where I get
> the events for the keypresses. But when I press space it crashes
> shortly after. The program stops, the Console closes, and windows says
> that the program pythonw.exe has had an unexpected reason to shut down.
> I am using python2.5.

Try not (re)using an object created in a certain thread in another thread.
You're creating a console object "c" and use it in both threads.

Try creating the console object for each thread by itself,
i.e. remove the global "c = Console.getconsole()" and replace
it by a getconsole() inside each thread class.

I don't have the Console module so I don't know if it fixes things
for you, but give it a try :)

--Irmen
--
http://mail.python.org/mailman/listinfo/python-list


Mythmon at gmail

Nov 11, 2006, 12:28 PM

Post #3 of 7 (395 views)
Permalink
Re: Why does this code crash python? [In reply to]

On Nov 11, 11:28 am, Irmen de Jong <irmen.NOS...@xs4all.nl> wrote:
> You're creating a console object "c" and use it in both threads.
>
> Try creating the console object for each thread by itself,
> i.e. remove the global "c = Console.getconsole()" and replace
> it by a getconsole() inside each thread class.
>
> I don't have the Console module so I don't know if it fixes things
> for you, but give it a try :)

Well, I tried that, and it did something. It made it so the space bar
switched the clock once, but not a second time. And it still crashed,
but not always at the same time, sometimes it would do it the second
time I hit the space bar, sometimes before I hit it the first time, but
always when i did something that would generate a Console event (moving
the mouse or something). So, I thought it had something to do with
Console not working well with threads, so I took it completely out of
threads and had just the event checking loop run by itself, and it
still crashed. So apparently I am using the Console events wrong. I am
going to try and find some examples online on how to watch for events
with it, but if worse comes to worse I can try and use keyboard hooks,
I've looked at those before, and they might work better.

--Mythmon

--
http://mail.python.org/mailman/listinfo/python-list


Mythmon at gmail

Nov 11, 2006, 4:48 PM

Post #4 of 7 (389 views)
Permalink
Re: Why does this code crash python? [In reply to]

On Nov 11, 3:23 pm, Dennis Lee Bieber <wlfr...@ix.netcom.com> wrote:
> I have no knowledge of Console, but under Windows I was able to hack
> this (see bottom of post) together using only one non-portable library
> -- msvcrt. Oh, and an ActiveState 2.4 build. I'm not touching 2.5 until
> a lot of 3rd party stuff is rebuilt and installers are available.
>

And here I am thinking this was going to be an easy project. I haven't
had a chance to work on this since my last message, but that example
should be a good thing, since now i have an example of how threads
/should/ be done. I was pretty much just guessing so far.

>
> > newTime = time.time()
> > timeLeft[turn] -= newTime - oldTime[turn] I'd duplicated this form, but it is wrong... when the clock swaps it
> will immediately decrement the current clock by the difference since it
> was last updated!
>
> > if float(seconds[0]) < 10: seconds[0] = '0' +
> > seconds[0][:-1]
> > if float(seconds[1]) < 10: seconds[1] = '0' +
> > seconds[1][:-1] I've not handled leading 0s on the seconds field (I tried, but can't
> figure out how to specify zero-filled field width for floats -- works
> for integers)
>

I ran into the same problem, thats why I did it the way I did, instead
of continuing to wrestle with python's string formatting.

> > go = True
> > while go: Why define "go" when it never changes

Whenever I make infinite loops, I put in a control variable so I could
stop it if I want, which I was planning on doing, when the time runs
out or something like that.


>
> Note that I lock access to timeLeft and side to ensure they don't
> change in the middle of access; may not have been critical, but why risk
> it (and if the code gets expanded so the changes take more time -- or
> multiple look-ups using side).
>
> getch() doesn't seem to return a value for <ctrl-c>, so I used
> <ctrl-z>
>
> Uh, it also updates the time with an invalid negative before it
> exits <G>

Now to see if I can get this to work for my self with an example. This
is more a learning project than something that has to be done.

--
http://mail.python.org/mailman/listinfo/python-list


mblume at socha

Nov 12, 2006, 4:40 AM

Post #5 of 7 (389 views)
Permalink
Re: Why does this code crash python? [In reply to]

<Mythmon [at] gmail> schrieb
> I am trying to make a program that will basically simulate
> a chess clock in python. ...
> ... it crashes shortly after.

Can't help you on why it crashes, but

>
> class eventMonitor (Thread):
> def run ( self ):
> [snipped]
> if event.keycode == 32:
> if turn == 1: turn = 0
> if turn == 0: turn = 1
>

looks wrong to me. This is supposed to switch between the
players (0 and 1), but the first if changes turn to 0, the
second changes it immediately back to 1.

I'd do:
if turn == 1: turn = 0
else: turn = 1

HTH
Martin


--
http://mail.python.org/mailman/listinfo/python-list


scott.daniels at acm

Nov 12, 2006, 5:14 PM

Post #6 of 7 (386 views)
Permalink
Re: Why does this code crash python? [In reply to]

Mythmon [at] gmail wrote:
>...

>>> if float(seconds[0]) < 10: seconds[0] = '0' +
>>> seconds[0][:-1]
>>> if float(seconds[1]) < 10: seconds[1] = '0' +
>>> seconds[1][:-1] I've not handled leading 0s on the seconds field (I tried, but can't
>> figure out how to specify zero-filled field width for floats -- works
>> for integers)
>
> I ran into the same problem, thats why I did it the way I did, instead
> of continuing to wrestle with python's string formatting.

For this portion, you could quite simply:

totalsecs = int(whatever)
print '%02d:%02d' % divmod(totalsecs, 60)

Or, depending on your leading zero requirements:

totalsecs = int(whatever)
print '%2d:%02d' % divmod(totalsecs, 60)

--Scott David Daniels
scott.daniels [at] acm
--
http://mail.python.org/mailman/listinfo/python-list


ms at cerenity

Nov 17, 2006, 4:43 PM

Post #7 of 7 (385 views)
Permalink
Re: Why does this code crash python? [In reply to]

The reason for your problem is that, at it's core, it's not threadsafe - you
have a shared data value that 2 distinct things are trying to update.

Mythmon [at] gmail wrote:
...
> from threading import Thread

The following value is updated by 2 things trying to run in parallel.

> c = Console.getconsole()

The following value is used for communications.

> turn = 0


> class timer (Thread):
> def run ( self ):
> global turn
....
> go = True
> while go:
> newTime = time.time()
> timeLeft[turn] -= newTime - oldTime[turn]
> oldTime[turn] = newTime

value that "turn" relates to here is merely read. On the surface this looks
safe, but if turn changes in value between any of these 3 "reads" of the
value of "turn", then it will go wrong.

> c.text(3,3,minutes[0] + ':' + seconds[0])
> c.text(12,3,minutes[1] + ':' + seconds[1])

c gets updated here.

> class eventMonitor (Thread):
> def run ( self ):
> global turn

Shared data, but this is the only writer to this value.

> go = True
> while go:
> event = c.peek()
> if event != None:
> c.get()
... (turn gets updated here as well, which can cause a race hazard in timer)
> c.text(10,20,'1')

c effectively changes in value here as well.

> timer().start()
> eventMonitor().start()

Now you *could* introduce locks into this if you wanted. The alternative
approach is to recognise that the real reason you're hitting problems is
because you're using shared data, and that an alternative is to send
messages between the things you're using.

ie collect events from the keyboard,
send them to something handling the logic,
have that send a message (as you essentially do with "turn" above) to the
timer logic,
and then have that send a message to the display.

That way, since you have explicit hand off of data you don't run the risk of
your code crashing. (you also avoid a few other classes of problems :)

The way this would work with Kamaelia (given I've just posted elsewhere in
the thread about that) is you could build a simple pipeline as follows:

Pipeline( KeyEvent(key_events = { pygame.K_SPACE: ("SPACE", "outbox")} ),
ChessTurnLogic(),
TimerLogic(),
Ticker(background_colour=(128,48,128),
render_left = 1,
render_top = 1,
render_right = 600,
render_bottom = 200,
position = (100, 300),
)
).run()

This uses pygame for the display instead of the console which you're using.
It takes keypresses and turns them into a message to trigger the chess turn
logic to emit whose turn it is.

This then feeds into somethin that manages the timer logic - which in this
case emits a message (to be displayed by the ticker) about whose just
finished their turn, and how much time they spent on their turn.

KeyEvent and Ticker are pre-existing components.

The ChessTurnLogic looks like this:

class ChessTurnLogic(Axon.Component.component):
def main(self):
myturn, notmyturn = "white", "black"
self.send(myturn, "outbox")
while 1:
while self.dataReady("inbox"):
self.recv("inbox")
myturn, notmyturn = notmyturn, myturn
self.send(myturn, "outbox")
if not self.anyReady():
self.pause()
yield 1


The Timer Logic looks like this:

class TimerLogic(Axon.Component.component):
def main(self):
times_info = {}
player = None
while 1:
while self.dataReady("inbox"):
new_player = self.recv("inbox")
now = time.time()

if player is not None:
(total, last) = times_info[player]
total = total + (now - last)
times_info[player] = (total, now)
self.send(player + " " + str(total) + "\n\n", "outbox")

player = new_player
try:
(total, last) = times_info[player]
times_info[player] = (total, now)
except KeyError:
times_info[player] = (0, now)

if not self.anyReady():
self.pause()

yield 1

There's probably shorter ways of dealing with the timer logic, but it'll
also work for a draughts (checkers) logic component:

class DraughtsTurnLogic(Axon.Component.component):
def main(self):
myturn, notmyturn = "black", "white"
self.send(myturn, "outbox")
while 1:
while self.dataReady("inbox"):
self.recv("inbox")
myturn, notmyturn = notmyturn, myturn
self.send(myturn, "outbox")
if not self.anyReady():
self.pause()
yield 1

Or indeed a game where there are more than 2 players. (Which is a nice
sideeffect of decoupling your code like this)

The full thing including imports looks like this for reference:

#!/usr/bin/python

import Axon
import time
import pygame
from Kamaelia.Chassis.Pipeline import Pipeline
from Kamaelia.UI.Pygame.Ticker import Ticker
from Kamaelia.UI.Pygame.KeyEvent import KeyEvent

class ChessTurnLogic(Axon.Component.component):
def main(self):
myturn, notmyturn = "white", "black"
self.send(myturn, "outbox")
while 1:
while self.dataReady("inbox"):
self.recv("inbox")
myturn, notmyturn = notmyturn, myturn
self.send(myturn, "outbox")
if not self.anyReady():
self.pause()
yield 1

class TimerLogic(Axon.Component.component):
def main(self):
times_info = {}
player = None
while 1:
while self.dataReady("inbox"):
new_player = self.recv("inbox")
now = time.time()

if player is not None:
(total, last) = times_info[player]
total = total + (now - last)
times_info[player] = (total, now)
self.send(player + " " + str(total) + "\n\n", "outbox")

player = new_player
try:
(total, last) = times_info[player]
times_info[player] = (total, now)
except KeyError:
times_info[player] = (0, now)

if not self.anyReady():
self.pause()

yield 1

Pipeline( KeyEvent(key_events = { pygame.K_SPACE: ("SPACE", "outbox")} ),
ChessTurnLogic(),
TimerLogic(),
Ticker(background_colour=(128,48,128),
render_left = 1,
render_top = 1,
render_right = 600,
render_bottom = 200,
position = (100, 300),
)
).run()

(I've tested the above code BTW. It's not pretty, but it works :).
Prettiness could be added in all sorts of ways though :-)


Regards,


Michael.
--
Kamaelia Project Lead/Dust Puppy
http://kamaelia.sourceforge.net/Home
http://yeoldeclue.com/blog





--
http://mail.python.org/mailman/listinfo/python-list

Python python RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.