Segmentation Fault in subprocess.stdout while displaying on QPlainTextEdit
Hi,
I am starting a thread to the function shown and streaming its result to a QTextEdit object in the thread. The function sometimes crashes with a segmentation fault for unknown reasons.
self.plainTextEdit = QPlainTextEdit()
self.thread = Thread(target = runcmd, args = ("make dc",))
self.thread.start()
self.thread.join()
def runcmd(self,cmd):
process = subprocess.Popen(shlex.split(cmd),stdout=subprocess.PIPE, bufsize=-1)
while True:
line = process.stdout.readline()
if not line:
break
self.plainTextEdit.moveCursor(QTextCursor.End)
self.plainTextEdit.insertPlainText(line.strip())
process.terminate()
The
make dc
command is a call to the design compiler synthesis tool. If I try to print the
line
variable instead of writing to the plainTextEdit Object the thread runs fine, displaying the result in the terminal window. Any help/advice is welcome......
Thank You
You can't use QT things from a python thread. Your options:
Send the data back from the thread to your main program, using a queue.Queue or some other similar synchronization object. Then the main program adds the data to the text widget. Documentation here.
Use a QThread, which is the QT equivalent of threads. Documentation here.
You cannot update the Qt GUI from another thread. Fortunately for us, Qt gave us signals, and PyQt gave us pyqtSignal, just for this situation. There are a few ways to do it, but I prefer the following style.
class YourClass(QtCore.QObject):
appendSignal = pyqtSignal(str)
def __init__(self):
super(YourClass, self).__init__()
self.plainTextEdit = QPlainTextEdit()
self.appendSignal.connect(self.reallyAppendToTextEdit)
self.appendToTextEdit = self.appendSignal.emit
self.thread = Thread(target = runcmd, args = ("make dc",))
self.thread.start()
self.thread.join()
def reallyAppendToTextEdit(self, txt):
self.plainTextEdit.moveCursor(QTextCursor.End)
self.plainTextEdit.insertPlainText(txt)
def runcmd(self,cmd):
process = subprocess.Popen(shlex.split(cmd),stdout=subprocess.PIPE, bufsize=-1)
while True:
line = process.stdout.readline()
if not line:
break
self.appendToTextEdit(line.strip())
process.terminate()
Instead of using subprocess.Popen() you should use QProcess which is GUI friendly so it is not necessary to use a new thread.
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import shlex
class LogWidget(QWidget):
def __init__(self, parent=None):
super(LogWidget, self).__init__(parent)
lay = QVBoxLayout(self)
self.plainTextEdit = QPlainTextEdit()
lay.addWidget(self.plainTextEdit)
self.runcmd("make dc")
def runcmd(self, cmd):
process = QProcess(self)
process.readyReadStandardOutput.connect(self.onReadyReadStandardOutput)
process.readyReadStandardError.connect(self.onReadyReadStandardError)
program, *arguments = shlex.split(cmd)
process.start(program, arguments)
def onReadyReadStandardOutput(self):
process = self.sender()
self.plainTextEdit.appendPlainText(str(process.readAllStandardOutput()))
def onReadyReadStandardError(self):
process = self.sender()
self.plainTextEdit.appendPlainText(str(process.readAllStandardError()))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = LogWidget()
w.show()
sys.exit(app.exec_())
Related
Based on the Qt docs and other examples on the web, I would have thought that the following program, which uses QThread.started signal, would start workers in non-main threads. But this is not the case, instead each work slot is called from main thread:
import time
import sys
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QPushButton, QTextEdit, QVBoxLayout, QWidget
def trap_exc_during_debug(*args):
# when app exits, put breakpoint in next line when run in debugger, and analyse args
pass
sys.excepthook = trap_exc_during_debug
class Checker(QObject):
sig_step = pyqtSignal(int, str)
sig_done = pyqtSignal(int)
def __init__(self, id: int):
super().__init__()
self.__id = id
#pyqtSlot()
def work(self):
thread_name = QThread.currentThread().objectName()
thread_id = int(QThread.currentThreadId())
print('running work #{} from thread "{}" (#{})'.format(self.__id, thread_name, thread_id))
time.sleep(2)
self.sig_step.emit(self.__id, 'step 1')
time.sleep(2)
self.sig_step.emit(self.__id, 'step 2')
time.sleep(2)
self.sig_done.emit(self.__id)
class MyWidget(QWidget):
NUM_THREADS = 3
sig_start = pyqtSignal()
def __init__(self):
super().__init__()
self.setWindowTitle("Thread Example")
form_layout = QVBoxLayout()
self.setLayout(form_layout)
self.resize(200, 200)
self.push_button = QPushButton()
self.push_button.clicked.connect(self.start_threads)
self.push_button.setText("Start {} threads".format(self.NUM_THREADS))
form_layout.addWidget(self.push_button)
self.log = QTextEdit()
form_layout.addWidget(self.log)
# self.log.setMaximumSize(QSize(200, 80))
self.text_edit = QTextEdit()
form_layout.addWidget(self.text_edit)
# self.text_edit.setMaximumSize(QSize(200, 60))
QThread.currentThread().setObjectName('main')
self.__threads_done = None
self.__threads = None
def start_threads(self):
self.log.append('starting {} threads'.format(self.NUM_THREADS))
self.push_button.setDisabled(True)
self.__threads_done = 0
self.__threads = []
for idx in range(self.NUM_THREADS):
checker = Checker(idx)
thread = QThread()
thread.setObjectName('thread_' + str(idx))
self.__threads.append((thread, checker)) # need to store checker too otherwise will be gc'd
checker.moveToThread(thread)
checker.sig_step.connect(self.on_thread_step)
checker.sig_done.connect(self.on_thread_done)
# self.sig_start.connect(checker.work) # method 1 works: each work() is in non-main thread
thread.started.connect(checker.work) # method 2 doesn't work: each work() is in main thread
thread.start()
self.sig_start.emit() # this is only useful in method 1
#pyqtSlot(int, str)
def on_thread_step(self, thread_id, data):
self.log.append('thread #{}: {}'.format(thread_id, data))
self.text_edit.append('{}: {}'.format(thread_id, data))
#pyqtSlot(int)
def on_thread_done(self, thread_id):
self.log.append('thread #{} done'.format(thread_id))
self.text_edit.append('-- Thread {} DONE'.format(thread_id))
self.__threads_done += 1
if self.__threads_done == self.NUM_THREADS:
self.log.append('No more threads')
self.push_button.setEnabled(True)
if __name__ == "__main__":
app = QApplication([])
form = MyWidget()
form.show()
sys.exit(app.exec_())
If I use a custom signal instead, it works fine. To see this, comment out the "method 2" line and uncomment the "method 1" line and repeat the run.
It would certainly be nicer to start the workers without having to create a custom signal, is there a way to do this (while sticking to the design of calling moveToThread on the workers)?
Note: The docs for QThread.started signal don't help much:
This signal is emitted from the associated thread when it starts executing
To me this implies that started would be emitted in the non-main thread, such that the work slot it is connected to would get called in the non-main thread, but this is clearly not the case. Even if my interpretation is incorrect and the signal is in-fact emitted in the main thread, the connection type is for both methods the default Qt.AutoConnection, to a slot on a QObject moved to another thread, so the started signal should be transmitted asynchronously (i.e. through each checker's QThread event loop), again clearly not the case.
I posted a support request to jetbrains and they promptly answered :
It was made intentionally for a better debugging. You can uncheck
setting Settings | Build, Execution, Deployment | Python Debugger >
PyQt compatible and will have workers started as expected.
Amazing!
I have obtained a widget from a QtDesigner and converted .ui file to .py file by pyside. now I want that widget to organize a database and an apart threading.Thread (in same module) to open and read database and send to UDP. the fact that I know how to deal with all these apartly but when bringing together it is hard. should I use thread as a class inside my widget class which is:
def setupUi(self, Form):
...
def retranslateUi(self, Form):
...
if __name__ == "__main__":
...
Form.show()
and also can anyone give an example for running another thread with that widget?
Thanks in advance
I give you an example for using QThreads in PySide/PyQt to achieve multithreading and communication with other Widgets. I use it myself.
from PySide import QtCore
class Master(QtCore.QObject):
command = QtCore.Signal(str)
def __init__(self):
super().__init__()
class Worker(QtCore.QObject):
def __init__(self):
super().__init__()
def do_something(self, text):
print('in thread {} message {}'.format(QtCore.QThread.currentThread(), text))
if __name__ == '__main__':
app = QtCore.QCoreApplication([])
# give us a thread and start it
thread = QtCore.QThread()
thread.start()
# create a worker and move it to our extra thread
worker = Worker()
worker.moveToThread(thread)
# create a master object and connect it to the worker
master = Master()
master.command.connect(worker.do_something)
# call a method of the worker directly (will be executed in the actual thread)
worker.do_something('in main thread')
# communicate via signals, will execute the method now in the extra thread
master.command.emit('in worker thread')
# start the application and kill it after 1 second
QtCore.QTimer.singleShot(1000, app.quit)
app.exec_()
# don't forget to terminate the extra thread
thread.quit()
I am trying to understand how to use signaling from a Qthread back to the Gui interface that started.
Setup: I have a process (a simulation) that needs to run almost indefinitely (or at least for very long stretches of time)., While it runs, it carries out various computations, amd some of the results must be sent back to the GUI, which will display them appropriately in real time.
I am using PyQt for the GUI. I originally tried using python's threading module, then switched to QThreads after reading several posts both here on SO and elsewhere.
According to this post on the Qt Blog You're doing it wrong, the preferred way to use QThread is by creating a QObject and then moving it to a Qthread. So I followed the advice inBackground thread with QThread in PyQt"> this SO question and tried a simple test app (code below): it opens up a simple GUI, let you start the background process, and it issupposed to update the step value in a spinbox.
But it does not work. The GUI is never updated. What am I doing wrong?
import time, sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class SimulRunner(QObject):
'Object managing the simulation'
stepIncreased = pyqtSignal(int, name = 'stepIncreased')
def __init__(self):
super(SimulRunner, self).__init__()
self._step = 0
self._isRunning = True
self._maxSteps = 20
def longRunning(self):
while self._step < self._maxSteps and self._isRunning == True:
self._step += 1
self.stepIncreased.emit(self._step)
time.sleep(0.1)
def stop(self):
self._isRunning = False
class SimulationUi(QDialog):
'PyQt interface'
def __init__(self):
super(SimulationUi, self).__init__()
self.goButton = QPushButton('Go')
self.stopButton = QPushButton('Stop')
self.currentStep = QSpinBox()
self.layout = QHBoxLayout()
self.layout.addWidget(self.goButton)
self.layout.addWidget(self.stopButton)
self.layout.addWidget(self.currentStep)
self.setLayout(self.layout)
self.simulRunner = SimulRunner()
self.simulThread = QThread()
self.simulRunner.moveToThread(self.simulThread)
self.simulRunner.stepIncreased.connect(self.currentStep.setValue)
self.connect(self.stopButton, SIGNAL('clicked()'), self.simulRunner.stop)
self.connect(self.goButton, SIGNAL('clicked()'), self.simulThread.start)
self.connect(self.simulRunner,SIGNAL('stepIncreased'), self.currentStep.setValue)
if __name__ == '__main__':
app = QApplication(sys.argv)
simul = SimulationUi()
simul.show()
sys.exit(app.exec_())
The problem here is simple: your SimulRunner never gets sent a signal that causes it to start its work. One way of doing that would be to connect it to the started signal of the Thread.
Also, in python you should use the new-style way of connecting signals:
...
self.simulRunner = SimulRunner()
self.simulThread = QThread()
self.simulRunner.moveToThread(self.simulThread)
self.simulRunner.stepIncreased.connect(self.currentStep.setValue)
self.stopButton.clicked.connect(self.simulRunner.stop)
self.goButton.clicked.connect(self.simulThread.start)
# start the execution loop with the thread:
self.simulThread.started.connect(self.simulRunner.longRunning)
...
After reading the literature on QProcesses and the multiprocessing module for python, I am still having trouble creating a working and responsive GUI throughout having large processes ongoing in the background.
So far, I have come up with this simplified version of my application, which still shows similar problems to what many have described.
from PyQt4 import QtCore, QtGui
import multiprocessing as mp
import numpy as np
import sys
class Spectra:
def __init__(self, spectra_name, X, Y):
self.spectra_name = spectra_name
self.X = X
self.Y = Y
self.iteration = 0
def complex_processing_on_spectra(self, pipe_conn):
self.iteration += 1
pipe_conn.send(self.iteration)
class Spectra_Tab(QtGui.QTabWidget):
def __init__(self, parent, spectra):
self.parent = parent
self.spectra = spectra
QtGui.QTabWidget.__init__(self, parent)
self.treeWidget = QtGui.QTreeWidget(self)
self.properties = QtGui.QTreeWidgetItem(self.treeWidget, ["Properties"])
self.step = QtGui.QTreeWidgetItem(self.properties, ["Iteration #"])
self.consumer, self.producer = mp.Pipe()
# Make process associated with tab
self.process = mp.Process(target=self.spectra.complex_processing_on_spectra, args=(self.producer,))
def update_GUI(self, iteration):
self.step.setText(1, str(iteration))
def start_computation(self):
self.process.start()
while(True):
message = self.consumer.recv()
if message == 'done':
break
self.update_GUI(message)
self.process.join()
return
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self)
self.setTabShape(QtGui.QTabWidget.Rounded)
self.centralwidget = QtGui.QWidget(self)
self.top_level_layout = QtGui.QGridLayout(self.centralwidget)
self.tabWidget = QtGui.QTabWidget(self.centralwidget)
self.top_level_layout.addWidget(self.tabWidget, 1, 0, 25, 25)
process_button = QtGui.QPushButton("Process")
self.top_level_layout.addWidget(process_button, 0, 1)
QtCore.QObject.connect(process_button, QtCore.SIGNAL("clicked()"), self.process)
self.setCentralWidget(self.centralwidget)
self.centralwidget.setLayout(self.top_level_layout)
# Open several files in loop from button - simplifed to one here
X = np.arange(0.1200,.2)
Y = np.arange(0.1200,.2)
self.spectra = Spectra('name', X, Y)
self.spectra_tab = Spectra_Tab(self.tabWidget, self.spectra)
self.tabWidget.addTab(self.spectra_tab, 'name')
def process(self):
self.spectra_tab.start_computation()
return
if __name__ == "__main__":
app = QtGui.QApplication([])
win = MainWindow()
win.show()
sys.exit(app.exec_())
This should be fully capable of executing if you have the dependencies.
At the moment I have a QThreaded version of my program which works with signals and slots; Hwoever, I think it is important to have the ability to use all of a computers processors, since most users have ~8 cores available to them. So, I would like to expand this signal/slot threaded approach to the multiprocessed version using multiprocessing or QProcesses.
Does anyone have suggestions for whether or not to use QProcess or multiprocessing? While they are both complicated to me, QProcess seems as though it has less forums of people using pyQt, So I went with multiprocessing. Would it be simpler to go with QProcess since I already have signals/slots working with threads?
EDIT: Should I add a class like this as suggested?
class My_Process(QtCore.QProcess):
def __init__(self, spectra):
QtCore.QProcess.__init__(self)
self.spectra = spectra
def worker(self):
QtConcurrent.run(self.spectra, self.spectra.complex_processing_on_spectra)
def run(self):
QtCore.QObject.connect(self, QtCore.SIGNAL(QTimer.timeout()), self.worker)
Even though the question is old and has been answered I would like add some clarification:
AFAIK QtConcurrent is not available in PyQt.
Threads in Qt with C++ are different from PyQt or python threads. The latter need to acquire python's GIL (global interpreter lock) when they run which effectively means that there is no real concurrency between python/pyqt threads.
Python threads are ok to keep the gui responsive, but you won't see any performance improvements for cpu bound tasks. I recommend using multiprocessing together with a QThread on your main process that handles the communication with the child process. You can use signals and slots between your main (gui) and your communication thread.
edit: I just had the same problem and did something along these lines:
from multiprocessing import Process, Queue
from PyQt4 import QtCore
from MyJob import job_function
# Runner lives on the runner thread
class Runner(QtCore.QObject):
"""
Runs a job in a separate process and forwards messages from the job to the
main thread through a pyqtSignal.
"""
msg_from_job = QtCore.pyqtSignal(object)
def __init__(self, start_signal):
"""
:param start_signal: the pyqtSignal that starts the job
"""
super(Runner, self).__init__()
self.job_input = None
start_signal.connect(self._run)
def _run(self):
queue = Queue()
p = Process(target=job_function, args=(queue, self.job_input))
p.start()
while True:
msg = queue.get()
self.msg_from_job.emit(msg)
if msg == 'done':
break
# Things below live on the main thread
def run_job(input):
""" Call this to start a new job """
runner.job_input = input
runner_thread.start()
def handle_msg(msg):
print(msg)
if msg == 'done':
runner_thread.quit()
runner_thread.wait()
# Setup the OQ listener thread and move the OQ runner object to it
runner_thread = QtCore.QThread()
runner = Runner(start_signal=runner_thread.started)
runner.msg_from_job.connect(handle_msg)
runner.moveToThread(runner_thread)
QProcess is for reading and writing to pipes (shell/cmd). Use one of these.
Create a class with worker function and run it as thread by QtConcurrent.run(object, method, args)
connect QTimer.timeout() with your worker function.
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()