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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With