Python: Threading with wxPython - python

I am new to threading and its something I am trying to get a grip of. I am creating a thread to handle a long running process separate from the main thread (which handles the graphical user process). Otherwise, I have a blocking GUI which is not nice.
The process keeps repeating itself, that the code in the thread runs again instead of stopping from the allDone function, which doesn't make sense to me.
No matter which route the code takes within the run portion (whether the findDomains function, searchAndScrape function, or the emailFormat function), they all end up at the emailFormat function calling the allDone function of MyClass which is an object of the GUI(called WindowClass):
class windowClass(wx.Frame):
def __init__(self, parent, title):
super(windowClass, self).__init__(parent, title=title, size=(500, 364), style=wx.DEFAULT_FRAME_STYLE & ~wx.MAXIMIZE_BOX ^ wx.RESIZE_BORDER)
self.SetBackgroundColour('white')
self.basicGUI()
def allDone(self, event):
myClass.worker.stop()
time.sleep(2)
dlg = wx.MessageBox("All done!", "Ask Alfred", wx.OK | wx.ICON_INFORMATION)
if dlg.ShowModal() == wx.ID_OK:
while True:
try:
os.unlink(self.fpath)
os.rename(self.temp, self.fpath)
self.Destroy()
except WindowsError:
myClass.closeIt(self)
break
To keep the brevity and not post 500 lines of code, here is just my thread class:
class openExcel(Thread):
def __init__(self, file):
Thread.__init__(self)
super(openExcel, self).__init__()
self._stop=Event()
self.file = file
self._want_abort = 0
self.start()
self._stop=False
def run(self):
rbook=xlrd.open_workbook(self.file)
sheet = rbook.sheet_by_index(0)
numrows = (sheet.nrows)
wbook = copy(rbook)
skipNorbertvalue=myClass.skipNorbert.IsChecked()
searchrow=numrows
for row in range(numrows):
valueone=sheet.cell_value(row, 2)
if valueone =="":
searchrow=row
break
scraperow = numrows
for row in range(numrows):
valuetwo=sheet.cell_value(row, 3)
if valuetwo == "":
scraperow=row
break
elif valuetwo=="Over search limit":
scraperow=row
break
if searchrow < numrows:
while True:
try:
browserindex=myClass.currentbrowser
search=startSearch(self.file)
search.findDomains(browserindex, searchrow, numrows, sheet, wbook, self.file)
except AttributeError:
myClass.noBrowser(self)
elif skipNorbertvalue==False and scraperow < numrows:
try:
browserindex=myClass.currentbrowser
search=startSearch(self.file)
search.searchAndscrape(browserindex, scraperow, numrows, wbook, self.file)
except AttributeError:
myClass.noBrowser(self)
else:
checkformat=emailFormat()
checkformat.possibleEmail(numrows, sheet, wbook, self.file)
def abort(self):
self._want_abort = 1
def stop(self):
self._stop=True

Related

Updating label upon getting message from queue

I'm trying to update the label to display the numbers being counted down when it receives the number through the queue. I am able to see it being printed in the console but the label does not change. Any help or suggestion would be helpful!
Here is my code:
import tkinter as tk
import time
import threading
import queue
class GUIApp:
def __init__(self):
self.root = tk.Tk()
self.buttonCountDown = tk.Button(text='Count Down', command=self.countDownAction)
self.buttonCountDown.pack()
self.label = tk.Label(text='default')
self.label.pack()
self.queue = queue.Queue()
self.root.mainloop()
def countDown(self, seconds):
for i in range(seconds, 0, -1):
self.queue.put(i)
time.sleep(1)
def listenToQueue(self):
while True:
try:
if self.queue.empty() == False:
print(self.queue.get(0))
self.label['text'] = self.queue.get(0)
elif self.queue.empty() == True:
pass
except queue.Empty:
pass
def countDownAction(self):
listenThread = threading.Thread(target=self.listenToQueue)
listenThread.start()
thread = threading.Thread(target=self.countDown, args=(5,))
thread.start()
thread.join()
app = GUIApp()
The first thing you need to know is Queue.get() removes the item and returns it, similar to dict.pop(). So when you do print(self.queue.get(0)), the item is already removed from the queue. You have to assign it to a variable first if you want to both print and config it:
def listenToQueue(self):
while True:
try:
if self.queue.empty() == False:
s = self.queue.get(0)
print (s)
self.label['text'] = s
elif self.queue.empty() == True:
pass
except queue.Empty:
pass
Next, calling thread.join() will wait for the thread terminates. You don't need to call this method at all in your current setup.
def countDownAction(self):
listenThread = threading.Thread(target=self.listenToQueue)
listenThread.start()
thread = threading.Thread(target=self.countDown, args=(5,))
thread.start()
#thread.join() #not required

