Hide CLI while grabbing its stdout?

Hello,

I use a wxPython GUI to run a CLI application. It’s just a listbox and a button to trigger the action.

Is it possible to hide the CLI console while still being able to grab its stdout output and display it in the GUI?

Thank you.

import wx, subprocess
 
CMD = fr'C:\blah.exe'
 
class ListBoxFrame(wx.Frame):
    def __init__(self, *args, **kwargs):  
        super().__init__(None, -1,title='Bulk Download')
         
        panel = wx.Panel(self, -1)
 
        sizer = wx.BoxSizer(wx.VERTICAL)
 
        self.lb1 = wx.ListBox(panel, -1, style=(wx.LB_SINGLE | wx.LB_ALWAYS_SB))
        sizer.Add(self.lb1,1, wx.ALL | wx.EXPAND ,5)
 
        self.dload_btn = wx.Button(panel, -1, "Download")
        sizer.Add(self.dload_btn,0, wx.EXPAND)
        self.dload_btn.Bind(wx.EVT_BUTTON, self.OnDloadButtonClick)
 
        panel.SetSizer(sizer)
 
    def OnDloadButtonClick(self,event):
        for i in range(self.lb1.GetCount()):
            URL = self.lb1.GetString(i)
            my_command = fr'{CMD} {URL}'
            p = subprocess.Popen(my_command, stdout=subprocess.PIPE, text=True)
            while (line := p.stdout.readline()) != "":
                self.statusbar.SetStatusText(line)
                wx.Yield()
        self.statusbar.SetStatusText("Done.")
 
app = wx.App()
ListBoxFrame().Show()
app.MainLoop()

This appears to be Windows specific as it doesn’t happen on linux.

I found several references to hiding the console on Windows when using Popen(). e.g. https://stackoverflow.com/questions/1813872/running-a-process-in-pythonw-with-popen-without-a-console

That thread was started back in the days of Python 2, so some of the answers may not work in Python 3. I can’t test that as I don’t use Windows. Also, beware of the security risks when using shell=True.

Side note: you omitted the call to self.CreateStatusBar() in your example.

Thanks. It seem impossible to run a CLI with no console and still get its output so it can be displayed in the GUI.

With the extension as .pyw, it still displays a console.

And with this code, I get no download:

import sys,os, wx, pyperclip, re
#import subprocess
from subprocess import Popen, PIPE, CREATE_NO_WINDOW

CMD = fr'C:\blah.exe"
class ListBoxFrame(wx.Frame):
	def __init__(self, *args, **kwargs):  
		super().__init__(None, -1,title='Bulk Youtube Download')

		self.statusbar = self.CreateStatusBar()

		panel = wx.Panel(self, -1)

		sizer = wx.BoxSizer(wx.VERTICAL)

		self.lb1 = wx.ListBox(panel, -1, style=(wx.LB_SINGLE | wx.LB_ALWAYS_SB))
		sizer.Add(self.lb1,1, wx.ALL | wx.EXPAND ,5)

		self.add_btn = wx.Button(panel, -1, "Add URL")
		sizer.Add(self.add_btn,0, wx.EXPAND)
		self.add_btn.Bind(wx.EVT_BUTTON, self.OnAddButtonClick)

		self.dload_btn = wx.Button(panel, -1, "Download")
		sizer.Add(self.dload_btn,0, wx.EXPAND)
		self.dload_btn.Bind(wx.EVT_BUTTON, self.OnDloadButtonClick)

		panel.SetSizer(sizer)

	def OnAddButtonClick(self,event):
		URL = pyperclip.paste()
		self.lb1.Append(URL)

	def OnDloadButtonClick(self,event):
		self.add_btn.Disable()
		self.dload_btn.Disable()
		wx.Yield()
		
		for i in range(self.lb1.GetCount()):
			URL = self.lb1.GetString(i)
			print(f"Starting {URL}") #not shown
			self.statusbar.SetStatusText(URL)
			my_command = fr'{CMD} {URL}'

			#OK p = subprocess.Popen(my_command, stdout=subprocess.PIPE, text=True)

			#BAD p = subprocess.Popen(my_command, stdout = subprocess.PIPE, creationflags = subprocess.CREATE_NO_WINDOW)
			#wx._core.wxAssertionError: C++ assertion ""(unsigned)number < m_panes.size()"" failed at ..\..\src\common\statbar.cpp(259) in wxStatusBarBase::SetStatusText(): invalid status bar field index"""
			
			#TRY from subprocess import Popen, PIPE, CREATE_NO_WINDOW
			#+
			p = subprocess.Popen(my_command)
			while (line := p.stdout.readline()) != "":
				self.statusbar.SetStatusText(line)
				wx.Yield()
			output = f"End of output.  Return code: {p.wait()}"
			print(output)
			self.statusbar.SetStatusText(output)
			wx.Yield()

		self.add_btn.Enable()
		self.dload_btn.Enable()
		self.statusbar.SetStatusText("Done.")

app = wx.App()
ListBoxFrame().Show()
app.MainLoop()

Didn’t try it with python, but there was an API call under windowses (I think shellExecute), where you could pass ‘HID’ as one of parameters, so while you’d still have a console window, it wouldn’t show.

Don’t know how would that translate into the .Popen() call, and does it use the same call in the first place.

Thanks. I’ll let it on the backburner since no solution worked so far.

#script doesn't run: stdout can't be read if terminal hidden?
p = subprocess.Popen(my_command, stdout=subprocess.PIPE, text=True, creationflags = CREATE_NO_WINDOW)
while (line := p.stdout.readline()) != "":
	self.statusbar.SetStatusText(line)
output = f"End of output.  Return code: {p.wait()}"