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/

Reply via email to