Why is the main thread of my multithreaded application unresponsive to Ctrl+C?

I have written a multithreaded application to watch and respond to changes in given list of files. I have a Watch class which gets the file size and sets it to size variable upon first call. Then, after a few seconds it again gets the size of the file and compares it with with the previous size and, if changed, sets size to the current size of the file. Furthermore there is a WatchWorker class which is a subclass of threading.Thread. The WatchWorker which makes use of Watch class to 'watch ' a given file.
Now here is the real problem :
The code I have written is working and notifies the user when a change is detected. But there is no response when I try to quit from the application using Ctrl+C. I'm on Windows.
Code:
import time
import threading
import os
class Watch(object):
def __init__(self, path, time=5):
self.path = path
self.time = time
self.size = os.stat(path).st_size
def loop(self):
while True:
time.sleep(self.time)
size = os.stat(self.path).st_size
if size != self.size:
self.size = size
print "Change detected in file {}".format(self.path)
class Watch_Worker(threading.Thread):
def __init__(self, path, *args, **kwargs):
super(Watch_Worker, self).__init__(*args, **kwargs)
self.path = path
def run(self):
super(Watch_Worker, self).run()
Watch(self.path).loop()
def main(*args):
for i in args:
thrd = Watch_Worker(path=i)
thrd.start()
print 'Watching ' + i
print "From main thread"
if __name__ == '__main__':
main('blah.js', 'cs.c', 'ab.rb')
Edit
Modified code to compare the values produced os.stat('somefile.ext').st_size).
I solved the problem of the main thread being prevented from exiting by setting self.daemon = True. The main thread does not wait for the threads to exit. This is solved by adding a infinite while loop withtime.sleep(5) to the end of main function. Here is the code:
class Watch_Worker(threading.Thread):
def __init__(self, path, time=2, *args, **kwargs):
super(Watch_Worker, self).__init__(*args, **kwargs)
self.path = path
self.time = time
self.size = os.stat(path).st_size
self.daemon = True
def run(self):
super(Watch_Worker, self).run()
while True:
time.sleep(self.time)
size = os.stat(self.path).st_size
if size != self.size:
self.size = size
print "Change detected in file {}".format(self.path)
def main(*args):
for i in args:
thrd = Watch_Worker(path=i)
thrd.start()
print 'Watching ' + i
while True:
time.sleep(5)
if __name__ == '__main__':
main('blah.js', 'cs.c', 'ab.rb')

Calling a thread within a module in python

