Ok, I really hated to resort to threading, but it seems like I have no other choice. I am writing a client script for the "instant message" server I just finished writing. Sadly, I am having a slight problem with threading. What I would like to do is have two threads running simultaneously: thread one would control the main loop of the GUI, thread two would receive messages sent from the server. Yet, I am having trouble with this.

My code (Note, this is just the part that the threading concerns):

import threading

class RecvData(threading.Thread):
    def __init__(self, client):
        print "RecvData thread started."
        while True:
            client.recvdata()

class GUILoop(threading.Thread):
    def __init__(self, myapp):
        print "GUILoop thread started."
        myapp.mainloop()
        
# Client Setup
client = ChatClient()
s = client.connect(host, port)
# GUI Setup
master = Tk()
myapp = MyGUI()
client.msgbox = myapp.setup(master, Tkinter, client)

# Threads
RecvData(client).start()
GUILoop(myapp).start()

Now, when I run this code, only the first thread executes. So what am I doing wrong and how can I fix this to make both threads run at the same time?

Thanks very much in advance.

Recommended Answers

All 11 Replies

In every class in the __init__ method you have to put

threading.Thread.__init__(self)

Without this threading with classes just dosent work properly.

So for example

import threading

class Test(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        #dostuff
T = Test()
T.start()

Hope that helps

Ok thanks, that just about did it. Now I just have one slight problem. How would I pass a variable to the "run" method?

For example:

import threading

myapp = MyGUI()

class Test(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self, myapp):
        myapp.mainloop()
T = Test()
T.start()

Because if I just replace T = Test() with T = Test(myapp), the argument it pass to "__init__", not "run". How would I go about doing this?

Thanks again.

Because if I just replace T = Test() with T = Test(myapp), the argument it pass to "__init__", not "run". How would I go about doing this?

There might be a smarter way of doing this, but you could store those variables in a member at the __init__ function and then access those members as needed in run.

It;s just personal preference, but I prefer multiprocessing to threading (If you are using Python2.5 or lower, then you will have to download pyprocessing and substitute "processing" for "multiprocessing" in the code below). http://pyprocessing.berlios.de/ This is just something used to test a dictionary and a list as a way to pass variables to one of the two processes that are running.

import time
from multiprocessing import Process, Manager


def test_f(test_d, test_l):
   test_d['2'] = 2
   test_d["QUIT"] = False
   while test_l[0] == "False":
      print "test_f", test_d["QUIT"], test_l
      test_d["ctr"] += 1
      time.sleep(1.0)
     
def test_f2(name):
    for j in range(0, 10):
       print "   test_f2", j
       time.sleep(0.5)

if __name__ == '__main__':
   manager = Manager()

   test_d = manager.dict()
   test_list = manager.list()
   test_list.append("False")
   test_d["ctr"] = 0
   test_d["QUIT"] = False
   
   p = Process(target=test_f, args=(test_d,test_list,))
   p.start()
##   p.join()
   
   p2 = Process(target=test_f2, args=('P2',))
   p2.start()

   #--- Change the dictionary/list so the first process ends
   time.sleep(2.0)
   test_d["QUIT"] = True
   test_list[0] = "True"
   print "\ntest_d/list changed"
   time.sleep(3.0)

   print "terminate first process just to be sure"
   p.terminate()   ##  p terminated here
   print "data from first process", test_d, test_list

Thanks for the idea jlm, except I actually think woooee has more of what I am looking for (running two pieces of code continuously and simultaneously).

Except now I get an error without a traceback when I run my script.

X Error of failed request: BadIDChoice (invalid resource ID chosen for this connection)
Major opcode of failed request: 45 (X_OpenFont)
Resource id in failed request: 0x6e00010
Serial number of failed request: 72
Current serial number in output stream: 73

What can I do to fix this?

Thanks again.

Ok, sorry but disregard my last post. I think I'm going to just stick with threading. Anyways, I think I am being a little unclear in what my real problem is. It is rather difficult to explain in words, so I will just give an example.

import threading

class RecvData(threading.Thread):
    def __init__(self, client):
        threading.Thread.__init__(self)
        client.recvdata()
        print "Successfully received data."

class GUILoop(threading.Thread):
    def __init__(self, myapp):
        threading.Thread.__init__(self)
        print "Checkpoint Two"
        myapp.mainloop()
        print "Checkpoint Three"
        
# Client Setup
client = ChatClient()
s = client.connect(host, port)
# GUI Setup
master = Tk()
myapp = MyGUI()
client.msgbox = myapp.setup(master, Tkinter, client)

# Threads
print "Checkpoint One"
GUILoop(myapp).start()
print "Checkpoint Four"
RecvData(client).start()

When I run this code, my output is like such:

Checkpoint One
Checkpoint Two

As you can see, it just doesn't get past the ".mainloop()" of my GUI (line 13 in the code), even though it's in a thread. So...to put it in the most simple terms...how can I make my script get to "Checkpoint Three"?

Thanks in advance.

myapp = MyGUI()

There is no MyGUI() class or function so who knows what myapp contains. I don't see an import Tkinter either. Also, try a myapp.mainloop() right after the myapp = MyGUI to test if the mainloop() is working properly, and then print myapp in the GUILoop class and where it is created, to see if it is being passed correctly. HTH.

Ok, here's my MyGUI class:

from Tkinter import *

class MyGUI(Frame):
    
    def __init__(self): pass

    def setup(self, master, Tkinter, client):
        Frame.__init__(self, master)
        self.pack()
        scrollbar = Scrollbar(master)
        scrollbar.pack(side=RIGHT, fill=Y)
        
        self.msgbox = Text(master)
        self.msgbox.focus_set()
        self.msgbox.pack()
        
        self.msgbox.config(yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.msgbox.yview)
        return self.msgbox
    
    def setup2(self):
        self.entrything = Entry()
        self.entrything.pack()
    
        self.contents = StringVar()
        self.contents.set("")
        self.entrything["textvariable"] = self.contents
        self.entrything.bind('<Key-Return>', self.print_line)
        
    def print_line(self, event):
        "Gets the content of the entry box and adds it to the main text area"
        mytext = "Foo: "+self.contents.get()
        self.entrything.delete(0, END)
        self.addmsg(mytext, client)
        
    def addmsg(self, chatstr, client):
        "Actually adds the contents of the entry box to the text area"
        if chatstr == "Foo: ":
            pass
        else:
            client.msgbox.insert(END, chatstr + "\n")
            client.senddata(chatstr)

(The reason I have two "setup" methods is because in the first one I return a variable, and in the second one I have to bind an action to a key. As far as I know, these can't both be done within the same method.)

Also, if I put a myapp.mainloop() right after the myapp = MyGUI, the GUI runs perfectly.

Thanks again.

(The reason I have two "setup" methods is because in the first one I return a variable, and in the second one I have to bind an action to a key. As far as I know, these can't both be done within the same method.)

That is misinformation, what led you to believe that?

That is misinformation, what led you to believe that?

I for some reason thought that binding a key to an action was a looping action (like the .mainloop() of a GUI). I'm not sure where I got that notion, but I just combined the two and received no errors. Thanks for that.

Back to the original problem, what can I do to fix the problem stated in my last post?

Thanks again.

Well I finally solved the problem, myself. I did this while doing another script. When doing that other script, I got stuck and looked for some samples on the web...somebody on the web used a method named "after". I thought about applying that method to my current situation and it worked! In case anybody wants to see the actual fix:

def recvdata1():
    client.recvdata()
    print "Successfully received data."
    master.after(150,recvdata1)       

client = ChatClient()
master = Tk()
myapp = MyGUI(master)
myapp.after(150,recvdata1)  #The first argument is the time in milliseconds, the second is the function to run after the time set in the first argument has passed.
myapp.mainloop()

Well, thanks to everybody that helped.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.