Reputation: 984
I've been having difficulty with multithreading and tkinter. slxl
is a module I use that works and its functions do not return values (just to make sure there is no confusion.)
Here is code that has worked in the past:
#relevant tkinter code
def create_widgets():
self.updatebttn = Button(self, text='Auto Update', command=self.spawnthread)
self.updatebttn.grid(row=1, column=0, sticky=N)
self.progressbar = Progressbar(self, orient='horizontal',
length=300, mode='determinate')
self.progressbar.grid(row=1, column=1, sticky=W)
self.progressbar["maximum"] = (slxl.sizeFinder()) * 1.1
def spawnthread(self):
self.updatebttn.config(state="disabled")
self.thread = ThreadedClient1(self.queue)
self.thread.start()
self.periodiccall()
def periodiccall(self):
if self.thread.is_alive():
self.after(100, self.periodiccall)
self.progressbar.step(500)
else:
self.updatebttn.config(state="active")
self.progressbar.stop()
class ThreadedClient1(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
def run(self):
time.sleep(1)
importer = slxl.runImportAndCoordAdder()
self.queue.put(importer)
The thing is, I would like ThreadedClient1 to be able to take the function it's putting into the queue, in this case slxl.runImportAndCoordAdder()
as an argument, since I use ThreadedClient
more than once with different functions (I have an identical use of it later on in the program only with a different function) and I don't like having ThreadedClient1
and ThreadedClient2
, the only difference being importer =
different functions.
I did attempt a solution with lambda as such:
def spawnthread(self):
self.updatebttn.config(state="disabled")
self.thread = ThreadedClient1(self.queue, lambda: slxl.runImportAndCoordAdder())
self.thread.start()
self.periodiccall()
#periodic call is the same
class ThreadedClient1(threading.Thread):
def __init__(self, queue, fcn):
threading.Thread.__init__(self)
self.queue = queue
self.fcn = fcn
def run(self):
time.sleep(1)
self.queue.put(lambda: self.fcn)
This did not work, as it ignored the function I'm interested in, budged the progressbar a bit/disabled and enabled the button, then ended.
What am I doing wrong?
Edit:
Problem solved. Another arose, though. I'd like to transfer spawnthread
, periodiccall
, and ThreadedClient
to a widget template module I have and generalize them so I can use them easily. I did this:
def spawnthread(self, widget):
widget.config(state="disabled")
self.thread = ThreadedClient1(self.queue)
self.thread.start()
self.periodiccall()
Which worked, until I tried to generalize periodiccall
as well.
#...
self.periodiccall(widget=widget)
def periodiccall(self, widget=None):
if self.thread.is_alive():
self.after(100, self.periodiccall)
self.progressbar.step(500)
#print widget
else:
widget.config(state="active")
self.progressbar.stop()
I popped in the print widget
to see what was going on since I kept getting an attribute error. What appears to be happening is that the first time it runs though the function it knows what "widget" is and then it turns into in the None
I wanted it to be by calling self.periodiccall
in the after
statement. At least, that's what I think is happening. How do I remove this error and make widget a variable in this recursive function?
Upvotes: 2
Views: 188
Reputation: 20679
The problem is that you are not calling the function when you use it as an argument for ThreadedClient1
:
class ThreadedClient1(threading.Thread):
# ...
def run(self):
time.sleep(1)
self.queue.put(lambda: self.fcn)
It is just a function that returns a reference to the lambda you pass as an argument, NOT the result of the call to slxl.runImportAndCoordAdder()
. It should be:
class ThreadedClient1(threading.Thread):
# ...
def run(self):
time.sleep(1)
self.queue.put(self.fcn())
Or another solution with the reference directly (without lambdas):
def spawnthread(self):
self.updatebttn.config(state="disabled")
self.thread = ThreadedClient1(self.queue, slxl.runImportAndCoordAdder)
self.thread.start()
self.periodiccall()
class ThreadedClient1(threading.Thread):
# ...
def run(self):
time.sleep(1)
self.queue.put(self.fcn())
Upvotes: 1