PDA

View Full Version : [question]redirecting stdout to display via widget?



sbjaved
6th January 2009, 16:03
Hi everyone.
I'm very new to PyQt4 (its my fifth day) and I would like some help with this program. I'm getting familiar with different classes and have only started to wrap my head around the SIGNAL/SLOT concept.
I want to redirect the output of the system call in the code to QTextBrowser, but I'm missing something basic here. Maybe use a different widget? conceptual mistake?


import sys
from PyQt4 import QtGui, QtCore
from subprocess import Popen, PIPE

class dialerGui(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)

self.setWindowTitle('PTCL')
self.setWindowIcon(QtGui.QIcon('/usr/share/pixmaps/idle.xpm'))

dial = QtGui.QPushButton('Dial', self)
self.connect(dial, QtCore.SIGNAL('clicked()'), self.slotDial)

quit = QtGui.QPushButton('Disconnect', self)
self.connect(quit, QtCore.SIGNAL('clicked()'), QtGui.qApp, QtCore.SLOT('quit()'))

output = QtGui.QLabel('Output')

displaybox = QtGui.QTextBrowser()
self.connect(dial, QtCore.SIGNAL('clicked()'), self.showOutput)

grid = QtGui.QGridLayout()
grid.setSpacing(10)
grid.addWidget(dial, 1, 0)
grid.addWidget(quit, 1, 1)
grid.addWidget(output, 2, 0)
grid.addWidget(displaybox, 3, 0, 5, 3)

self.setLayout(grid)
self.resize(190, 50)

def slotDial(self):
x = Popen(("wvdial", "ptcl"), stdout=PIPE).stdout
f = x.readline()
x.close()

def showOutput(self):
displaybox.append(f)

app = QtGui.QApplication(sys.argv)
dg = dialerGui()
dg.show()
sys.exit(app.exec_())


Any help will be much appreciated.

sbjaved
7th January 2009, 09:32
Here is the traceback:

[sbjaved@pasaris Code]$ python ptcl_dialer_testing.py
--> WvDial: Internet dialer version 1.60
--> Warning: section [Dialer ptcl] does not exist in wvdial.conf.
--> Cannot open /dev/modem: Device or resource busy
--> Cannot open /dev/modem: Device or resource busy
--> Cannot open /dev/modem: Device or resource busy
Traceback (most recent call last):
File "ptcl_dialer_testing.py", line 46, in showOutput
displaybox.append(f)
NameError: global name 'displaybox' is not defined

This output only displays in the terminal. I would like to redirect it to the QTextBrowser widget.

rexi
7th January 2009, 21:28
Have you considered using QProcess instead of that popen function? It provides methods to read a process's output, like QProcess::readAllStandardOutput(). Or maybe Python itself provides a way to execute external processes and return their output, like Perl or Ruby do with ``?

wysota
8th January 2009, 00:14
I would use a simple system() call, I'm sure Python has one. It's the most simple way to fetch output of a short running program.

sbjaved
8th January 2009, 11:06
I would use a simple system() call, I'm sure Python has one. It's the most simple way to fetch output of a short running program.
The subprocess module allows you to redirect the stderr/stdout which in my case is linked to the variable 'f'. The problem I'm having is linking the variable 'f' defined in showOutput function to 'displaybox'(QTextBrowser) defined in __init__ function. Python doesn't recognize the local variable 'displaybox' (see the Traceback) and I'm stumped thinking of a way to link the output to the widget.
In Line 21 of the code, I've linked the signal clicked() emitted by the dial QPushButton widget to the showOutput function:

displaybox = QtGui.QTextBrowser()
self.connect(dial, QtCore.SIGNAL('clicked()'), self.showOutput)

Maybe something wrong here?

