-
I'm having difficulty getting subprocess output redirected to a TextArea. The following demo intentionally uses no PIPE methods in order to demonstrate the full output which I want in the TextArea; when I try adding PIPE methods I am no longer able to see output in the Thonny editor console. PIPE will create an io.TextIOWrapper which is encoded 'UTF-8' but I'm unable to convert that to a string to put into the TextArea. More importantly, I want all those print() calls to be redirected to the TextArea as well. I have AI code that will redirect output to a file (not a TextArea) so I'm fairly confident that it should be possible to do. I have included a py5.py file for testing purposes. 'cmdStr' needs to be a full path so you will need to adjust that for your system. The goal is to get everything that shows up in the Thonny console shifted over to the TextArea when the 'Run' button is hit in the following demo. Thanks in advance for any insight. # Uses Import mode for py5
# https://docs.python.org/3/library/io.html
# https://docs.python.org/2/library/subprocess.html#subprocess.Popen.stdin
from java.awt import *
from javax.swing import *
import subprocess
import os
import io
_wndW = 500
_wndH = 200
def runBtnAction(event):
global logArea, cmdStr,filePath
# cmdStr needs to be a full path (change for your system)
cmdStr = "/opt/miniconda3/bin/py5-run-sketch"
filePath = "/Users/xxxxx/py5_code/Arrows.py"
try:
process = subprocess.Popen([cmdStr, filePath],text=True)
# process = subprocess.Popen([cmdStr,filePath], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1024)
# outStr = <_io.TextIOWrapper name=96 encoding='UTF-8'>
# outStr = process.stdout.readline()
outFile = process.stdout
except Exception as e:
print(f"Command failed with error: {e}")
print(f"Stderr: {e.stderr}")
def runBtn():
runBtn = JButton("Run")
runBtn.setBounds(40, 10, 70, 24)
frame.add(runBtn)
runBtn.addActionListener(runBtnAction)
runBtn.repaint()
def logArea():
global logArea
logArea = JTextArea()
logArea.setBounds(20,40,_wndW-40,_wndH - 60)
logArea.setEditable(True)
logArea.setLineWrap(True)
logArea.setWrapStyleWord(True)
frame.add(logArea)
logArea.repaint()
def setup():
global frame,logArea
size(_wndW, _wndH)
wnd = get_surface()
canvas = wnd.get_native()
frame = canvas.getFrame()
wnd.set_title('Subprocess Demo')
wnd.set_always_on_top(True)
frame.remove(canvas)
logArea()
runBtn() Test File: Arrows.py # Uses Imported mode for py5
from controlP5 import ControlP5
def drawUpArrow():
upArrw = create_graphics(20,20)
upArrw.begin_draw()
upArrw.background(255)
upArrw.fill(0,255,0)
upArrw.triangle(10,2,2,18,18,18)
upArrw.end_draw()
return upArrw
def drawDownArrow():
dArrw = create_graphics(20,20)
dArrw.begin_draw()
dArrw.background(255)
dArrw.fill(0,255,0)
dArrw.triangle(2,2,18,2,10,18)
dArrw.end_draw()
return dArrw
def settings():
size(200,200)
def setup():
global up,dwn,cp5
window_title("cp5 Button Demo")
cp5 = ControlP5(get_current_sketch())
upArrw = drawUpArrow()
up = cp5.addButton("up").setId(2).setPosition(80,60).setSize(20,20)
up.setImage(upArrw)
dArrw = drawDownArrow()
dwn = cp5.addButton("dwn").setId(1).setPosition(80,80).setSize(20,20)
dwn.setImage(dArrw)
get_surface().set_always_on_top(True)
print("Py5 Hello World.")
def mouse_pressed(e):
if(up.isPressed()):
print("up arrow was pressed.")
if(dwn.isPressed()):
print("down arrow was pressed.") |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
This may be the solution; works like a charm from setup() but fails when called by a button event handler. # Uses Imported mode for py5
from java.awt import *
from javax.swing import *
import subprocess
import os
import io
_wndW = 750
_wndH = 200
def stopBtnAction(event):
global process
process.terminate()
def stopBtn():
stopBtn = JButton("Stop")
stopBtn.setBounds(30, 10, 70, 24)
frame.add(stopBtn)
stopBtn.addActionListener(stopBtnAction)
stopBtn.repaint()
def logArea_scroll():
global logScrlPane,logArea
logArea = JTextArea()
logScrlPane = JScrollPane(logArea)
logScrlPane.setBounds(20,40,_wndW-40,_wndH - 60)
logArea.setEditable(True)
logArea.setLineWrap(True)
logArea.setWrapStyleWord(True)
frame.add(logScrlPane)
logArea.repaint()
def settings():
size(_wndW,_wndH)
def setup():
global process,counter,frame
window_title("subprocess demo")
wnd = get_surface()
get_surface().set_always_on_top(True)
canvas = wnd.get_native()
frame = canvas.getFrame()
wnd.set_title('Subprocess Demo')
frame.remove(canvas)
stopBtn()
logArea_scroll()
# <_io.BufferedReader name=93> returns bytes
# <_io.TextIOWrapper name=90 encoding='UTF-8'> returns string
# text=True causes return to be a string
cmdStr = "/opt/miniconda3/bin/py5-run-sketch"
filePath = "/Users/xxxx/py5_code/Arrows2.py"
process = subprocess.Popen([cmdStr,filePath], stdout=subprocess.PIPE, text=True)
while True:
outStr = process.stdout.readline()
logArea.append(outStr)
if not outStr:
break
print(outStr.strip())
process.wait() # Wait for the process to complete |
Beta Was this translation helpful? Give feedback.
-
This should be the solution. Launch_thread is your friend. The runProcess will work correctly if run on a separate thread: # Uses Imported mode for py5
from java.awt import *
from javax.swing import *
import threading
import subprocess
import os
import io
_wndW = 750
_wndH = 200
def runProcess():
global logArea,process,frame,wnd
cmdStr = "/opt/miniconda3/bin/py5-run-sketch"
filePath = "/Users/xxxx/py5_code/Arrows2.py"
process = subprocess.Popen([cmdStr,filePath], stdout=subprocess.PIPE, text=True)
while True:
outStr = process.stdout.readline()
logArea.append(outStr)
if not outStr:
break
print(outStr.strip())
# process.wait() # Wait for the process to complete
def runBtnAction(event):
launch_thread(runProcess,'runProcess')
def runBtn():
global frame
runBtn = JButton("Run")
runBtn.setBounds(30, 10, 70, 24)
frame.add(runBtn)
runBtn.addActionListener(runBtnAction)
runBtn.repaint()
def stopBtnAction(event):
global process
stop_thread(runProcess)
process.terminate()
def stopBtn():
global frame
stopBtn = JButton("Stop")
stopBtn.setBounds(130, 10, 70, 24)
frame.add(stopBtn)
stopBtn.addActionListener(stopBtnAction)
stopBtn.repaint()
def logArea_scroll():
global frame,logScrlPane,logArea
logArea = JTextArea()
logScrlPane = JScrollPane(logArea)
logScrlPane.setBounds(20,40,_wndW-40,_wndH - 60)
logArea.setEditable(False)
logArea.setLineWrap(True)
logArea.setWrapStyleWord(True)
frame.add(logScrlPane)
logArea.repaint()
def settings():
size(_wndW,_wndH)
def setup():
global process,counter,frame,wnd
window_title("subprocess demo")
wnd = get_surface()
get_surface().set_always_on_top(True)
canvas = wnd.get_native()
frame = canvas.getFrame()
wnd.set_title('Subprocess Demo')
frame.remove(canvas)
runBtn()
stopBtn()
logArea_scroll()
frame.setVisible(True) |
Beta Was this translation helpful? Give feedback.
This should be the solution. Launch_thread is your friend. The runProcess will work correctly if run on a separate thread: