Hello,
I have posted to the list about threading problems
with my pygtk program and have gotten some
suggestions, some of which I have tried, others I
havent because it would require some major structural
changes to the code. I thought that surrounding gtk
calls in other threads with gtk.threads_enter() and
gtk.threads_leave() would do the trick, but it hasnt
worked.
I'm attaching the program below. If anyone would like
to run it and suggest ways I could get the gui to
update properly, I'd appreciate it. I would like to
not have to restructure the code too much, but if all
else fails, I'm willing to do that. I just don't know
what to go from here... any help is appreciated.
The program is a simple chat program between any
number of nodes on the network. On thread sends out
"heartbeat" udp packets every 10 seconds. Another
thread listens for packets coming in from other nodes
and updates the GUI appropriately. You'll see that you
can receive packets and it will only update on the GUI
if you move the mouse out and back into the
application window. By the way, you wont need two
computers to see what im talking about... you can just
run it on one and you'll see the update problems.
This is my first pygtk program (and python program for
that matter) and I'd appreciate *any* comments,
suggestions. I am really interesting in learing how to
get threaded programs with GUIs right.
Thanks.
--begin code--
#!/usr/bin/env python
# boxes1.py
import gtk
import sys, string
#
# for the client
from socket import socket, AF_INET, SOCK_DGRAM,
SOL_SOCKET, SO_BROADCAST
from time import time, ctime, sleep
import struct
from threading import Lock, Thread, Event
import threading
from time import time, ctime, sleep
import select
import re
class MainView:
def delete_event(self, widget, wvent, data=None):
gtk.mainquit()
return gtk.FALSE
def __init__(self):
# create the window
self.window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)
# set the window size (width, height)
self.window.set_default_size(240, 320)
# set the border width
self.window.set_border_width(5)
# set the title bar text
self.window.set_title("Wireless Device Chat")
# connect the delete_event signal to the main
window.
self.window.connect("delete_event",
self.delete_event)
self.notebook = gtk.GtkNotebook()
self.notebook.set_tab_pos(gtk.POS_TOP)
table = gtk.GtkTable(3,6,gtk.FALSE)
self.window.add(table)
table.attach(self.notebook, 0,6,0,1)
self.notebook.show_tabs = gtk.TRUE
self.notebook.show()
## Page 1 -- Devices
buffer_label = "Devices"
self.frame1 = gtk.GtkFrame("Devices")
self.frame1.set_border_width(3)
self.frame1.set_usize(100, 75)
self.frame1.show()
#self.page1label = gtk.GtkLabel(buffer_label)
#self.frame1.add(self.page1label)
#self.page1label.show()
# make a scrolled window to put our list in
self.scrolled_window = gtk.GtkScrolledWindow()
self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,
gtk.POLICY_ALWAYS)
# add the scrolled window to the frame
self.frame1.add(self.scrolled_window)
# show the scrolled window
self.scrolled_window.show()
self.devices = DeviceList(2, [ "IP Address", "Last
Seen" ])
self.devices.add_gui_object(self)
self.scrolled_window.add(self.devices)
self.devices.show()
self.tab1label = gtk.GtkLabel(buffer_label)
self.notebook.append_page(self.frame1,
self.tab1label)
##
## Page 2 -- Messages
buffer_label = "Messages"
self.frame2 = gtk.GtkFrame("Messages")
self.frame2.set_border_width(3)
self.frame2.set_usize(100, 75)
self.frame2.show()
# make a scrolled window to put our list in
self.messages_scrolled_window =
gtk.GtkScrolledWindow()
self.messages_scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,
gtk.POLICY_ALWAYS)
# add the scrolled window to the frame
self.frame2.add(self.messages_scrolled_window)
# show the scrolled window
self.messages_scrolled_window.show()
self.messages = MessageOutputList()
self.messages.add_gui_object(self)
self.messages_scrolled_window.add(self.messages)
self.messages.show()
self.tab2label = gtk.GtkLabel(buffer_label)
self.notebook.append_page(self.frame2,
self.tab2label)
##
## Page 3 -- Debug
buffer_label = "Debug"
self.frame3 = gtk.GtkFrame("Debug")
self.frame3.set_border_width(3)
self.frame3.set_usize(100, 75)
self.frame3.show()
#self.page2label = gtk.GtkLabel(buffer_label)
#self.frame2.add(self.page2label)
#self.page2label.show()
# make a scrolled window to put our list in
self.debug_scrolled_window = gtk.GtkScrolledWindow()
self.debug_scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,
gtk.POLICY_ALWAYS)
# add the scrolled window to the frame
self.frame3.add(self.debug_scrolled_window)
# show the scrolled window
self.debug_scrolled_window.show()
self.debug = DebugOutputList()
self.debug_scrolled_window.add(self.debug)
self.debug.show()
self.tab3label = gtk.GtkLabel(buffer_label)
self.notebook.append_page(self.frame3,
self.tab3label)
table.show()
# show the window
self.window.show()
class WirelessChatDriver:
def __init__(self):
mv = MainView()
device_dictionary = DeviceDictionary()
hbs = HeartBeatSender(43278, 10)
hbs.add_gui_object(mv)
hbs.start()
hbl = HeartBeatListener(43278)
hbl.set_dictionary(device_dictionary)
hbl.add_gui_object(mv)
hbl.start()
# the gtk main loop
gtk.threads_enter()
gtk.mainloop()
gtk.threads_leave()
# control returns here when mainquit() is called
hbs.shutdown()
hbs.join()
hbl.shutdown()
hbl.join()
class MessageSendView:
def __init__(self, message_text):
self.dialog = gtk.GtkDialog()
self.dialog.set_title("Send Message")
self.label = gtk.GtkLabel("Message to %s:" %
message_text)
self.message_entry = gtk.GtkEntry()
self.dialog.vbox.pack_start(self.label, gtk.TRUE,
gtk.TRUE, 0)
self.dialog.vbox.pack_start(self.message_entry,
gtk.TRUE, gtk.TRUE, 0)
self.label.show()
self.message_entry.show()
self.send_button = gtk.GtkButton("Send")
self.dialog.action_area.add(self.send_button)
self.send_button.connect("clicked",
self.send_button_handler, "send")
self.send_button.show()
self.clear_button = gtk.GtkButton("Clear")
self.dialog.action_area.add(self.clear_button)
self.clear_button.connect("clicked",
self.clear_button_handler, "clear")
self.clear_button.show()
self.dialog.show()
def add_sender_object(self, message_sender):
self.message_sender = message_sender
def send_button_handler(self, widget, data):
self.message_sender.send(self.message_entry.get_text())
self.message_entry.set_text("")
def clear_button_handler(self, widget, data):
self.message_entry.set_text("")
class DeviceList(gtk.GtkCList):
def __init__(self, cols, titles):
gtk.GtkCList.__init__(self, cols, titles)
self.connect("select_row", self.selection_made)
def add_gui_object(self, view):
self.view = view
def selection_made(self, clist, row, column, event,
data=None):
# Get the text that is stored in the selected row
and column
# which was clicked in. We will receive it as a
pointer in the
# argument text.
#text = clist.get_text(row, column)
text = clist.get_text(row, 0)
print ("selected %s\n" % text)
self.view.debug.add("selected device %s" % text)
message_sender = MessageSender(text, 43278)
message_sender.add_gui_object(self.view)
msv = MessageSendView(text)
msv.add_sender_object(message_sender)
return
class MessageOutputList(gtk.GtkCList):
def __init__(self):
gtk.GtkCList.__init__(self, 2, [ "Sender", "Message"
])
self.connect("select_row", self.selection_made)
def add(self, sender, message):
self.append( ["%s" % sender, "%s" % message] )
def add_gui_object(self, view):
self.view = view
def selection_made(self, clist, row, column, event,
data=None):
# Get the text that is stored in the selected row
and column
# which was clicked in. We will receive it as a
pointer in the
# argument text.
#text = clist.get_text(row, column)
text = clist.get_text(row, 0)
print ("Selected %s\n" % text)
self.view.debug.add("selected message %s" % text)
message_sender = MessageSender(text, 43278)
message_sender.add_gui_object(self.view)
msv = MessageSendView(text)
msv.add_sender_object(message_sender)
return
class DebugOutputList(gtk.GtkCList):
def __init__(self):
gtk.GtkCList.__init__(self, 2, [ "Time", "Message"
])
def add(self, message):
self.append( ["%s" % ctime(time()), "%s" % message]
)
class DeviceDictionary:
"""Manage device dictionary"""
def __init__(self):
self.device_dictionary = {}
self.dictionary_lock = Lock()
def update(self, entry):
self.dictionary_lock.acquire()
self.device_dictionary[entry] = time()
self.dictionary_lock.release()
class MessageSender:
"""Sends a message over UDP"""
def __init__(self, dest_ip, dest_port):
self.dest_ip = dest_ip
self.dest_port = dest_port
self.socket = socket(AF_INET, SOCK_DGRAM)
def add_gui_object(self, view):
self.view = view
def send(self, message):
# send the message
self.socket.sendto("message,%s" % message,
(self.dest_ip, self.dest_port))
# add the message to the messages list on the GUI
#self.view.messages.add("<<ME>>", message)
# add a message to the debug list on the GUI
self.view.debug.add("Sent message to %s:%s" %
(self.dest_ip, self.dest_port))
class HeartBeatSender(Thread):
"""Broadcasts a UDP "heartbeat" packet every
specified number of seconds"""
def __init__(self, port, delay):
Thread.__init__(self)
#SERVERIP = '127.0.0.1'
self.SERVERIP = '<broadcast>'
self.PORT = port
self.INTERVAL = delay
self.finished = threading.Event()
self.socket = socket(AF_INET, SOCK_DGRAM)
self.socket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
def add_gui_object(self, view):
self.view = view
def shutdown(self):
"""Shutdown the thread"""
self.finished.set()
def run(self):
while 1:
if self.finished.isSet():
return
self.task()
self.finished.wait(self.INTERVAL)
def task(self):
self.socket.sendto('heartbeat,', (self.SERVERIP,
self.PORT))
gtk.threads_enter()
self.view.debug.add("Sent heartbeat packet")
gtk.threads_leave()
class HeartBeatListener(Thread):
"""Listen for UDP "heartbeat" packets from others"""
def __init__(self, port):
Thread.__init__(self)
#self.hostIP = gethostbyname('localhost')
# The empty string below means INADDR_ANY
self.hostIP = ''
self.port = port
self.recSocket = socket(AF_INET, SOCK_DGRAM)
self.recSocket.setblocking(0)
self.recSocket.bind((self.hostIP, self.port))
self.finished = threading.Event()
self.finished.set()
def shutdown(self):
"""Shutdown the thread"""
self.finished.clear()
def add_gui_object(self, view):
self.view = view
def set_dictionary(self, dictionary):
self.dictionary = dictionary
def run(self):
while self.finished.isSet():
ready = select.select([self.recSocket],[], [], 0)
if self.recSocket in ready[0]:
data, addr = self.recSocket.recvfrom(548)
print "Received packet [" + data + "] from " +
`addr`
gtk.threads_enter()
self.view.debug.add("Received packet from %s" %
addr[0])
gtk.threads_leave()
p = re.compile('(\w+),(.*)')
result = p.search(data)
if result:
if result.group(1) == 'heartbeat':
self.dictionary.update(addr[0])
gtk.threads_enter()
self.view.devices.clear()
gtk.threads_leave()
self.dictionary.dictionary_lock.acquire()
gtk.threads_enter()
for key in
self.dictionary.device_dictionary.keys():
self.view.devices.append([
"%s" % key, "%s" %
ctime(self.dictionary.device_dictionary[key]) ])
gtk.threads_leave()
self.dictionary.dictionary_lock.release()
gtk.threads_enter()
self.view.debug.add("Updated device
list")
gtk.threads_leave()
elif result.group(1) == 'message':
gtk.threads_enter()
self.view.messages.add(addr[0],
result.group(2))
gtk.threads_leave()
else:
pass
def main():
WirelessChatDriver()
return 0
if __name__ == "__main__":
main()
---end code---
__________________________________________________
Do You Yahoo!?
Sign up for SBC Yahoo! Dial - First Month Free
http://sbc.yahoo.com
_______________________________________________
pygtk mailing list [EMAIL PROTECTED]
http://www.daa.com.au/mailman/listinfo/pygtk
Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/