I have a log in screen which calls a module from a file which is 'HomescreenscrollCopy31' . This works perfectly fine.
The only issue is that there is a thread within the module which does not seem to operate.
Here is the code in the login screen:
def login():
content = nameentry.get()
content1 = IDentry.get()
if content == "1" and content1 == "1":
root.destroy()
from HomescreenscrollCopy31 import FullScreenApp
The HomescreenscrollCopy31.py file looks like this,
class FullScreenApp(object):
def __init__(self, master, **kwargs):
self.master=master
pad=3
self._geom='200x200+0+0'
master.geometry("{0}x{1}+0+0".format(
master.winfo_screenwidth()-pad, master.winfo_screenheight()-pad))
master.bind('<Escape>',self.toggle_geom)
def toggle_geom(self,event):
geom=self.master.winfo_geometry()
print(geom,self._geom)
self.master.geometry(self._geom)
self._geom=geom
def trick(threadName, sleepTime):
while 1 < 2:
.....
try:
t = threading.Thread(target=trick, args=("Trick running", 5))
t.daemon = True
t.start()
except: Exception,
print "start up"
It seems that it runs through the HomescreenscrollCopy31.py and politely prints the "start up" only to tease me, as it never actually runs the thread and thus should print the "Trick running" output.
Why is this?
I think it's better to write it this way.
def login():
content = nameentry.get()
content1 = IDentry.get()
if content == "1" and content1 == "1":
root.destroy()
from HomescreenscrollCopy31 import FullScreenApp
myFullScreenApp = FullScreenApp(...) # you need to give it some input, like master, to initialize an instance for FullScreenApp class
class FullScreenApp(object):
def __init__(self, master, **kwargs):
self.master=master
pad=3
self._geom='200x200+0+0'
master.geometry("{0}x{1}+0+0".format(
master.winfo_screenwidth()-pad, master.winfo_screenheight()-pad))
master.bind('<Escape>',self.toggle_geom)
# these code will be executed when instance initializing
t = threading.Thread(target=self.trick, args=["Trick running", 5])
t.daemon = True
t.start()
def toggle_geom(self,event):
geom=self.master.winfo_geometry()
print(geom,self._geom)
self.master.geometry(self._geom)
self._geom=geom
def trick(threadName, sleepTime):
while 1 < 2:
.....
You just need to initialize an instance of the class to get thread
running. If using staticmethod way, it will make code more
complicated which I don't recommend.
When invoking a new thread, args should be a list([]) but not a
tuple(()).
You can use while 1 instead of while 1<2. Anyway, this is not the
major issue. :)
Hope it helps.

Multithreading (?): Manual interference in a loop

I've been looking into a way to directly change variables in a running module.
What I want to achieve is that a load test is being run and that I can manually adjust the call pace or whatsoever.
Below some code that I just created (not-tested e.d.), just to give you an idea.
class A():
def __init__(self):
self.value = 1
def runForever(self):
while(1):
print self.value
def setValue(self, value):
self.value = value
if __name__ == '__main__':
#Some code to create the A object and directly apply the value from an human's input
a = A()
#Some parallelism or something has to be applied.
a.runForever()
a.setValue(raw_input("New value: "))
Edit #1: Yes, I know that now I will never hit the a.setValue() :-)
Here is a multi-threaded example. This code will work with the python interpreter but not with the Python Shell of IDLE, because the raw_input function is not handled the same way.
from threading import Thread
from time import sleep
class A(Thread):
def __init__(self):
Thread.__init__(self)
self.value = 1
self.stop_flag = False
def run(self):
while not self.stop_flag:
sleep(1)
print(self.value)
def set_value(self, value):
self.value = value
def stop(self):
self.stop_flag = True
if __name__ == '__main__':
a = A()
a.start()
try:
while 1:
r = raw_input()
a.set_value(int(r))
except:
a.stop()
The pseudo code you wrote is quite similar to the way Threading / Multiprocessing works in python. You will want to start a (for example) thread that "runs forever" and then instead of modifying the internal rate value directly, you will probably just send a message through a Queue that gives the new value.
Check out this question.
Here is a demonstration of doing what you asked about. I prefer to use Queues to directly making calls on threads / processes.
import Queue # !!warning. if you use multiprocessing, use multiprocessing.Queue
import threading
import time
def main():
q = Queue.Queue()
tester = Tester(q)
tester.start()
while True:
user_input = raw_input("New period in seconds or (q)uit: ")
if user_input.lower() == 'q':
break
try:
new_speed = float(user_input)
except ValueError:
new_speed = None # ignore junk
if new_speed is not None:
q.put(new_speed)
q.put(Tester.STOP_TOKEN)
class Tester(threading.Thread):
STOP_TOKEN = '<<stop>>'
def __init__(self, q):
threading.Thread.__init__(self)
self.q = q
self.speed = 1
def run(self):
while True:
# get from the queue
try:
item = self.q.get(block=False) # don't hang
except Queue.Empty:
item = None # do nothing
if item:
# stop when requested
if item == self.STOP_TOKEN:
break # stop this thread loop
# otherwise check for a new speed
try:
self.speed = float(item)
except ValueError:
pass # whatever you like with unknown input
# do your thing
self.main_code()
def main_code(self):
time.sleep(self.speed) # or whatever you want to do
if __name__ == '__main__':
main()