sbjaved
8th January 2009, 11:17
Have you considered using QProcess instead of that popen function? It provides methods to read a process's output, like QProcess::readAllStandardOutput(). Or maybe Python itself provides a way to execute external processes and return their output, like Perl or Ruby do with ``?
I tried using QProcess following the guide posted here (http://diotavelli.net/PyQtWiki/Capturing_Output_from_a_Process). But that guide is for PyQt and I couldn't get it to work. Maybe if you could post a simple PyQt4 QProcess example, it would be very helpful...
Thanks for replying.

rexi
8th January 2009, 18:06
Sorry, but I don't really speak enough Python to provide an example ;)

But looking at your code, I guess dialogbox is not a member variable of your class, but local to the __init__ method. Aren't members accessed by something like self.displaybox?

GeertVc
8th February 2009, 20:42
I tried using QProcess following the guide posted here (http://diotavelli.net/PyQtWiki/Capturing_Output_from_a_Process). But that guide is for PyQt and I couldn't get it to work. Maybe if you could post a simple PyQt4 QProcess example, it would be very helpful...
Thanks for replying.

Hi,

I suffered from the same. The example you're referring to, is indeed not working flawless under PyQt4.

I've been able to "translate" it to PyQt4. I'm using Eric4 as my IDE and created a project for the corrected version. It's working very good now...

If you're interested in the code, let me know. It needs some more cleaning up, but I think I can manage to give it to you within a couple of days.

Best rgds,

--Geert

sbjaved
8th February 2009, 20:51
Hi,

I suffered from the same. The example you're referring to, is indeed not working flawless under PyQt4.

I've been able to "translate" it to PyQt4. I'm using Eric4 as my IDE and created a project for the corrected version. It's working very good now...

If you're interested in the code, let me know. It needs some more cleaning up, but I think I can manage to give it to you within a couple of days.

Best rgds,

--Geert
I'm very interested. Please email me the code at sbjaved AT gmail.

fantagol
19th April 2011, 23:40
Hi folks!
I'm a newbie and i'm trying to "translate" This tutorial (http://diotavelli.net/PyQtWiki/Capturing_Output_from_a_Process) to PyQt4.
I don't get the output of the process on the textBrowser.
Can anyone help me?
Fantagol

test1.py


#!/usr/bin/python
from time import sleep

print "this is"
print "a test""
i=0
while 1:
print i
sleep(1)
if i == 5:
#os.kill(os.getpid(),signal.SIGKILL)
i +=1


CapturingOutputfromProcess.py


import sys,os,signal
from PyQt4 import QtCore, QtGui

class Window(QtGui.QWidget):
def __init__(self):
#Layout
QtGui.QWidget.__init__(self)
self.textBrowser = QtGui.QTextBrowser(self)
self.lineEdit = QtGui.QLineEdit(self)
self.startButton = QtGui.QPushButton("Start", self)
self.stopButton = QtGui.QPushButton("Stop", self)
self.stopButton.setEnabled(False)
layout = QtGui.QGridLayout(self)
layout.setSpacing(8)
layout.addWidget(self.textBrowser, 0, 0)
layout.addWidget(self.lineEdit, 1, 0)
layout.addWidget(self.startButton, 1, 1)
layout.addWidget(self.stopButton, 1, 2)
#Connection
self.connect(self.lineEdit, QtCore.SIGNAL("returnPressed()"), self.startCommand)
self.connect(self.startButton, QtCore.SIGNAL("clicked()"), self.startCommand)
self.connect(self.stopButton, QtCore.SIGNAL("clicked()"), self.stopCommand)
#Process
self.process = QtCore.QProcess()
self.connect(self.process, QtCore.SIGNAL("readyReadStdout()"), self.readOutput)
self.connect(self.process, QtCore.SIGNAL("readyReadStderr()"), self.readErrors)
self.connect(self.process, QtCore.SIGNAL("processExited()"), self.resetButtons)

def startCommand(self):
self.process.start("python test1.py", QtCore.QStringList(["my_file.tex"]))
StartString="Process started with pid: "+str(self.process.pid())
print StartString
self.textBrowser.append(StartString)
print self.process.ProcessState()
#self.process.close()
self.startButton.setEnabled(False)
self.stopButton.setEnabled(True)
#self.textBrowser.clear()

#~ if not self.process.start():
#~ self.textBrowser.setText(
#~ QtGui.QString("*** Failed to run %1 ***").arg(self.lineEdit.text())
#~ )
#~ self.resetButtons()
#~ return
def stopCommand(self):
self.resetButtons()
print "Stop process, PID was: ", self.process.pid()
os.kill(self.process.pid(), signal.SIGKILL)
#QtCore.QTimer.singleShot(5000, self.process, SLOT("kill()"))

def readOutput(self):
print self.process.readStdout()
self.textBrowser.append(QtGui.QString(self.process .readStdout()))
if self.process.isRunning()==False:
self.textBrowser.append("\n Completed Successfully")
def readErrors(self):
self.textBrowser.append("error: " + QtGui.QString(self.process.readLineStderr()))
def resetButtons(self):
self.startButton.setEnabled(True)
self.stopButton.setEnabled(False)

if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)

window = Window()
window.show()

sys.exit(app.exec_())

wysota
20th April 2011, 08:33
First you read output from the process and print it to the screen and then you read it again and try to add it to the widget. Since you have already read the output, nothing remains to be read and you don't get anything in your widget.

fantagol
20th April 2011, 10:38
Tks 1k wysota, for your answer, but if I change readOutput in

def readOutput(self):
print self.process.readStdout()

I don't get any output too, and futher the process is not running,
according to QProcess.ProcessState = 0.

bye
Fantagol

wysota
20th April 2011, 10:53
When are you reading the state? Just after calling start()? If so then the process is not started yet at that point. Either way make sure you pass correct arguments to start(). To me it seems "python" is the name of your program and "test1.py" and "my_file.tex" are its arguments. Unless of course you do have an executable called "python test1.py" but that would be weird.

fantagol
20th April 2011, 13:12
Thanks very much for your precious help wysota, but nothing happens.This is what I did:

According to this Documentation (http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qprocess.html#start)

I add this connection

self.connect(self.process, QtCore.SIGNAL("started()"), self.processIsStarted)

add a new function:


def processIsStarted(self):
print "according to SIGNAL STARTED() process with pid: ",str(self.process.pid()),"is started"
print "Its State is ", self.process.ProcessState()
and change the calling:

def startCommand(self):
self.process.start("python", QtCore.QStringList(["test1.py","my_file.txt"]))
StartString="Process started with pid: "+str(self.process.pid())
print StartString
self.textBrowser.append(StartString)
print self.process.ProcessState()
self.startButton.setEnabled(False)
self.stopButton.setEnabled(True)
To starts a given program, i have to use this instantiation?

QProcess.start (self, QString program, QStringList arguments, QIODevice.OpenMode mode = QIODevice.ReadWrite)
Is QIODevice.OpenMode important?

tks to all
fantagol

wysota
20th April 2011, 17:55
No, the mode is not important for you. Does it work if you change test1.py to an absolute path? It is likely your file cannot be found. Although in this case python SHOULD start, it would just quit immediately. Unless your python binary cannot be found as well.

fantagol
20th April 2011, 23:24
Here the new files:
6269
6268

I think that the python script "test1.py" is started by my code, but self.process.ProcessState() say me quite another thing. I change the test1.py with a simple infinite print cycle:


#!/usr/bin/python
print "test1"
print "test2"
#from time import sleep
i=0
while 1:
print i
#sleep(1)
if i == 5:
#os.kill(os.getpid(),signal.SIGKILL)
pass
i +=1


I think that the new process is started because i can see its pid on "System Monitor", and its cpu occupation. This is the new attemp in the script:




def startCommand(self):
self.process.start("python", QtCore.QStringList(["/home/MyUserAccount/test1.py"]))
StartString="Process started with pid: "+str(self.process.pid())
print StartString
self.textBrowser.append(StartString)
print self.process.ProcessState()
#self.process.close()
self.startButton.setEnabled(False)
self.stopButton.setEnabled(True)
#self.textBrowser.clear()

def processIsStarted(self):
print "according to SIGNAL STARTED() process with pid: ",self.process.pid()

def stopCommand(self):
print "Attempt to stop a process with Pid: ", str(self.process.pid())
print "What is the State of this process?"
print "Its State is ", self.process.ProcessState()
self.resetButtons()
print "Stop process, PID was: ", self.process.pid()
os.kill(self.process.pid(), signal.SIGKILL)

When i run $python CapturingOutputfromProcess03.py , and press the button start, the message is shown correctly on the textbrowser "Process started with pid: 2277":

Process started with pid: 2277
0
according to SIGNAL STARTED() process with pid: 2277

A new pid appears on the System Monitor with its CPU occupation.
Then, when I press the stop button, the gui disappears and the terminal reveals this:


Attempt to stop a process with Pid: 2283
What is the State of this process?
Its State is 0
Stop process, PID was: 2283

I hope we can reach a solution.
Thanks a lot

wysota
21st April 2011, 14:00
But what's wrong exactly? It seems the script gets executed, what more do you want?

fantagol
21st April 2011, 22:03
But what's wrong exactly? It seems the script gets executed, what more do you want?
I want to redirect the standard output, and print on the textBrowser what comes from test1.py. I don't see anything append on the widget by test1.py.

wysota
21st April 2011, 22:46
Then connect to the appropriate signal and read the output from within the slot.

fantagol
22nd April 2011, 00:53
I'm looking for the solution from many days!!
This is the SIGNAL:


self.connect(self.process, QtCore.SIGNAL("readyReadStdout()"), self.readOutput)
self.connect(self.process, QtCore.SIGNAL("readyReadStderr()"), self.readErrors)
and this is the SLOT:



def readOutput(self):
#print self.process.readStdout()
self.textBrowser.append(QtGui.QString(self.process .readStdout()))
#if self.process.isRunning()==False:
# self.textBrowser.append("\n Completed Successfully")
def readErrors(self):
self.textBrowser.append("error: " + QtGui.QString(self.process.readLineStderr()))

....Where is the mistake?

norobro
22nd April 2011, 03:12
As Wysota says:

connect to the appropriate signal
QProcess::readyReadStdout() is a PyQt3 signal. Check all of your functions against the doc that you linked in post #14.

wysota
22nd April 2011, 11:16
....Where is the mistake?
The mistake is you are trying to use a signal that doesn't exist.

fantagol
22nd April 2011, 18:35
Yeah.... i found the mistake this morning, The correct signals are:

readyReadStandardOutput()
readyReadStandardError()
but now i read the text printed by test1.py only when the process is terminated.
I'd like to read line by line while it is running

norobro
22nd April 2011, 19:53
A forum search yielded a similar thread (http://www.qtcentre.org/threads/38302-QProcess-readyRead-QTextEdit?highlight=flush).

For Python read the manual page: man python

fantagol
23rd April 2011, 04:32
I can't modify the original test1.py script and flushing device after print.

norobro
23rd April 2011, 04:48
From man python:
-u Force stdin, stdout and stderr to be totally unbuffered.So just pass the "-u" switch as an argument when you call QProcess::start().

fantagol
23rd April 2011, 11:44
It Works!!!! Thanks so much norobro and wysota!!
What book you advise me to read about Qt an python? Rapid gui?