[PySide] using QProcess to run python function
Frank Rueter | OHUfx
frank at ohufx.com
Mon Jan 27 20:41:18 CET 2014
will do, got pulled away to a different job but will get back to this in
a few days.
On 28/01/14 08:00, Sean Fisk wrote:
> On Sat, Jan 25, 2014 at 2:38 AM, Frank Rueter | OHUfx <frank at ohufx.com
> <mailto:frank at ohufx.com>> wrote:
>
> fantastic, thanks Sean!!
> I will examine this to make sure I understand everything, then
> give QThreadPool a whirl. Am more than happy to learn from more
> experienced people and adjust my approaches accordingly, so thanks
> a lot for your time with this!
>
>
> No problem! Let me know if you have any questions about the code.
>
>
> Cheers,
> frank
>
>
>
> On 25/01/14 20:03, Sean Fisk wrote:
>>
>> Hi Frank,
>>
>> I updated your example to hopefully work as you would like. I
>> added a progress bar update as well. I’m going to make a
>> last-ditch effort to convince you to stop doing things manually
>> with |QThread|, and move to using |QThreadPool|
>> <http://pyside.github.io/docs/pyside/PySide/QtCore/QThreadPool.html>.
>> Everything about the |QThreadPool| API is much nicer, and it’s
>> worked much better for me in real projects. Also, the Python ‘s
>> |multiprocessing|
>> <http://docs.python.org/2/library/multiprocessing.html> and
>> |concurrent.futures| <http://pythonhosted.org/futures/> modules
>> can work well if you’re careful about your callbacks.
>>
>> Also, there is a large discussion about not overriding the
>> |run()| method of |QThread|. I don’t think overriding it is so
>> bad if you don’t need the thread to be running an event loop of
>> its own. I still prefer the |QThreadPool| API, though. Someone
>> please correct me if this is totally wrong and there is never a
>> reason to override it.
>>
>> Here is the code:
>>
>> |#!/usr/bin/env python
>>
>> import sys
>> import time
>>
>> from PySideimport QtGui
>> from PySideimport QtCore
>>
>> TOTAL_WIDGETS =10
>>
>> class Worker(QtCore.QObject):
>> processed = QtCore.Signal(int)
>> finished = QtCore.Signal()
>>
>> # Overriding this is not necessary if you're not doing anything in it.
>>
>> # def __init__(self):
>> # super(Worker, self).__init__()
>>
>> def helloWorld(self):
>> for iin xrange(TOTAL_WIDGETS):
>> # We sleep first to simulate an operation taking place.
>> time.sleep(0.5)
>> print 'hello %s' % i
>> self.processed.emit(i +1)
>> # Must manually emit the finished signal.
>> self.finished.emit()
>>
>> class MainUI(QtGui.QWidget):
>> def __init__(self, parent=None):
>> super(MainUI, self).__init__(parent)
>> self.extraThread = QtCore.QThread()
>>
>> # IMPORTANT: Don't quit the app until the thread has completed. Prevents errors like:
>> #
>> # QThread: Destroyed while thread is still running
>> #
>> QtGui.QApplication.instance().aboutToQuit.connect(self.quit)
>>
>> self.worker = Worker()
>> self.worker.moveToThread(self.extraThread)
>> self.setupUI()
>> self.connectSignalsAndSlots()
>>
>> def setupUI(self):
>> # CREAT MAIN LAYOUT AND WIDGETS
>> mainLayout = QtGui.QVBoxLayout()
>> btnLayout = QtGui.QHBoxLayout()
>> mainLayout.addLayout(btnLayout)
>> self.setLayout(mainLayout)
>>
>> self.progressBar = QtGui.QProgressBar(self)
>> self.progressBar.setRange(0, TOTAL_WIDGETS)
>> self.progressBar.setVisible(False)
>> self.btnWork = QtGui.QPushButton('Do Work')
>> self.btnCancel = QtGui.QPushButton('Cancel')
>> self.btnCancel.setDisabled(True)
>>
>> self.guiResponseProgressbar = QtGui.QProgressBar(self)
>> self.guiResponseProgressbar.setRange(0,0)
>>
>> self.outputWindow = QtGui.QTextEdit()
>>
>> mainLayout.addWidget(self.progressBar)
>> mainLayout.addWidget(self.outputWindow)
>> mainLayout.addWidget(self.guiResponseProgressbar)
>>
>> btnLayout.addWidget(self.btnWork)
>> btnLayout.addWidget(self.btnCancel)
>>
>> def connectSignalsAndSlots(self):
>> print 'connecting signals'
>> self.btnWork.clicked.connect(self.startWorker)
>>
>> # Pleas see <http://nooooooooooooooo.com/>. Bad bad bad bad bad.
>> # self.btnCancel.clicked.connect(self.extraThread.terminate)
>>
>> # THREAD STARTED
>> # Not necessary; just do this in startWorker.
>> # self.extraThread.started.connect(lambda: self.btnWork.setDisabled(True))
>> # self.extraThread.started.connect(lambda: self.btnCancel.setEnabled(True))
>> # self.extraThread.started.connect(self.progressBar.show)
>> self.extraThread.started.connect(self.worker.helloWorld)
>>
>> # THREAD FINISHED
>> # self.extraThread.finished.connect(lambda: self.btnCancel.setDisabled(True))
>> # self.extraThread.finished.connect(self.extraThread.deleteLater)
>> # self.extraThread.finished.connect(self.worker.deleteLater)
>> self.extraThread.finished.connect(self.finished)
>>
>> # Connect worker signals.
>> self.worker.processed.connect(self.progressBar.setValue)
>> self.worker.finished.connect(self.finished)
>>
>> # SHOW PROGRESS BAR WHEN PUBLISHING STARTS
>> # self.extraThread.started.connect(self.progressBar.show)
>> # CONNECT PROCESS TO PROGRESS BAR AND OUTPUT WINDOW
>> # NOT DONE YET
>>
>> def startWorker(self):
>> # GO
>> self.btnWork.setDisabled(True)
>> self.btnCancel.setEnabled(True)
>> self.progressBar.show()
>> print 'starting worker thread'
>> self.extraThread.start()
>>
>> # THIS IS BLOCKING THE GUI THREAD! Try putting this back in and seeing
>> # what happens to the gui_response_progressbar.
>>
>> # for i in xrange(10):
>> # print 'from main thread:', i
>> # time.sleep(.3)
>>
>> def finished(self):
>> print 'finished'
>> self.btnCancel.setDisabled(True)
>> # self.extraThread.deleteLater()
>> # self.worker.deleteLater()
>>
>> def quit(self):
>> # Quit the thread's event loop. Note that this *doesn't* stop tasks
>> # running in the thread, it just stops the thread from dispatching
>> # events.
>> self.extraThread.quit()
>> # Wait for the thread to complete. If the thread's task is not done,
>> # this will block.
>> self.extraThread.wait()
>>
>> if __name__ =='__main__':
>> args = sys.argv
>> app = QtGui.QApplication(args)
>> p = MainUI()
>> p.show()
>> # Annoyance on Mac OS X.
>> p.raise_()
>> sys.exit(app.exec_())|
>>
>> Cheers,
>>
>>
>>
>> --
>> Sean Fisk
>>
>>
>> On Sat, Jan 25, 2014 at 12:30 AM, Frank Rueter | OHUfx
>> <frank at ohufx.com <mailto:frank at ohufx.com>> wrote:
>>
>> quick update:
>> I just learnt about connection types but those didn't help
>> either in this case:
>> http://qt-project.org/doc/qt-5/threads-qobject.html#signals-and-slots-across-threads
>>
>>
>>
>>
>> On 25/01/14 18:15, Frank Rueter | OHUfx wrote:
>>> And of course I ran into trouble :-D
>>> Here is my test code trying to ustilise QThread:
>>> http://pastebin.com/Q26Q9M1M
>>>
>>> The basic structure seems to work (the extra thread and the
>>> main thread are running at the same time), but my UI does
>>> not update according to the signal connections, e.g.:
>>> When the thread starts the progress bar is supposed to be
>>> shown, the "Do Work" button should be disabled, the "Cancel"
>>> button disabled.
>>> etc.
>>>
>>> I tried calling self.update() in the MainUI when the thread
>>> starts but to no avail.
>>> I'm sure I'm missing something obvious as usual.
>>>
>>> I tried to adhere to what I learnt on this list about
>>> threading and avoid sub-classing QThread.
>>>
>>> Cheers,
>>> frank
>>>
>>> On 24/01/14 17:41, Frank Rueter | OHUfx wrote:
>>>> Great, thanks. I shall adjust my code.
>>>> I am interested in seeing how you would go about it, but
>>>> let me have a go from scratch, I will be able to understand
>>>> things much better afterwards :)
>>>>
>>>> Cheers,
>>>> frank
>>>>
>>>>
>>>> On 24/01/14 17:38, Sean Fisk wrote:
>>>>> On Thu, Jan 23, 2014 at 10:44 PM, Frank Rueter | OHUfx
>>>>> <frank at ohufx.com <mailto:frank at ohufx.com>> wrote:
>>>>>
>>>>> Much appreciated but as I mentioned, I think I jumped
>>>>> the gun with my post and should be using QThread to
>>>>> hook the python code to my QProgressBar and debug
>>>>> output window (QTextEdit).
>>>>>
>>>>> That would be a correct way to use |QProgressBar|. Also,
>>>>> for subprocess output, I would consider using
>>>>> |QPlainTextEdit| instead of |QTextEdit| as is is more
>>>>> performant with high amounts of text.
>>>>>
>>>>> If you are interested, I just wrote an asynchronous module
>>>>> for PySide for the project on which I am currently
>>>>> working. It is based on Python’s futures
>>>>> <http://pythonhosted.org/futures/> module and works great
>>>>> with Qt’s signals/slots. Although the project is primarily
>>>>> closed-source, I would be happy to share the async
>>>>> implementation with you. I don’t know if would fit your
>>>>> needs, but for us it’s much easier than manually spawning
>>>>> |QThread|s and even easier than |QThreadPool|.
>>>>>
>>>>> Good luck!
>>>>>
>>>>> I will investigate that now
>>>>>
>>>>>
>>>>>
>>>>> On 24/01/14 16:08, Sean Fisk wrote:
>>>>>> On Thu, Jan 23, 2014 at 9:42 PM, Frank Rueter | OHUfx
>>>>>> <frank at ohufx.com <mailto:frank at ohufx.com>> wrote:
>>>>>>
>>>>>> Thanks Sean and Ryan,
>>>>>>
>>>>>> I'm still not quite clear on how this ties into
>>>>>> QProcess.start()
>>>>>>
>>>>>>
>>>>>> It doesn’t tie in with |QProcess| at all. We’re
>>>>>> advising to avoid using that :)
>>>>>>
>>>>>> I do have a if __name__ ... block in the script
>>>>>> in question.
>>>>>> An example would certainly be awesome, but if
>>>>>> it's less hassle, explaining how your and Ryan's
>>>>>> advise helps use QProcess on a python module
>>>>>> might already suffice. Maybe a simlpe example
>>>>>> says it all though?!
>>>>>>
>>>>>>
>>>>>> I will whip up a simple example for you, but it might
>>>>>> take a few hours (lots of stuff to do right now).
>>>>>>
>>>>>> I'm not using python 3 btw
>>>>>>
>>>>>> Thanks guys for your help!!
>>>>>>
>>>>>> frank
>>>>>>
>>>>>>
>>>>>> On 24/01/14 15:33, Sean Fisk wrote:
>>>>>>>
>>>>>>> Hi Frank,
>>>>>>>
>>>>>>> You should definitely avoid calling Python as a
>>>>>>> subprocess if you can. As far as Ryan’s example,
>>>>>>> I agree with the |if __name__...| but I think
>>>>>>> that using the |imp| module is a bit overkill. I
>>>>>>> would recommend using Setuptool’s |entry_points|
>>>>>>> keyword
>>>>>>> <http://pythonhosted.org/setuptools/setuptools.html#automatic-script-creation>.
>>>>>>> Or distutils’ |scripts| keyword
>>>>>>> <http://docs.python.org/2/distutils/setupscript.html#installing-scripts>,
>>>>>>> if you must.
>>>>>>>
>>>>>>> An example of a well-known Python package which
>>>>>>> does this is Pygments
>>>>>>> <https://bitbucket.org/birkenfeld/pygments-main>, which
>>>>>>> has a large “library” component but also comes
>>>>>>> with the |pygmentize| command-line script. The
>>>>>>> Pygments codebase is pretty large, so if you
>>>>>>> would like me to whip up a simpler example I’d
>>>>>>> be glad to do so.
>>>>>>>
>>>>>>> Cheers,
>>>>>>>
>>>>>>> --
>>>>>>> Sean Fisk
>>>>>>>
>>>>>>>
>>>>>>> On Thu, Jan 23, 2014 at 9:17 PM, Frank Rueter |
>>>>>>> OHUfx <frank at ohufx.com <mailto:frank at ohufx.com>>
>>>>>>> wrote:
>>>>>>>
>>>>>>> Sorry if I'm being thick, but I'm not quite
>>>>>>> understanding how this helps to connect a
>>>>>>> python function to qprocess?! All your code
>>>>>>> does is execute the script, right?!
>>>>>>> I can already call myscript.main() straight
>>>>>>> up, but maybe I'm missing the point as I'm
>>>>>>> unfamiliar with the imp module.
>>>>>>>
>>>>>>> Let me elaborate a little bit more:
>>>>>>> myscript.main() calls a bunch of other
>>>>>>> python scripts that (directly or through
>>>>>>> other scripts again) execute external
>>>>>>> programs to do some conversion work. Those
>>>>>>> external programs spit out their progress to
>>>>>>> stdout which I can see fine when I run
>>>>>>> myscript.main() manually in a python terminal.
>>>>>>>
>>>>>>> Now I need run myscript.main() via QProcess
>>>>>>> and grab stdout to do be able to show a
>>>>>>> progress bar as well as show stdout and
>>>>>>> stderr in a debug window inside my QT code.
>>>>>>>
>>>>>>>
>>>>>>> Cheers,
>>>>>>> frank
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 24/01/14 14:58, Ryan Gonzalez wrote:
>>>>>>>> If you put an "if __name__ == '__main__'"
>>>>>>>> and a main functions, you could always
>>>>>>>> import the script from the GUI frontend.
>>>>>>>> Example:
>>>>>>>>
>>>>>>>> myscript.py
>>>>>>>>
>>>>>>>> def main(argv):
>>>>>>>> do_cool_stuff()
>>>>>>>> return 0
>>>>>>>>
>>>>>>>> if __name__ == '__main__':
>>>>>>>> sys.exit(main(sys.argv))
>>>>>>>>
>>>>>>>> mygui.py(Python 2):
>>>>>>>>
>>>>>>>> import imp
>>>>>>>>
>>>>>>>> ...
>>>>>>>>
>>>>>>>> main = imp.load_module('myscript',
>>>>>>>> *imp.find_module('myscript'))
>>>>>>>>
>>>>>>>> main.main(my_argv)
>>>>>>>>
>>>>>>>> mygui.py(Python 3):
>>>>>>>>
>>>>>>>> import importlib.machinery
>>>>>>>>
>>>>>>>> main =
>>>>>>>> importlib.machinery.SourceFileLoader('myscript',
>>>>>>>> 'myscript.py').load_module('myscript')
>>>>>>>>
>>>>>>>> main.main(my_argv)
>>>>>>>>
>>>>>>>>
>>>>>>>> On Thu, Jan 23, 2014 at 7:48 PM, Frank
>>>>>>>> Rueter | OHUfx <frank at ohufx.com
>>>>>>>> <mailto:frank at ohufx.com>> wrote:
>>>>>>>>
>>>>>>>> Hi all,
>>>>>>>>
>>>>>>>> I got a little code design question:
>>>>>>>>
>>>>>>>> I have a python script that does a lot
>>>>>>>> of file
>>>>>>>> processing/converting/uploading etc and
>>>>>>>> I'd like to write a decent
>>>>>>>> interface for it now.
>>>>>>>> The main goal is to be able to show the
>>>>>>>> user detailed info about the
>>>>>>>> current step and progress as well as
>>>>>>>> clean up properly in case the whole
>>>>>>>> thing is cancelled.
>>>>>>>>
>>>>>>>> My existing python code needs to stay
>>>>>>>> independent of QT so any
>>>>>>>> application that supports python can
>>>>>>>> use it.
>>>>>>>> I am wondering now how to best connect
>>>>>>>> the python script and the PySide
>>>>>>>> code. Should I just run the script as
>>>>>>>> an argument to the python
>>>>>>>> interpreter like I would with any other
>>>>>>>> program? E.g.:
>>>>>>>>
>>>>>>>> process = QtCore.QProcess(self)
>>>>>>>> process.start(<path_to_python>,
>>>>>>>> <path_to_python_script>)
>>>>>>>>
>>>>>>>> As simple as this seems, it feels odd
>>>>>>>> to use python to call itself as an
>>>>>>>> external program.
>>>>>>>>
>>>>>>>>
>>>>>>>> I'm happy to go that way but am curious
>>>>>>>> how others are doing this?!
>>>>>>>>
>>>>>>>> Cheers,
>>>>>>>> frank
>>>>>>>>
>>>>>>>> _______________________________________________
>>>>>>>> PySide mailing list
>>>>>>>> PySide at qt-project.org
>>>>>>>> <mailto:PySide at qt-project.org>
>>>>>>>> http://lists.qt-project.org/mailman/listinfo/pyside
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> Ryan
>>>>>>>> If anybody ever asks me why I prefer C++ to
>>>>>>>> C, my answer will be simple: "It's
>>>>>>>> becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait,
>>>>>>>> I don't think that was nul-terminated."
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> PySide mailing list
>>>>>>> PySide at qt-project.org
>>>>>>> <mailto:PySide at qt-project.org>
>>>>>>> http://lists.qt-project.org/mailman/listinfo/pyside
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> PySide mailing list
>>>> PySide at qt-project.org <mailto:PySide at qt-project.org>
>>>> http://lists.qt-project.org/mailman/listinfo/pyside
>>>
>>>
>>>
>>> _______________________________________________
>>> PySide mailing list
>>> PySide at qt-project.org <mailto:PySide at qt-project.org>
>>> http://lists.qt-project.org/mailman/listinfo/pyside
>>
>>
>> _______________________________________________
>> PySide mailing list
>> PySide at qt-project.org <mailto:PySide at qt-project.org>
>> http://lists.qt-project.org/mailman/listinfo/pyside
>>
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/pyside/attachments/20140128/4561cd43/attachment.html>
More information about the PySide
mailing list