Re: [Tutor] Tkinter, widgets not displaying...

2006-02-10 Thread Kent Johnson
Hugo González Monteverde wrote:
 Hi All,
 
 I wrote a small turn delivering graphical app that is supposed to 
 display turns in a queue.

  def insert(self, turn_string):
  Insert a new turn into the queue, move the rest upwards, 
 delete oldest.

This is overly complicated:

  current_values = [self.read_panel(i) for i in 
 range(len(self.canvases))]

You are going to throw away the first value, there is no need to read it.
  next_values = current_values[:]

There is no need to copy current_values since it is a brand new list 
already.

  next_values[:-1] = current_values[1:]
  next_values[-1] = turn_string

This works but you could just copy the part of current_values you want 
and append turn_string.

Taking all this together, you can write just
next_values = [self.read_panel(i) for i in range(1, len(self.canvases))]
next_values.append(turn_string)

But there is a deeper issue here. You seem to be writing some kind of 
simulation. You might want to consider separating your model - the data 
and mechanics of the simulation - from the GUI - the visual 
representation. The way you have it now, the data is stored in the GUI 
itself. This leads to tight coupling between the model and the GUI. It 
will be hard to test the model or to put a different front-end on it, 
and hard to have a clean conceptual model of the simulation.

For example, you could have this model class:

class QueueModel(object):
   def __init__(self, length=4):
 self.data = [None * length ]

   def insert(self, item):
 self.data.pop()  # remove the first item
 self.data.append(item)

Then in your GUI, your insert() method would become update(self, model). 
Or maybe instantiate the GUI giving it an instance of the model. Your 
main program would drive them both. Oversimplifying by leaving out the 
threading, it would look like
   model = QueueModel(length=4)
   gui = TurnQueue(model, pw=200, ph=100)
   for i in range(100):
 model.insert(str(i))
 gui.update()
 sleep(0.2)

As the model develops, this separation from the GUI will make it much 
easier to work with.

Kent

___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Tkinter, widgets not displaying...

2006-02-10 Thread Kent Johnson
John Fouhy wrote:
 If you want to do multithreaded programming with a GUI, one good way
 is to use .after_idle.
 
 ie, instead of myturns.insert(str(i)), do
 top.after_idle(myturns.insert, str(i))  (I think this is the right
 syntax).  This will cause the mainloop thread to run the code instead.

Another way to do this without a separate thread is to use top.after() 
to schedule the inserts. Schedule the first one before calling 
mainloop(), and have each insert schedule the next one.

Kent

___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Tkinter, widgets not displaying...

2006-02-10 Thread Hugo González Monteverde
Hi Kent and John,

Thanks a lot for the advice on how to improve my program. I will look 
into separating the data and model as John suggests here. I didn't know 
about after_idle() and after(); seems that dir()'ing the classes 
sometimes gives out a lot more information than you can chew at any 
given time :)

Thanks,

Hugo
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


[Tutor] Tkinter, widgets not displaying...

2006-02-09 Thread Hugo González Monteverde
Hi All,

I wrote a small turn delivering graphical app that is supposed to 
display turns in a queue.

If I instantiate the class and call its methods, thus displaying strings 
in several canvases, from the interactive prompt, everything works fine. 
  If I do it when running the script as a programs, nothing gets displayed.

If I call mainloop() at the end of my program, everything gets 
displayed, but then I cannot use my own function classes. This program 
will never respond to events, but just get called from a queue manager 
script for display...

Here's the code:

#!/usr/bin/env python
from __future__ import division
import Tkinter
import tkFont
from Tkconstants import *


from time import sleep

class TurnQueue(Tkinter.Frame):
 def __init__(self, master=None, pw=60, ph=60, panels=4):
 self.ph = ph
 self.pw = pw
 self.panels = panels

 Tkinter.Frame.__init__(self, master)
 self.createWidgets()
 self.pack()

 def createWidgets(self):
 Create four canvases and a warning window.
 self.canvases =  [Tkinter.Canvas(height=self.ph, width=self.pw, 
borderwidth=1, background = '#FF') for i in range(self.panels)]
 self.textbox_ids = []

 # display them all, initialized
 for i in self.canvases:
 id = i.create_text(self.pw//2, self.ph//2, text='', 
font=('bitstream charter', self.ph, 'bold'), fill='red')
 self.textbox_ids.append(id)
 i.pack()

 def write_panel(self, panel_no, newtext):
 writepanel(self, panel, newtext)
 Set panel panel to text newtext
 
self.canvases[panel_no].itemconfigure(self.textbox_ids[panel_no], 
text=newtext)

 def read_panel(self, panel_no):
 textcontents - read_panel(self, panel_no)
 Read panel text contents
 
return(self.canvases[panel_no].itemcget(self.textbox_ids[panel_no], 'text'))

 def insert(self, turn_string):
 Insert a new turn into the queue, move the rest upwards, 
delete oldest.
 current_values = [self.read_panel(i) for i in 
range(len(self.canvases))]
 next_values = current_values[:]
 next_values[:-1] = current_values[1:]
 next_values[-1] = turn_string

 # update all panel values
 for i in range(len(self.canvases)):
 self.write_panel(i, next_values[i])

if __name__ == __main__:
 myturns = TurnQueue(pw=200, ph=100, panels=4)

 for i in range(100):
 myturns.insert(str(i))
 sleep(0.2)
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Tkinter, widgets not displaying...

2006-02-09 Thread Hugo González Monteverde
Sorry for replying to myself, but I found a page that says that it can 
be done without threads... so it hit me: use threads!!! (duh)

Ok, for future reference, this is the part where I use the classes I 
defined:

if __name__ == __main__:
 top = Tkinter.Tk()
 myturns = TurnQueue(top, pw=200, ph=100, panels=4)

 #importing here is ugly, but just for testing.
 from thread import start_new_thread

 start_new_thread(Tkinter.Tk.mainloop, (top,))
 for i in range(10):
 myturns.insert(str(i))
 sleep(0.2)

So now I see my line of canvases displaying numbers going upwards..

Hugo
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Tkinter, widgets not displaying...

2006-02-09 Thread John Fouhy
On 10/02/06, Hugo González Monteverde [EMAIL PROTECTED] wrote:
 Sorry for replying to myself, but I found a page that says that it can
 be done without threads... so it hit me: use threads!!! (duh)

Yes, this will be what you need.  Your GUI won't do anything until you
run mainloop, and mainloop won't exit until you close your GUI.  So if
you want to programmatically feed data in, you need to do it from a
different thread.

One caveat, though ---

 if __name__ == __main__:
  start_new_thread(Tkinter.Tk.mainloop, (top,))
  for i in range(10):
  myturns.insert(str(i))
  sleep(0.2)

By doing this, you are changing the GUI from a different thread from
the one running mainloop.  This is a BAD IDEA.  It can easily lead to
weird bugs that you will struggle to understand.

If you want to do multithreaded programming with a GUI, one good way
is to use .after_idle.

ie, instead of myturns.insert(str(i)), do
top.after_idle(myturns.insert, str(i))  (I think this is the right
syntax).  This will cause the mainloop thread to run the code instead.

HTH!

--
John.
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor