[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