This simple program doesn't execute correctly, locking in the message "Read message" after press the button. I think I'm not wrong in multiprocessing code because I can run this code without kivy with no problems, but if I simply add same import modules from kivy, the process stops returning any value. In fact my tests show the process even start to run.
I did a very simple code to show the problem. You can simply copy and run. When I press the button test the program locks immediately and the main thread does't receive the message from process.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from multiprocessing import Process, Queue, Event
kv = '''
<InterfaceView>:
Button:
text: 'teste'
on_press: root.do_process()
'''
def pprosess(message, q, stop):
q.put(message)
stop.wait()
Builder.load_string(kv)
class InterfaceView(BoxLayout):
def do_process(self):
q = Queue()
stop = Event()
p = Process(target=pprosess, args=('any message', q, stop))
p.daemon = True
p.start()
print('Read message')
print('message: ', q.get())
stop.set()
p.join()
print('Process closed')
class SimpleApp(App):
def build(self):
return InterfaceView()
if __name__ == '__main__':
SimpleApp().run()
The code below executes very well the same thing without kivy, believe me, or not. Here you can observe the correct terminal output.
from multiprocessing import Process, Queue, Event
def pprosess(message, q, stop):
q.put(message)
stop.wait()
def main():
q = Queue()
stop = Event()
p = Process(target=pprosess, args=('any message', q, stop))
p.daemon = True
p.start()
print('Read message')
print('message: ', q.get())
stop.set()
p.join()
print('Process closed')
if __name__ == '__main__':
main()
I'm very frustrated struggling with this for days long. Please, somebody help me.
The stop.wait() event is causing the problem. It is equivalent to time.sleep(). Remove stop should fix the problem.
Programming Guide » Events and Properties » Main loop
In Kivy applications, you have to avoid long/infinite loops or sleeping.
Example
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from multiprocessing import Process, Queue, Event
kv = '''
<InterfaceView>:
Button:
text: 'teste'
on_press: root.do_process()
'''
def pprosess(message, q):
q.put(message)
Builder.load_string(kv)
class InterfaceView(BoxLayout):
def do_process(self):
q = Queue()
p = Process(target=pprosess, args=('any message', q))
p.daemon = True
p.start()
print('Read message')
print('message: ', q.get())
p.join()
print('Process closed')
class SimpleApp(App):
def build(self):
return InterfaceView()
if __name__ == '__main__':
SimpleApp().run()
Output
My problem, in fact, was with Spyder Development Environment and/or some of its components.
I was using the current IPython console to run my code, when I change to execute in an external terminal the problem was solved.
Thanks #embryo and specially #ikolim for helping. Their tests and suggestion lead me to the problem.
So, if you use Spyder Development Environment, attention to bad interaction effects between IPython console, python multiprocessing module and kivy.
All codes I posted are correct with no problems.
Related
I do have an interface to store settings and to start a process. However, I cannot close everything once the process starts because kivy gets stuck after calling process.run. Here is a minimal example:
#! /usr/bin/env python
"""
Activate the touch keyboard. It is important that this part is on top
because the global config should be initiated first.
"""
from kivy.config import Config
Config.set('kivy', 'keyboard_mode', 'multi')
# the main app
from kivy.app import App
# The Builder is used to define the main interface.
from kivy.lang import Builder
# The start screen is defined as BoxLayout
from kivy.uix.boxlayout import BoxLayout
# The pHBot class
from phbot import pHBot
# The pHBot is defined as process. Otherwise it would not be possible to use the close button.
from multiprocessing import Process, Queue
# Definition of the Main interface
Builder.load_string('''
<Interface>:
orientation: 'vertical'
Button:
text: 'Run pH-Bot'
font_size: 40
on_release: app.run_worker()
Button:
text: 'Close pH-Bot'
font_size: 40
on_release: app.stop_phbot()
''')
# Interface as a subclass of BoxLayout without any further changes. This part is used by kivy.
class Interface(BoxLayout):
pass
class SettingsApp(App):
"""
The settings App is the main app of the pHBot application. It is initiated by kivy and contains the functions
defining the main interface.
"""
def build(self):
"""
This function initializes the app interface and has to be called "build(self)". It returns the user interface
defined by the Builder.
"""
# A queque for the control all processes.
self.qu_stop = Queue()
# returns the user interface defined by the Builder
return Interface()
def run_worker(self):
"""
The pHBot application is started as a second process.
"""
bot = pHBot(self.qu_stop)
phbot = Process(target=bot.ph_control())
# start the process
phbot.run()
def stop_phbot(self):
self.qu_stop.put('STOP')
if __name__ == '__main__':
SettingsApp().run()
The second class is within a file called phbot.py:
import time
class pHBot:
def __init__(self, qu_stop_in):
self.qu_stop_in = qu_stop_in
def ph_control(self):
while True:
if self.qu_stop_in.full():
if self.qu_stop_in.get() == 'STOP':
break
print('Back and forth forever ...')
time.sleep(2)
What am I missing here ?
Note that a Process is started with start(). Calling run() really immediately launches the worker from the same process, and thus it is blocking. The relevant lines in run_worker should therefore be:
bot = pHBot(self.qu_stop)
phbot = Process(target=bot.ph_control)
# start the process
phbot.start()
In addition, in your worker, don't check whether the Queue is full. Rather, do a non-blocking get and handle the Queue.Empty exception:
import Queue
...
def ph_control(self):
while True:
try:
item = self.qu_stop_in.get(False)
if item == 'STOP':
break
except Queue.Empty:
print "Nothing to see"
print('Back and forth forever ...')
time.sleep(2)
phbot = Process(target=bot.ph_control())
You are calling bot.ph_control - that's what the () syntax does. I'd guess you need to pass the function itself (Process(target=bot.ph_control))), but I'm not familiar with multiprocessing.
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 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.
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()