I have an error in my Python code, I'm new to Python, and I'm also new to PyQT. What I'm trying to do is build a basic http proxy server with a GUI. I've done it in the console, but when I tried to implement a GUI, I'm getting an error.
Here is my code, any help is appreciated.
import BaseHTTPServer, SocketServer,sys
from PyQt4 import QtCore, QtGui
from Ui_MiniGui import Ui_MainWindow
class ThreadingHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
pass
class webServer(QtCore.QThread):
log = QtCore.pyqtSignal(object)
def __init__(self, parent = None):
QtCore.QThread.__init__(self, parent)
def run(self):
self.log.emit("Listening On Port 1805")
Handler = BaseHTTPServer.BaseHTTPRequestHandler
def do_METHOD(self):
method = self.command
#I got some trouble here, how can i emit the signal back to the log?
#self.log.emit(method) not work, python not crash
#webServer.log.emit(method) not work, python not crash
#below one not work and python crashes immediately
webServer().log.emit(method)
Handler.do_GET = do_METHOD
self.httpd = ThreadingHTTPServer(("", 1805), Handler)
self.httpd.serve_forever()
class Form(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.server = webServer()
self.server.log.connect(self.write_to_textEdit)
self.server.start()
def write_to_textEdit(self, data):
print data
self.ui.textEdit.setText(data)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = Form()
myapp.show()
sys.exit(app.exec_())
In this line
webServer().log.emit(method)
You create a new webserver instance and emit it's log signal. As this is a new object, this signal hasn't been connected to anything, so it's doing nothing.
To emit the signal on the right object, you could do the following:
def run(self):
...
server = self
def do_METHOD(self):
...
server.log.emit(self)
Related
I think my issue is simple, but I have hit a brick wall. I am not a programmer but needed a program to control a laser engraver via Arduino. My Original code was mostly working, but I wanted the ability to select a serial port with a QComboBox so I can make it software available for everyone.
I figured out how to do that with the code below:
import sys
import serial
import time
import serial.tools.list_ports
from PyQt4 import QtGui
from window_test import Ui_MainWindow
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.btn_laser_poweron.clicked.connect(self.btnFIRE)
self.ui.btn_laser_poweroff.clicked.connect(self.btnOFF)
self.ui.btn_lig_power.clicked.connect(self.btnLIG)
self.ui.btn_cutting_power.clicked.connect(self.btnCUT)
self.ui.btn_power_meter.clicked.connect(self.btnTEST)
self.ui.spinBox.valueChanged.connect(self.PwrLevel)
self.ui.comboBox.activated.connect(self.srlprt)
def srlprt(self):
serial.Serial(str(self.ui.comboBox.currentText()))
def btnFIRE(self):
ser.write("a" + chr(255))
def btnOFF(self):
ser.write("b" + chr(0))
def btnTEST(self):
ser.write("c" + chr(0))
time.sleep(59.5)
ser.write("d" + chr(255))
def btnLIG(self):
ser.write("e" + chr(29))
def btnCUT(self):
ser.write("f" + chr(160))
def PwrLevel(self):
val = self.ui.spinBox.value()
ser.write("g" + chr(val))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Main()
window.show()
sys.exit(app.exec_())
Now my problem is that none of my buttons work because "ser" is not globally defined. I understand that I broke that when I removed "ser = serial.Serial(port=COM3)" when it was above the class definition, but I don't know how to fix it. Any help would be greatly appreciated.
Cheers!
A simple solution would be to just set ser as attribute of your Main instance. Also it couldn't hurt to close the serial connection if it is open before opening a new one, e.g:
import sys
import serial
import time
import serial.tools.list_ports
from PyQt4 import QtGui
from window_test import Ui_MainWindow
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ser = None
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.btn_laser_poweron.clicked.connect(self.btnFIRE)
self.ui.btn_laser_poweroff.clicked.connect(self.btnOFF)
self.ui.btn_lig_power.clicked.connect(self.btnLIG)
self.ui.btn_cutting_power.clicked.connect(self.btnCUT)
self.ui.btn_power_meter.clicked.connect(self.btnTEST)
self.ui.spinBox.valueChanged.connect(self.PwrLevel)
self.ui.comboBox.activated.connect(self.srlprt)
def srlprt(self):
if self.ser:
self.ser.close()
self.ser = serial.Serial(str(self.ui.comboBox.currentText()))
def btnFIRE(self):
self.ser.write("a" + chr(255))
def btnOFF(self):
self.ser.write("b" + chr(0))
def btnTEST(self):
self.ser.write("c" + chr(0))
time.sleep(59.5)
self.ser.write("d" + chr(255))
def btnLIG(self):
self.ser.write("e" + chr(29))
def btnCUT(self):
self.ser.write("f" + chr(160))
def PwrLevel(self):
val = self.ui.spinBox.value()
self.ser.write("g" + chr(val))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Main()
window.show()
sys.exit(app.exec_())
The user enters values using line edits on the MyWidget screen and then presses the Enter button. This opens the MyDialog screen on which data will be plotted when the Run button is pressed. How can I make the line edit data accessible to run in MyDialog for plotting? Or, is there a better way of doing this which wouldn't require passing variables between classes? My program is based on this answer.
from PyQt4 import QtCore, QtGui, uic
# Import Qt widgets
from matplotlib.backends.backend_qt4agg \
import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg \
import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
GUI_FILE = "Minimal_UI.ui" # GUI with line edit and 'enter' button
form_class = uic.loadUiType(GUI_FILE)[0]
class MyWidget(QtGui.QWidget, form_class):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.setupUi(self)
self.pushButton_Enter.clicked.connect(self.on_pushButton_clicked)
self.dialogTextBrowser = MyDialog(self)
#QtCore.pyqtSlot()
def on_pushButton_clicked(self):
# I'd like to be able to pass Temp_0 to the run method
self.Temp_0 = self.lineEdit_Temp_0.text()
self.dialogTextBrowser.exec_()
class MyDialog(QtGui.QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.fig = Figure()
self.canvas = FigureCanvas(self.fig)
self.toolbar = NavigationToolbar(self.canvas, self)
self.run_button = QtGui.QPushButton('Run')
self.run_button.clicked.connect(self.run)
self.stop_button = QtGui.QPushButton('Stop')
self.stop_button.clicked.connect(self.stop)
layout = QtGui.QVBoxLayout()
# Widgets are stacked in the order they are added
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.run_button)
layout.addWidget(self.stop_button)
self.setLayout(layout)
def run(self):
# Create axes
ax = self.fig.add_subplot(111)
# Discard the old graphs
ax.hold(False)
# Plot data--I'd like to be able to use line edit data here
ax.plot([1, 2, 3, 4], '*-')
# Refresh canvas
self.canvas.draw()
def stop(self):
print 'Stop Pressed'
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWidget')
main = MyWidget()
main.show()
sys.exit(app.exec_())
MyDialog constructor has an attribute parent.
With the code bellow, you create an instance of MyDialog with MyWidget as a parent:
self.dialogTextBrowser = MyDialog(self)
Two ways for a widget to access data from it's parent:
Use the parent attribute in the __init__ function
self.lineEditData=parent.lineEdit.text()
Use the parent() method anywhere
def run(self):
self.lineEditData=self.parent().lineEdit.text()
I say it depends on how your suppose to use the application. If your suppose to fill the lineEdit once click and get a plot, I would use the parent attribute or directly pass the data in the __init__ function.
But if the user can go back to the lineEdit, change something, and click "run" again, then you should use the parent() method in run.
I wrote a simple application in Python and PySide. When I run it, SIGNALs are not working.
The application starts without errors.
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
dial = QDial()
dial.setNotchesVisible(True)
spinbox = QSpinBox()
layout = QHBoxLayout()
layout.addWidget(dial)
layout.addWidget(spinbox)
self.setLayout(layout)
self.connect(dial, SIGNAL("valueChaged(int)"), spinbox.setValue)
self.connect(spinbox, SIGNAL("valueChaged(int)"), dial.setValue)
self.setWindowTitle("Signals and Slots")
# END def __init__
# END class Form
def main():
app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
# END def main
if __name__ == '__main__':
main()
# END if
I am using:
Pyside 1.2.2;
Python 2.7.6;
OS Centos; Windows 7
I am running the application with:
Sublime Text 3 and Eclipse Luna;
How can I make SIGNALs working?
Your signal name is incorrect;
Incorrect :
valueChaged (int)
Correct :
valueChanged (int)
Test it, work fine;
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class QFormDialog (QDialog):
def __init__(self, parent = None):
super(QFormDialog, self).__init__(parent)
self.myQial = QDial()
self.myQSpinbox = QSpinBox()
self.myQHBoxLayout = QHBoxLayout()
self.myQial.setNotchesVisible(True)
self.myQHBoxLayout.addWidget(self.myQial)
self.myQHBoxLayout.addWidget(self.myQSpinbox)
self.setLayout(self.myQHBoxLayout)
self.connect(self.myQial, SIGNAL('valueChanged(int)'), self.myQSpinbox.setValue)
self.connect(self.myQSpinbox, SIGNAL('valueChanged(int)'), self.myQial.setValue)
self.setWindowTitle('Signals and Slots')
if __name__ == '__main__':
myQApplication = QApplication(sys.argv)
myQFormDialog = QFormDialog()
myQFormDialog.show()
myQApplication.exec_()
Note : PyQt4 & PySide is same way to implemented.
I am currently learning how to work with the tkinter gui and I am trying to integrate with SQLAlchemy. The first hurdle was figuring out how to move between frames without destroying/closing the window, luckily I found this question: Switch between two frames in tkinter. So I used a lot of the provided code and created a simple application that is trying to have someone log in.
Now for the part I can't figure out. I have a function _check_credentials(self) deep inside the structure of windows that is trying to query the db. I have successfully created the table and queried it elsewhere.
I need to pass the session from the main.py module to the Login class and finally to the _check_credentials(self) method.
main.py -> Authorzation() -> Login() -> login_btn = tk.Button() -> _check_credentials()
Additional info:
I have included all of my code. Right now it runs, but crashes when the login button is pressed.
I have attempted to pass the session directly, ie. Authorzation(Session) but that crashes immediately.
Also, I am trying to follow the guidelines laid out in the sqlalchemy docs http://docs.sqlalchemy.org/en/latest/orm/session.html, FAQ, "When do I construct a Session, when do I commit it, and when do I close it?".
main.py:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from module1 import Authorzation
import Tkinter as tk
Base = declarative_base()
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
app = Authorzation()
app.mainloop()
module1.py:
import Tkinter as tk
import user
TITLE_FONT = ("Helvetica", 18, "bold")
class Authorzation(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (Login, Register):
frame = F(container, self)
self.frames[F] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Login)
def show_frame(self, c):
'''Show a frame for the given class'''
frame = self.frames[c]
frame.tkraise()
class Login(tk.Frame):
def __init__(self, parent, controller):
self.controller = controller
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Title", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
#Create the Username field
label_username = tk.Label(self, text="username")
label_username.pack()
self.username = tk.StringVar()
tk.Entry(self, textvariable=self.username).pack()
#Create the password field
label_password = tk.Label(self, text="password")
label_password.pack()
self.password = tk.StringVar()
tk.Entry(self, textvariable=self.password).pack()
login_btn = tk.Button(self, text="Login",
command=self._check_credentials)
login_btn.pack(pady=5)
reg_btn = tk.Button(self, text="Registration",
command=lambda: controller.show_frame(Register))
reg_btn.pack(pady=10)
def _check_credentials(self):
session_user = session.query(user.User)\
.filter_by(
username=self.username
).first()
if session_user:
return session_user.check_pwd(), session_user
else:
print("Sorry we could not find your username")
return False, None
self.controller.show_frame(Login)
class Register(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="This is page 1", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame(Login))
button.pack()
After doing research on *args, and **kwargs. I realized I had not tried putting the session first in the __init__() function. Here is how I then passed the session to the controller.
main.py:
app = Authorzation(session)
module1.py
class Authorzation(tk.Tk):
def __init__(self, session, *args, **kwargs):
self.session = session
Once the session was in the controller it was easy to reference it from the Login() frame.
module1.py:
class Login(tk.Frame):
def __init__(self, parent, controller):
self.controller = controller
...
def _check_credentials(self):
session = self.controller.session
Note: this solution is also nice because it alleviates the need to pass the session through the button.
I've been having difficulty with my PySide program for a few days now. I don't think the problem is incredibly difficult because there are answer out there. Problem I have is none of them seem to work for me.
I want to 'listen' to the file objects stdout and stderr and output the contents to QText Edit widget while my PySide program is running. Now, I already realise this question (or something similar) has been asked before on here but like I said, can't get it to work for me for some reason and most other solutions out there are based on the one that I can't get working, so a very frustrating last few days for me. This solution (OutLog), is included in my code snippet below, just in case one of you guys can see a botched implementation on my part.
Things to remember:
1 I'm doing this on Windows 7(duuuh, da, da, duh)
2 I'm using eclipse and running it from inside the IDE(duh, da, da, duh, DUUUUH: It would be really handy if the suggestions worked with either commandline or an IDE)
3 I really just want to duplicate the output of stdout and stderr to the widget while the program runs. For this to happen line-by-line would be a dream but even if it all comes out as a chunk at the end of a loop or something, that would be fab.
4 Oh, and also regarding OutLog, could somebody tell me how, if self.out is set to 'None' in the init, this class can actually work? I mean, self.out is always a NoneType object, right???
Any help would be appreciated, even if it's just pointers to where I could find more information. I've been trying to build my own solution (I'm a bit of a sadist that way) but I've found it hard to find relevant info on how these objects work to do that.
Anyway, whine over. Here's my code:
#!/usr/bin/env python
import sys
import logging
import system_utilities
log = logging.getLogger()
log.setLevel("DEBUG")
log.addHandler(system_utilities.SystemLogger())
import matplotlib
matplotlib.use("Qt4Agg")
matplotlib.rcParams["backend.qt4"] = "PySide"
import subprocess
import plot_widget
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PySide import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
"""This is the main window class and displays the primary UI when launched.
Inherits from QMainWindow.
"""
def __init__(self):
"""Init function.
"""
super(MainWindow, self).__init__()
self.x = None
self.y = None
self.data_plot = None
self.plot_layout = None
self.terminal = None
self.setup_plot()
self.setup_interface()
def setup_plot(self):
"""Member function to setup the graph window in the main UI.
"""
#Create a PlotWidget object
self.data_plot = plot_widget.PlotWidget()
#Create a BoxLayout element to hold PlotWidget
self.plot_layout = QtGui.QVBoxLayout()
self.plot_layout.addWidget(self.data_plot)
def setup_interface(self):
"""Member function to instantiate and build the composite elements of the
UI."""
#Main widget houses layout elements (Layout cannot be placed directly in a QMainWindow).
central_widget = QtGui.QWidget()
test_splitter = QtGui.QSplitter(QtCore.Qt.Vertical)
button_splitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
#UI BoxLayout elements
central_layout = QtGui.QVBoxLayout()
#button_layout = QtGui.QHBoxLayout()
#UI PushButton elements
exit_button = QtGui.QPushButton("Close")
run_button = QtGui.QPushButton("Run...")
#UI Text output
self.editor = QtGui.QTextEdit()
self.editor.setReadOnly(True)
self.terminal = QtGui.QTextBrowser()
self.terminal.setReadOnly(True)
#UI PushButton signals
run_button.clicked.connect(self.run_c_program)
run_button.clicked.connect(self.data_plot.redraw_plot)
exit_button.clicked.connect(QtCore.QCoreApplication.instance().quit)
#Build the UI from composite elements
central_layout.addLayout(self.plot_layout)
central_layout.addWidget(self.editor)
button_splitter.addWidget(run_button)
button_splitter.addWidget(exit_button)
test_splitter.addWidget(button_splitter)
test_splitter.addWidget(self.terminal)
test_splitter.setCollapsible(1, True)
central_layout.addWidget(test_splitter)
central_widget.setLayout(central_layout)
self.setCentralWidget(central_widget)
self.show()
class OutLog:
def __init__(self, edit, out=None, color=None):
"""(edit, out=None, color=None) -> can write stdout, stderr to a
QTextEdit.
edit = QTextEdit
out = alternate stream ( can be the original sys.stdout )
color = alternate color (i.e. color stderr a different color)
"""
self.edit = edit
self.out = None
self.color = color
def write(self, m):
if self.color:
tc = self.edit.textColor()
self.edit.setTextColor(self.color)
self.edit.moveCursor(QtGui.QTextCursor.End)
log.debug("this is m {}".format(m))
self.edit.insertPlainText( m )
if self.color:
self.edit.setTextColor(tc)
if self.out:
self.out.write(m)
def main():
app = QtGui.QApplication(sys.argv)
log.debug("Window starting.")
window = MainWindow()
sys.stdout = OutLog(window.terminal, sys.stdout)
sys.stderr = OutLog(window.terminal, sys.stderr, QtGui.QColor(255,0,0))
window.show()
sys.exit(app.exec_())
log.info("System shutdown.")
if __name__ == '__main__':
main()
"Help me Obi-Wan..."
Thanks in advance guys (and gals :-))
It seems that all you need to do is override sys.stderr and sys.stdout with a wrapper object that emits a signal whenever output is written.
Below is a demo script that should do more or less what you want. Note that the wrapper class does not restore sys.stdout/sys.stderr from sys.__stdout__/sys.__stderr__, because the latter objects may not be same as the ones that were orignally replaced.
PyQt5:
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
class OutputWrapper(QtCore.QObject):
outputWritten = QtCore.pyqtSignal(object, object)
def __init__(self, parent, stdout=True):
super().__init__(parent)
if stdout:
self._stream = sys.stdout
sys.stdout = self
else:
self._stream = sys.stderr
sys.stderr = self
self._stdout = stdout
def write(self, text):
self._stream.write(text)
self.outputWritten.emit(text, self._stdout)
def __getattr__(self, name):
return getattr(self._stream, name)
def __del__(self):
try:
if self._stdout:
sys.stdout = self._stream
else:
sys.stderr = self._stream
except AttributeError:
pass
class Window(QtWidgets.QMainWindow):
def __init__(self):
super().__init__( )
widget = QtWidgets.QWidget(self)
layout = QtWidgets.QVBoxLayout(widget)
self.setCentralWidget(widget)
self.terminal = QtWidgets.QTextBrowser(self)
self._err_color = QtCore.Qt.red
self.button = QtWidgets.QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout.addWidget(self.terminal)
layout.addWidget(self.button)
stdout = OutputWrapper(self, True)
stdout.outputWritten.connect(self.handleOutput)
stderr = OutputWrapper(self, False)
stderr.outputWritten.connect(self.handleOutput)
def handleOutput(self, text, stdout):
color = self.terminal.textColor()
self.terminal.moveCursor(QtGui.QTextCursor.End)
self.terminal.setTextColor(color if stdout else self._err_color)
self.terminal.insertPlainText(text)
self.terminal.setTextColor(color)
def handleButton(self):
if QtCore.QTime.currentTime().second() % 2:
print('Printing to stdout...')
else:
print('Printing to stderr...', file=sys.stderr)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 200)
window.show()
sys.exit(app.exec_())
PyQt4:
import sys
from PyQt4 import QtGui, QtCore
class OutputWrapper(QtCore.QObject):
outputWritten = QtCore.pyqtSignal(object, object)
def __init__(self, parent, stdout=True):
QtCore.QObject.__init__(self, parent)
if stdout:
self._stream = sys.stdout
sys.stdout = self
else:
self._stream = sys.stderr
sys.stderr = self
self._stdout = stdout
def write(self, text):
self._stream.write(text)
self.outputWritten.emit(text, self._stdout)
def __getattr__(self, name):
return getattr(self._stream, name)
def __del__(self):
try:
if self._stdout:
sys.stdout = self._stream
else:
sys.stderr = self._stream
except AttributeError:
pass
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
widget = QtGui.QWidget(self)
layout = QtGui.QVBoxLayout(widget)
self.setCentralWidget(widget)
self.terminal = QtGui.QTextBrowser(self)
self._err_color = QtCore.Qt.red
self.button = QtGui.QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout.addWidget(self.terminal)
layout.addWidget(self.button)
stdout = OutputWrapper(self, True)
stdout.outputWritten.connect(self.handleOutput)
stderr = OutputWrapper(self, False)
stderr.outputWritten.connect(self.handleOutput)
def handleOutput(self, text, stdout):
color = self.terminal.textColor()
self.terminal.moveCursor(QtGui.QTextCursor.End)
self.terminal.setTextColor(color if stdout else self._err_color)
self.terminal.insertPlainText(text)
self.terminal.setTextColor(color)
def handleButton(self):
if QtCore.QTime.currentTime().second() % 2:
print('Printing to stdout...')
else:
sys.stderr.write('Printing to stderr...\n')
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 200)
window.show()
sys.exit(app.exec_())
NB:
Instances of the OutputWrapper should be created as early as possible, so as to ensure that other modules that need sys.stdout/sys.stderr (such as the logging module) use the wrapped versions wherever necessary.
self.out = None is probably a typo and should be self.out = out. That way, you can see anything that printed in the console as well. This is the first step to be sure that the code prints anything at all.
The next thing is that you need to realize which output you're redirecting. A subprocess get its own stdio, so no amount of redirection of the parent's stdout is going to have any effect.
Getting stdio correct with a subprocess isn't trivial. I suggest to start with subprocess.communicate() which gives you all the output as a single string. That's usually good enough.