Accessing a non-global variable in Python

I'm trying to change the state of a Gtk status icon from a thread as specified in MailThread.run() below, but I don't know how to reach the status icon object from the method in order to change set_visible to either True or False.
Basically I would like to know what to write in place of "# set status icon visible off/on".
#!/usr/bin/env python
import gtk, sys, pynotify, imaplib, time, threading
from email import parser
class Mail:
def check_mail(self):
obj = imaplib.IMAP4_SSL('imap.gmail.com','993')
acc = 'email'
pwrd = 'pass'
obj.login(acc, pwrd)
obj.select()
num = str(len(obj.search(None,'UnSeen')[1][0].split()))
return acc, num
class MailThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
gtk.gdk.threads_init()
def run(self):
while True:
print "hello"
mail = Mail()
num = mail.check_mail()[1]
if num < 1:
# set status icon visible off
else:
# set status icon visible on
time.sleep(60)
class StatusIcon:
# activate callback
def activate( self, widget, data=None):
mail = Mail()
acc, num = mail.check_mail()
pynotify.init("myapp")
n = pynotify.Notification(acc, "You have " + num + " unread e-mails.", "emblem-mail")
n.show()
# Show_Hide callback
def show_hide(self, widget,response_id, data= None):
if response_id == gtk.RESPONSE_YES:
widget.hide()
else:
widget.hide()
# destroyer callback
def destroyer(self, widget,response_id, data= None):
if response_id == gtk.RESPONSE_OK:
gtk.main_quit()
else:
widget.hide()
# popup callback
def popup(self, button, widget, data=None):
dialog = gtk.MessageDialog(
parent = None,
flags = gtk.DIALOG_DESTROY_WITH_PARENT,
type = gtk.MESSAGE_INFO,
buttons = gtk.BUTTONS_OK_CANCEL,
message_format = "Do you want to close e-mail notifications?")
dialog.set_title('Exit')
dialog.connect('response', self.destroyer)
dialog.show()
def __init__(self):
# create a new Status Icon
self.staticon = gtk.StatusIcon()
self.staticon.set_from_icon_name("emblem-mail")
self.staticon.connect("activate", self.activate)
self.staticon.connect("popup_menu", self.popup)
self.staticon.set_visible(True)
# starting thread
thread = MailThread()
thread.setDaemon(True)
thread.start()
# invoking the main()
gtk.main()
if __name__ == "__main__":
# status icon
statusicon = StatusIcon()
You can accept the status icon in the thread's __init__():
class MailThread(threading.Thread):
def __init__(self, status_icon = None):
threading.Thread.__init__(self)
gtk.gdk.threads_init()
self.status_icon = status_icon
And then you can use it in run().
Additionally, you need to do all the GUI work from the main thread. The main thread has a queue maintained by GTK you can use to tell it to go do some GUI work. This is how it works:
def run(self):
# <...>
if num < 1:
gobject.idle_add(self.set_status_icon, False)
else:
gobject.idle_add(self.set_status_icon, True)
# <...>
def set_status_icon(self, state = False):
# code that changes icon state goes here
pass
idle_add basically means "add that to the queue and do it when you have some free time".

Categories

Resources