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
Related
I want to update the progress bar from my main.py using thread approach.
if __name__ == "__main__":
app = QApplication(sys.argv)
uiplot = gui.Ui_MainWindow()
Update_Progressbar_thread = QThread()
Update_Progressbar_thread.started.connect(Update_Progressbar)
def Update_Progressbar():
progressbar_value = progressbar_value + 1
while (progressbar_value < 100):
uiplot.PSprogressbar.setValue(progressbar_value)
time.sleep(0.1)
uiplot.PSStart_btn.clicked.connect(Update_Progressbar_thread.start)
The problem is this approach james my GUI and I cannot click any buttons etc.
Alternatively how can I make it work ?
Thanks
Explanation:
With your logic you are invoking "Update_Progressbar" to run when the QThread starts, but where will "Update_Progressbar" run? Well, in the main thread blocking the GUI.
Solution:
Your goal is not to run "Update_Progressbar" when the QThread starts but to run in the thread that handles QThread. So in this case you can create a Worker that lives in the thread handled by QThread
class Worker(QObject):
progressChanged = pyqtSignal(int)
def work(self):
progressbar_value = 0
while progressbar_value < 100:
self.progressChanged.emit(progressbar_value)
time.sleep(0.1)
if __name__ == "__main__":
app = QApplication(sys.argv)
uiplot = gui.Ui_MainWindow()
thread = QThread()
thread.start()
worker = Worker()
worker.moveToThread(thread)
worker.progressChanged.connect(uiplot.PSprogressbar.setValue)
uiplot.PSStart_btn.clicked.connect(worker.work)
# ...
I've been messing around with PyQt and signals/slots across threads.
Here a situation where I can't find my mistake:
I have a class (MultipleProcessLauncher) that is able to launch multiple processes in separate threads.
I catch the stdout of each processes and send those messages to a single queue that is read by another thread (OutputWorker), this last thread should send a signal onNewMessage (I think it doesn't) catch on the main class but the callback function is never called.
The process threads populate the queue with messages
The reading thread catch all those messages (I can print them with print(item) in the while loop)
But:
- The signal of the reading thread doesn't seems to emit anything, so the callback function of the main thread is never called...
Your help would be much appreciated, I think I'm missing something with cross threads signals...
class OutputWorker(QObject):
onNewMessage = pyqtSignal(['QString'])
def __init__(self, queue, parent=None):
super(OutputWorker, self).__init__(parent)
self.queue = queue
def work(self):
while True:
item = self.queue.get()
self.onNewMessage.emit(item)
self.queue.task_done()
class MultipleProcessLauncher(QObject):
commandEvent = pyqtSignal(['QString'])
def __init__(self, parent=None):
super(MultipleProcessLauncher, self).__init__(parent)
self.messaging_queue = Queue()
# Start reading message
self.reading_thread = QThread()
self.worker = OutputWorker(self.messaging_queue)
self.worker.moveToThread(self.reading_thread)
self.worker.onNewMessage.connect(self.command_event)
self.reading_thread.started.connect(self.worker.work)
self.reading_thread.start()
def execute(self, command):
p = subprocess.Popen(command, stdout=subprocess.PIPE)
t = Thread(target=self.enqueue, args=(p.stdout, self.messaging_queue))
t.daemon = True
t.start()
def enqueue(self, stdout, queue):
for line in iter(stdout.readline, b''):
queue.put(line.decode())
stdout.close()
def command_event(self, event):
# This point is never reached
print('message received')
if __name__ == '__main__':
manager = MultipleProcessLauncher()
manager.execute('ipconfig')
time.sleep(100)
Qt's cross-thread signaling is based on event loop, so you need to exec a QApplication so that there is a main event loop to process signals from other threads. For example:
if __name__ == '__main__':
app = QApplication([])
manager = MultipleProcessLauncher()
manager.execute('ipconfig')
MAX_WAIT_MSEC = 100 * 1000 # 100 seconds
QTimer.singleShot(MAX_WAIT_MSEC, app.quit)
app.exec()
In your real application you will probably execute the manager based on user input so the execute would be in a slot, and there wouldn't be a need to quit, etc, but you get the idea.
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 tried using self.terminate() in the QThread class, and also self.thread.terminate() in the GUI class. I also tried putting self.wait() in both cases. However, there are two scenarios that happen:
1) The thread does not terminate at all, and the GUI freezes waiting for the thread to finish. Once the thread finished, the GUI unfreezes and everything is back to normal.
2) The thread indeed does terminate, but at the same time it freezes the entire application.
I also tried using self.thread.exit(). No joy.
To further clarify, I am trying to implement a user-abort button in GUI which would terminate the executing of the thread at any point in time.
Thanks in advance.
EDIT:
Here is the run() method:
def run(self):
if self.create:
print "calling create f"
self.emit(SIGNAL("disableCreate(bool)"))
self.create(self.password, self.email)
self.stop()
self.emit(SIGNAL("finished(bool)"), self.completed)
def stop(self):
#Tried the following, one by one (and all together too, I was desperate):
self.terminate()
self.quit()
self.exit()
self.stopped = True
self.terminated = True
#Neither works
And here is the GUI class' method for aborting the thread:
def on_abort_clicked(self):
self.thread = threadmodule.Thread()
#Tried the following, also one by one and altogether:
self.thread.exit()
self.thread.wait()
self.thread.quit()
self.thread.terminate()
#Again, none work
From the Qt documentation for QThread::terminate:
Warning: This function is dangerous and its use is discouraged. The
thread can be terminated at any point in its code path. Threads can be
terminated while modifying data. There is no chance for the thread to
clean up after itself, unlock any held mutexes, etc. In short, use
this function only if absolutely necessary.
It's probably a much better idea to re-think your threading strategy such that you can e.g. use QThread::quit() to signal the thread to quit cleanly, rather than trying to get the thread to terminate this way. Actually calling thread.exit() from within the thread should do that depending on how you have implemented run(). If you'd like to share the code for your thread run method that might hint as to why it doesn't work.
This is what I did:
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
stop_flag = 1
...
#=========500 ms class ===================
class Timer500msThread(QThread):
signal_500ms = pyqtSignal(str)
....
def timer500msProcess(self):
if MainWindow.stop_flag == 0 :
self.timer_500ms.stop()
#==========
#=========Main Window ===================
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
MainWindow.stop_flag=0 #this sets the flag to 0 and when the next 500ms triggers the
#the thread ends
print("Program Ending")
I had a similar problem and solved it with use of pyqtSignals and pyqtSlots.
You could create a pyqtSignal in your MainWindow-class and use the aboutToQuit-function to your QApplication instance.
Then you connect this aboutToQuit-function with another which emit a signal to the slot in your separate thread.
Then you can define a stop() function in this Thread which runs if the signal is emitted.
In this case the thread would not terminate during its work.
MainWindow:
class mainSignals(QObject):
isClosed = pyqtSignal()
class mainwindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super(mainwindow, self).__init__()
self.mainSignal = mainSignals()
...
...
if __name__ = "__main__":
# This function runs if the application is closed.
# (app.aboutToQuit.connect(windowClose))
def windowClose():
window.mainSignal.isClosed.emit() # Emit the signal to the slot in (sepThread)
app = QApplication(sys.argv)
app.aboutToQuit.connect(windowClose)
window = mainwindow()
window.show()
sys.exit(app.exec())
sepThread:
class sepThread(QRunnable):
def __init__(self, parent):
super(sepThread,self).__init__()
self._parent = parent
self.mainOpen = True
self._parent.mainSignal.isClosed.connect(self.stopThread)
# If the signal was emitted by the Mainapplication
# the stopThread function runs and set the mainOpen-attribute to False
def stopThread(self):
self.mainOpen = False
def run(self):
while self.mainOpen == True:
# Do something in a loop while mainOpen-attribute is True
...
...