I am new to python and trying to implement multiprocess with Tkinter. I am having a main GUI process, with two other "test" processes. This code works fine in Windows, and the main window is displayed and the other two processes are also running. However when I run this code in Ubuntu, it does not work, the two test processes are running but the main GUI window is not displayed.
Can anyone help me on this?
from Tkinter import *
from multiprocessing import Process
import time
def mywind():
root=Tk()
root.title = "MyWindow"
frame=Frame(root)
root.mainloop()
def test1():
while True:
print "In test1"
time.sleep(1)
def test2():
while True:
print "In test2"
time.sleep(1)
if __name__ == '__main__':
p1 = Process(target=test1)
p1.start()
p2 = Process(target=test2)
p2.start()
p = Process(target=mywind)
p.start()
while True:
time.sleep(1)
Try this:
from Tkinter import *
from multiprocessing import Process
import time
root = None
def mywind():
root=Tk()
root.title = "MyWindow"
frame=Frame(root)
return root
def test1():
while True:
print "In test1"
time.sleep(1)
def test2():
while True:
print "In test2"
time.sleep(1)
if __name__ == '__main__':
p1 = Process(target=test1)
p1.start()
p2 = Process(target=test2)
p2.start()
root = mywind()
root.mainloop()
I can't perfectly explain why putting the main loop into the main process rather than a subprocess works. I assume it has something to do with the way Tk resources (and underlying native windowing resources) are managed by the Tkinter library, and collisions with the idea of running them in a separate process.
Related
I'm doing Tkinter. My functions run correctly (playing a bell sound once a minute), but it's not running on a thread. Whenever I click the Start button, the program window grays out and the top says "Not responding" (because my recursive calls to start() are locking it, I'm assuming.)
Why am I not threading correctly? Thanks.
def start():
now = tt1.time()
listOfTimes = []
for t in timeSlotEFs:
listOfTimes.append(t.get())
now = datetime.now()
timee = now.strftime('%H%M')
print('here')
for t in listOfTimes:
if t==timee:
winsound.PlaySound('newhourlychimebeg.wav',winsound.SND_FILENAME)
s.enterabs(now+60,1,start) #I want to call recursively every 60 seconds
s.run()
def start2():
t = threading.Thread(target=start)
t.run()
startBtn = Button(ssFrame, text='Start', command=start2)
startBtn.grid(row=0,column=0,padx=paddX,pady=paddY)
It feels like you mixed between the definitions that threading.Thread imports from.
You should create run function and then start the thread.
This way i see the result and it works:
from tkinter import *
import threading
root = Tk()
def run():
print('hello Thread')
def start2():
t = threading.Thread(target=run)
t.start()
print(t) # Output: <Thread(Thread-1, stopped 10580)>
startBtn = Button(root, text='Start', command=start2)
startBtn.grid(row=0,column=0)
root.mainloop()
In start2(), I changed t.run() to t.start(). That was it, and it now works. Thx Jim.
I'm trying to implement multiprocessing to a program I made for my job, and I'm running into issues. Please see the simplified code below; when I launch a process, it launches another instance of the gui; when I close that instance, the cpu_intensive function is processed. It's my first time using Tkinter and threads and processes so there might be more than one problem in there ;-) Thanks for looking into this:
from Tkinter import *
from threading import Thread
from multiprocessing import Process, Queue
source_queue = Queue()
def thread_launch():
"""launches a thread to keep the gui responsive"""
thread1 = Thread(target=process_engine)
return thread1.start()
def process_engine():
"""spawns processes and populates the queue"""
p1 = Process(target=cpu_intensive, args=(source_queue,))
p1.start()
p2 = Process(target=cpu_intensive, args=(source_queue,))
p2.start()
for i in range(10):
source_queue.put(i)
print "Added item", i, "to queue"
source_queue.close()
source_queue.join_thread()
p1.join()
p2.join()
def cpu_intensive(item_toprocess):
"""function to be multiprocessed"""
print "Processed: item", item_toprocess.get()
class Application:
def __init__(self, master):
"""defines the gui"""
self.master = master
self.process_button = Button(self.master,
text="Process",
command=thread_launch)
self.process_button.pack(padx=100, pady=100)
def __main__():
"""launches the program"""
root = Tk()
app = Application(root)
root.mainloop()
if __name__ == __main__():
__main__()
Try:
if __name__ == "__main__":
instead. The previous version was calling main but checking the returned value (None in this case). The problem was (I suspect you are on Windows) that the child process was also calling main in the if statement.
I'm building a python app with pygtk. It consists of some buttons that activate/deactivate some infinite looped processes and a textview that should keep showing whats going on inside each process. Like verbose stuff.
These processeses hasn't an end. They stop only when the user hit it's corresponding button (or close the app).
What's going wrong: I cant print stuff in the textview from these processes. Maybe because they haven't an end...
Actually the app is too big to show the whole code here. So I've made a simple and little example of what I'm doing.
import pygtk
pygtk.require("2.0")
import gtk
import time
import glib
from multiprocessing import Process
gtk.threads_init()
class Test(gtk.Window):
def delete_event(self, widget, event, data=None):
if isinstance(self.my_process, Process):
if self.my_process.is_alive():
self.my_process.terminate()
gtk.main_quit()
return False
def __init__(self):
gtk.Window.__init__(self)
self.set_default_size(500, 400)
self.set_title(u"Test")
self.connect("delete_event", self.delete_event)
self.mainBox = gtk.VBox(False, 5)
self.text = gtk.TextView()
self.text.set_wrap_mode(gtk.WRAP_WORD)
self.button = gtk.Button("Start")
self.add(self.mainBox)
self.mainBox.pack_start(self.text, True, True, 0)
self.mainBox.pack_start(self.button, False, True, 0)
self.button.connect("clicked", self.start_clicked)
self.show_all()
def start_clicked(self, widget):
self.register_data("Starting...")
self.my_process = Process(target=self.do_something)
self.my_process.start()
def do_something(self):
while True:
time.sleep(0.5)
#get a list of a lot of things
#Do stuff with each item in the list
#show me on the gui whats going on
glib.idle_add(self.register_data, "Yo! Here I'm")
print "Hello, boy."
def register_data(self, data):
data = data + "\r\n"
#gtk.gdk.threads_enter()
buff = self.text.get_buffer()
biter = buff.get_start_iter()
buff.insert(biter, data)
#gtk.gdk.threads_leave()
if __name__ == "__main__":
mnc = Test()
mnc.set_position(gtk.WIN_POS_CENTER)
gtk.threads_enter()
gtk.main()
gtk.threads_leave()
Remove all .threads_init(), .threads_enter(), .threads_leave(). multiprocessing is not threading.
Put data you'd like to display into multiprocessing.Queue() in your child process:
def do_something(self):
while True:
#get a list of a lot of things
#Do stuff with each item in the list
#show me on the gui whats going on
self.data_queue.put("Yo! Here I'm")
and poll it in GUI loop:
def __init__(self, ...):
# ...
self.data_queue = Queue()
gobject.timeout_add(100, self.update_text)
where:
def update_text(self):
# receive updates from the child process here
try:
data = self.data_queue.get_nowait()
except Empty:
pass # nothing at this time
else:
self.register_data(data)
return True
To avoid polling you could write to multiprocessing.Pipe in your child process and setup GUI callback using gobject.io_add_watch(). Here's a complete code example:
#!/usr/bin/python3
from multiprocessing import Pipe, Process
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GObject, Gtk
# create GUI to show multiprocessing output
win = Gtk.Window()
win.set_default_size(640, 480)
label = Gtk.Label('process output')
win.add(label)
# start dummy infinite loop in a child process
def loop(conn):
import itertools, sys, time
for i in itertools.count():
conn.send(i)
time.sleep(0.1 - time.monotonic() % 0.1)
parent_conn, child_conn = Pipe(duplex=False)
Process(target=loop, args=[child_conn], daemon=True).start()
child_conn.close()
# read values from the child
def read_data(source, condition):
assert parent_conn.poll()
try:
i = parent_conn.recv()
except EOFError:
return False # stop reading
# update text
label.set_text('Result from the child: %03d' % (i,))
return True # continue reading
# . configure the callback
GObject.io_add_watch(parent_conn.fileno(), GObject.IO_IN, read_data)
win.connect('delete-event', Gtk.main_quit)
win.show_all()
Gtk.main()
You can also do it with an arbitrary subprocess (not just a python child process).
You should use gtk.threads_enter() when you are inside a thread to every call to
gtk and close with gtk.threads_leave() after call him.
Something like:
def do_something(self):
while True:
time.sleep(0.5)
gtk.threads_enter()
#get a list of a lot of things
#Do stuff with each item in the list
#show me on the gui whats going on
glib.idle_add(self.register_data, "Yo! Here I'm")
gtk.threads_leave()
print "Hello, boy."
and sometimes you need to use:
gtk.gdk.threads_init()
gtk.gdk.threads_enter()
#code section
gtk.gdk.threads_leave()
Here's a threaded example. But a non-threaded, non polling approach would be better, because in GTK 3, the threads_enter/threads_leave have been deprecated, so your program would be harder to port to GTK 3 + PyGObject.
In C, one would probably use g_spawn_async_with_pipes. An equivalent in python would be glib.spawn_async I guess, and you'd use glib.io_add_watch to be notified when there's data to read in the standard output.
I am trying to build an GUI application with wxPy that perform some long scripted action. I have put the GUI and the script in different threads to prevent blocking. this worked well except after I close the GUI, the thread containing the script remain running.
below is a simplified version of my program(sorry if the code is hard to understand or missing parts, I am terrible at shortening programs)
class Form(wx.Frame):
...
def test(self, evt):
t2 = threading.Thread(target = self.runTest)
t2.start()
def runTest(self):
result = really_long_script()
def main():
app = wx.App(False)
form = form(app)
app.MainLoop()
t1 = threading.Thread(target = main)
t1.start()
Is there any way I can just kill the thread? right now the script still runs in background when I close the window.
Any help would be greatly appreciated!
Thanks,
John
If you set the thread to be a daemon thread, it will die with the main thread.
You can do this by adding the line t2.daemon = True before you call start
Edit:
Check this example, with the t.daemon = True line the thread dies when you close the frame, if you comment out that t.daemon = True line, the thread stays alive after the frame closes
import wx
import time
from threading import Thread
def print_something_forever(something):
while True:
print something
time.sleep(1)
class Frame(wx.Frame):
def __init__(self,parent):
wx.Frame.__init__(self,parent)
self.panel= wx.Panel(self)
t= Thread(target=print_something_forever,args=("Thread alive!",))
t.daemon= True
t.start()
self.Show()
if __name__ == "__main__":
app= wx.App(False)
Frame(None)
app.MainLoop()
Python doesn't support killing/destroying threads, probably because memory leaks, resources loss, etc.
try this "Threadizing class" :D
class Run_Other_Thread(threading.Thread):
"Raises a child thread \
I'm busy dying, rather lying - _P0W !"
def __init__(self,func_name,*args): #Constructor
self._func=func_name
self._func_args=args
threading.Thread.__init__(self)
def run(self): # Start Dying
try:
print("\n** Running New Thread :"+self._func.func_name)
except:
print("\n** Running New Thread :"+self._func.__name__)
self._func(*self._func_args)
def stop(self):
print('!! Stopped')
def __del__(self):#Constructor
try:
print('\n ## Farewell :'+self._func.func_name)
except:
print('\n ## Farewell :'+self._func.__name__)
You may run GUI as:(and try closing)
def GUI():
app= wx.App(False)
Frame(None)
app.MainLoop()
if __name__ == '__main__':
"Tkinter GUI in Thread helps in De-bugging"
Run_Other_Thread(GUI).start() # Release command window control
So I am making a GUI to get tweets. I have made an event box which would take the signal and change the textview.
I am using multiprocessing to change the textview, but it just doesn't change. I even tried changing the size of the window. But nothing changes. I can get textbuffer of the textview but can not change it.
import pygtk
pygtk.require('2.0')
import gtk
from multiprocessing import Process
class multi:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_size_request(800,400)
self.window.set_title("Twitter Box")
self.window.set_border_width(4)
self.window.connect("destroy", self.close_application)
self.vbox1 = gtk.EventBox()
self.vbox1.set_size_request(750,450)
self.vbox1.connect('leave_notify_event',self.go_multi)
self.window.add(self.vbox1)
self.vbox1.show()
self.tweetview = gtk.TextView()
self.tweetbuffer = self.tweetview.get_buffer()
self.tweetbuffer.set_text('Why not working?')
self.vbox1.add(self.tweetview)
self.tweetview.show()
self.window.show()
def close_application(self, widget):
gtk.main_quit()
def go_multi(self, widget, data=None):
p = Process(target = self.change_textview)
p.start()
p.join()
def change_textview(self):
print 'changing text'
startiter = self.tweetbuffer.get_start_iter()
enditer = self.tweetbuffer.get_end_iter()
text = self.tweetbuffer.get_text(startiter, enditer)
print text
if text:
self.tweetbuffer.set_text('Changed....')
else:
self.tweetbuffer.set_text('')
return
def main():
multi()
gtk.main()
if __name__ == '__main__':
main()
I am making GUI to get tweets. Sometimes it takes really long to retrieve timeline due to slow connectivity and the GUI freezes. So, I wanted to make it such that, it would create a process and it will fetch the timeline and set tweetbuffer. But I am unable to set text in tweetbuffer.
I don't fully inderstand why you do this:
def go_multi(self, widget, data=None):
p = Process(target = self.change_textview)
p.start()
p.join()
because, even in the remote possibility in which it should work, you're basically calling the change_textview function and waiting for the process to finish.
Cleared this, I don't think you need multiprocessing at all, make your gui multithreading instead.
Make a Gtk multithread may be a little tricky at first, but it's not a difficult task.
You have two ways for doing so:
Update your widget using GLib.idle_add (or GObject.idle_add, I never fully understand why sometimes they're not the same)
Or follow what's explained [here]. Which it basically says to:
Call the following methods before you call Gtk.main():
GObject.threads_init()
Gdk.threads_init()
In your thread, surround the code that updates the Gtk widgets with:
Gdk.threads_enter()
# your code here
Gdk.threads_leave()
You must run the main loop to process the rendering events before anything becomes visible.
Also, you must not call GTK functions from a second thread.
Read this to get you started: Multi-threaded GTK applications – Part 1: Misconceptions
And here is how to apply this knowledge to PyGTK: Threads on PyGTK
If you still want to continue on that way after all answers:
Disclaimer:
I have no idea how useful this answer.
Explanation:
I tried to use your logic. I also imported Queue to share some data between processes. I guess it was needed as I saw from documentation. You may find some other info in code sample below.
import pygtk
pygtk.require('2.0')
import gtk
from multiprocessing import Process, Queue
class multi:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_size_request(800,400)
self.window.set_title("Twitter Box")
self.window.set_border_width(4)
self.window.connect("destroy", self.close_application)
self.vbox1 = gtk.EventBox()
self.vbox1.set_size_request(750,450)
self.vbox1.connect('leave_notify_event',self.go_multi)
self.window.add(self.vbox1)
self.vbox1.show()
self.tweetview = gtk.TextView()
self.tweetbuffer = self.tweetview.get_buffer()
self.tweetbuffer.set_text('Why not working?')
self.vbox1.add(self.tweetview)
self.tweetview.show()
self.window.show()
def close_application(self, widget):
gtk.main_quit()
def go_multi(self, widget, data=None):
q = Queue()
p = Process(target = self.change_textview, args=(q,))
p.start()
self.tweetbuffer.set_text(q.get())
p.join()
def change_textview(self, q):
print 'changing text'
startiter = self.tweetbuffer.get_start_iter()
enditer = self.tweetbuffer.get_end_iter()
text = self.tweetbuffer.get_text(startiter, enditer)
print text
if text:
q.put(('Changed....'))
else:
q.put((''))
def main():
multi()
gtk.main()
if __name__ == '__main__':
main()