It depends on what you need to do, and on what you are comfortable using. Threads are good if what you are doing is going to take a long time and can't be divided up into small chunks that can be executed in an idle callback. Even if something can be done in multiple idle handlers I find that it is *sometimes* easier to use a separate thread.
The primary disadvantage of threads is that synchronisation problems and race conditions can get very complex if you aren't careful. Use Python's 'Queue' module for everything; if your algorithm requires something more complicated than Queue then change your algorithm ;-)
I have attached the new incarnation of the IconView. It now implements the scrolling, too. Unfortunately, if I scroll too quickly, the application locks. One solution (that I don't like too much) is to change the update policy for the scrollbar.
Any ideas on why is this lock happening ?
TIA,
Ionutz
import sys
if sys.platform.startswith("win"):
# Fetchs gtk2 path from registry
import _winreg
import msvcrt
try:
k = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "Software\\GTK\\2.0")
except EnvironmentError:
print "You must install the Gtk+ 2.2 Runtime Environment to run this program"
while not msvcrt.kbhit():
pass
sys.exit(1)
else:
gtkdir = _winreg.QueryValueEx(k, "Path")
import os
# we must make sure the gtk2 path is the first thing in the path
# otherwise, we can get errors if the system finds other libs with
# the same name in the path...
os.environ['PATH'] = "%s/lib;%s/bin;" % (gtkdir[0], gtkdir[0]) +
os.environ['PATH']
import pygtk
pygtk.require ('2.0')
import gtk
import threading
import array
# ------------- ADT part -----------------
class Fifo:
def __init__(self):
self.__data = array.array('l')
def peek(self):
return self.__data[-1]
def empty(self):
return len(self.__data) == 0
def push(self, item):
self.__data.insert(0, item)
def pop(self):
return self.__data.pop()
def clear(self):
while len(self.__data) != 0:
self.__data.pop()
class Stack:
def __init__(self):
self.__data = array.array('l')
def peek(self):
return self.__data[-1]
def empty(self):
return len(self.__data) == 0
def push(self, item):
self.__data.append(item)
def pop(self):
return self.__data.pop()
def clear(self):
while len(self.__data) != 0:
self.__data.pop()
# ------------- Icon View part -----------------
class IconView(gtk.HBox):
def __init__(self):
gtk.HBox.__init__(self, gtk.FALSE, 0)
self.__area = gtk.DrawingArea()
self.pack_start(self.__area, gtk.TRUE, gtk.TRUE, 0)
self.__area.show()
self.__scrollbar = gtk.VScrollbar(adjustment=None)
self.pack_start(self.__scrollbar, gtk.FALSE, gtk.FALSE, 0)
self.__scrollbar.show()
self.__selected = None # nothing selected
self.__items = 0 # number of items
self.__current_row = 0
self.__rows = 1 # number of rows
self.__cols = 1 # number of columns
self.__item_width = 1 # item width
self.__item_height = 1 # item height
self.__item_border = 0
self.__show_labels = True
# threading
self.__icon_lock = threading.Lock()
self.__icon_event = threading.Event()
self.__icon_event.clear()
self.__icon_queue = Fifo()
self.__icon_thread = threading.Thread(None,
self.item_thread,
'label',
(self.__icon_queue,
self.__icon_event,
self.__icon_lock,
self.get_icon,
self.display_icon,
'label'))
self.__icon_thread.start()
# we need this, otherwise the whole app stops responding
# self.__scrollbar.set_update_policy(gtk.UPDATE_DELAYED)
self.__adjustment = self.__scrollbar.get_adjustment()
self.__adjustment.set_all(0,0,0,0,0,0)
self.__adjustment.connect("value_changed", self.on_scroll)
self.__area.connect("configure_event", self.on_configure)
self.__area.connect("expose-event", self.on_expose)
self.__area.connect("unrealize", self.on_unrealize)
def on_scroll(self, adj):
current_row = int(adj.value)
if current_row != self.__current_row:
self.__area.queue_draw()
def on_unrealize(self, area, data = None):
self.__icon_lock.acquire()
self.__icon_queue.clear()
self.__icon_queue.push(-1)
self.__icon_lock.release()
self.__icon_event.set()
def item_thread(self, queue, event, lock, get_item, display_item, name):
while True:
lock.acquire()
while queue.empty():
lock.release()
event.wait()
lock.acquire()
event.clear()
index = queue.peek()
lock.release()
if index == -1:
return # we've been signalized to exit the thread
item = get_item(index)
lock.acquire()
if not queue.empty():
if index == queue.peek():
display_item(index, item)
queue.pop()
lock.release()
def get_label(self, index):
return ('Item %d' % index)
def set_layout_label(self, layout, label, max_width):
layout.set_text(label)
width, height = layout.get_pixel_size()
i = -3
while width > max_width:
label0 = label[:i] + '...'
layout.set_text(label0)
width, height = layout.get_pixel_size()
i -= 1
return layout
def display_label(self, index, label):
row = index / self.__cols - self.__current_row
col = index % self.__cols
x = col * self.__item_width + self.__item_border
y = row * self.__item_height + self.__item_border
self.__window.draw_layout(self.__gc,
x,
y,
self.set_layout_label(self.__pangolayout,
label,
self.__item_width - 2 *
self.__item_border))
def get_icon(self, index):
return ('Icon %d' % index)
def display_icon(self, index, icon):
row = index / self.__cols
col = index % self.__cols
x = col * self.__item_width + self.__item_border
y = row * self.__item_height + self.__item_border
gtk.threads_enter()
#print '-- display icon "%s"' % icon
gtk.threads_leave()
def on_expose(self, area, event):
rect = event.area
self.__pangolayout = area.create_pango_layout("")
self.__style = area.get_style()
self.__window = area.window
self.__gc = self.__style.fg_gc[gtk.STATE_NORMAL]
# determin the area that needs to be drawn
col1 = rect.x / self.__item_width
col2 = min((rect.x + rect.width) / self.__item_width + 1, self.__cols)
row1 = rect.y / self.__item_height
row2 = min((rect.y + rect.height) / self.__item_height + 1, self.__rows) # we
don't let row2 be more than the total number of rows
self.__icon_lock.acquire()
self.__icon_queue.clear()
self.__current_row = int(self.__adjustment.value)
if col1 == 0 and col2 == 0:
for row in range(row1, row2):
if row < self.__items: # the col is always 0
index = row + self.__current_row
self.__icon_queue.push(index)
# labels
label = self.get_label(index)
self.display_label(index, label)
else:
for row in range(row1, row2):
for col in range(col1, col2):
index = (row + self.__current_row) * self.__cols + col
if index < self.__items:
self.__icon_queue.push(index)
# labels
label = self.get_label(index)
self.display_label(index, label)
self.__icon_lock.release()
self.__icon_event.set()
return gtk.TRUE
def on_configure(self, widget, event, data=None):
width = event.width
height = event.height
self.__cols = width / self.__item_width
if self.__cols <= 0:
self.__cols = 1 # we can't have less than 1 column !
self.__rows = self.__items / self.__cols
while self.__rows * self.__cols < self.__items:
self.__rows += 1
page_size = int(height / self.__item_height)
if page_size < 1:
page_size = 1
if page_size > self.__rows:
page_size = self._rows
self.__adjustment.set_all(0, 0, self.__rows, 1, page_size, page_size)
def set_all(self, items, width, height, border, show_labels = True):
# set all properties of the icon view with one call
# items - number of items fromthe view
# width - width of one item
# height - height of one item
# border - the border arround the image / text
# show_labels - if true, print some labels under the item
if items < 0:
items = 0
if width < 1:
width = 1
if height < 1:
height = 1
if border < 0:
border = 0
self.__items = items
self.__item_width = width
self.__item_height = height
self.__item_border = border
self.__show_labels = show_labels
# redraw all items now ...
def get_selected(self):
return self.__selected
def set_selected(self, value):
self.__selected = value
def get_rows(self):
return self.__rows
def get_cols(self):
return self.__cols
selected = property(get_selected, set_selected, None, 'selected item')
rows = property(get_rows, None, None, 'number of rows')
cols = property(get_cols, None, None, 'number of rows')
# ------------- Test part -----------------
class Test:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
# You should always remember to connect the delete_event signal
# to the main window. This is very important for proper intuitive
# behavior
self.window.connect("delete_event", self.delete_event)
# Create an icon-view and adds it to the window
icon_view = IconView()
icon_view.set_all(300, 69, 128, 5)
#icon_view.set_size_request(400, 300) # make the icon view a bit bigger
self.window.add(icon_view)
icon_view.show()
# the last thing we do is showing the window
self.window.show()
def main(self):
gtk.main()
def delete_event(self, widget, event, data=None):
gtk.main_quit()
return gtk.FALSE
if __name__ == "__main__":
gtk.threads_init()
gtk.threads_enter()
test = Test()
test.main()
gtk.threads_leave()
_______________________________________________ pygtk mailing list [EMAIL PROTECTED] http://www.daa.com.au/mailman/listinfo/pygtk Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/
