Tkinter/threading issue

J

Jerrad Genson

Hello,

I'm learning Tkinter, and I have an issue that I'd appreciate help
with. I have a program that initializes a GUI (I'll call this the "GUI
process"), then spawns another process that listens on a network via
the TCP/IP protocol for incoming strings (I'll call this the "server
process"). Everything works well up to this point.

What I want to happen next is for the server process to update a label
in the GUI process when it receives a message from the network. First
I tried passing a control variable for the label's text into the
server process, but when I updated the control variable, nothing
happened(no error messages, no feedback of any kind).

I tried piping the message from the server process into the GUI
process using os.write() and os.read() - and this works, but not the
way I need it to. I need the label to be updated automatically,
without any intervention from the user (at the moment, it only works
when the user clicks an "Update" button). I tried having the GUI
process check the pipe for messages automatically, but when I do this
it hangs when there's nothing in the pipe.

I've tried other avenues as well, but probably nothing worth
mentioning. If you have any suggestions, I would be very grateful.
 
T

Thomas Jollans

Hello,

I'm learning Tkinter, and I have an issue that I'd appreciate help
with. I have a program that initializes a GUI (I'll call this the "GUI
process"), then spawns another process that listens on a network via
the TCP/IP protocol for incoming strings (I'll call this the "server
process"). Everything works well up to this point.

Okay, so you want your TCP (or is it UDP? not that it matters - but "TCP/IP"
is not a protocol) server to sit in a separate process. May I ask why? Either
way, you first need to trigger something inside the GUI process when a network
packet arrives. The easiest way would of course be to have the server in the
same process...

Anyway, IPC. You say you're using Ubuntu. If you're only targetting UNIX-like
systems, you can use os.fork() to create the new process -- you're probably
doing so anyway (?) -- and use socket.socketpair or os.pipe to create a pipe
between parent and child. If you're launching the process with
subprocess.Popen, you can simply use the standard files (stdin, stdout,...) as
a pipe. If the processes are completely separate, you'd need to use some
convention to create a socket between the two, eithe a AF_LOCAL/AF_UNIX
socket, or, if you want to support Windows and the likes, a local loopback TCP
socket (->127.0.0.1)

Once you've got a pipe between the two processes - or the network server
within your GUI process - you just have to wait for a message to come flying
in, and then update the widget.

There are a couple of ways of waiting for a message without disrupting the
GUI: you can either create a separate thread in which you, wait for input, or
you can add a piece of code to the main loop (I don't know how difficult this
is with Tkinter -- shouldn't be too tricky) that checks if there's any news,
typically by calling select.select with a zero timeout.
 
J

Jerrad Genson

Thank you for the reply. When I said "TCP/IP" protocol, what I meant
was this: http://en.wikipedia.org/wiki/Internet_Protocol_Suite.

The reason the server is in a separate process is because it needs to
continually be listening for network packets, which would disrupt the
GUI. In any case, that part is working fine - I actually have
everything you mentioned implemented already, in mostly the way you
described it, except for the very last part where the GUI label gets
updated. I tried creating a "listener" thread that waits for a message
to appear on its pipe from the server process, and then updates the
label using a control variable. Again, nothing happens. The program
doesn't hang, or throw an exception, but the label isn't being updated
either - I presume because I'm trying to set the control variable in a
thread separate from the GUI. I'll post some of the code so you can
see what I'm talking about.
 
J

Jerrad Genson

class MessageServer:
'''Creates a message server object that listens for textual
information
and sends it back to the main program. Intended to be spawned
as a
separate process.
'''

def __init__(self, port_number, server_send, server_receive):
'''@param server_send The pipe MessageServer uses to send text
back to the main program.
@param port_number The port the server listens on the
network at.
'''

self.server_send = server_send
self.server_receive = server_receive
self.client_socket = None
self.address = None

self.error_logger =
errorlogger.ErrorLogger(program_name=PROGRAM_NAME)

self.server_socket = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
self.server_socket.bind(('localhost', port_number))
self.server_socket.listen(1)

self.listen()

def listen(self):
# listen for messages until process is killed
# if a message is received, send it back to the main program
(self.client_socket, self.address) =
self.server_socket.accept()
while True:
input_stream = str(self.client_socket.recv(1024))
if len(input_stream) != 0:
os.write(self.server_send, input_stream)
input_stream = None
 
J

Jerrad Genson

def check_message(self, spawn=True):
'''Method for pulling message from server process.'''

if spawn: self.pid2 = os.fork()
if self.pid2 == 0:
if verbose: print('message checker initialized')
# repeat message check forever
while True:
incoming_data = os.read(self.client_receive, 1024)
if verbose: print('Message receive successful')
if verbose: print('Message: ' + incoming_data)
self.update_messages(incoming_data)

def update_messages(self, new_text=False):
'''Adds new string to conversation box.
@param new_text The string to be added.
'''

if not new_text: new_text = INCOMING_DATA
try:
current_text = self.chat_text.get()
current_text += '\n' + new_text
self.chat_text.set(current_text)

except:
error_details = 'Fatal Error. Message update failed.
Program will now close.'
tkMessageBox.showerror('Update Error', error_details)
error_details += '\nINCOMING_DATA: ' + new_text
error_details += '\ncurrent_text: ' + current_text
stack_trace = traceback.format_stack()
self.error_logger.log_error(stack=stack_trace,
details=error_details)
self.exit_program(force=True)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,233
Members
46,820
Latest member
GilbertoA5

Latest Threads

Top