Why is button not disabled? How to keep UI responsive?

Hello,

On Windows + Python3, I wrote a script to add several URLs to a listbox, and then call a CLI application to download each.

I have a couple of issues:

  1. Disabling a button works in basic code, but not when running the CLI right after, even when adding .Update() in between. Why is that?

  2. How to keep the GUI responsive while running the CLI app?

class ListBoxFrame(wx.Frame):
	def __init__(self, *args, **kwargs):  
		...
		self.add_btn = wx.Button(panel, -1, "Add URL")
		self.add_btn.Bind(wx.EVT_BUTTON, self.OnAddButtonClick)

		self.dload_btn = wx.Button(panel, -1, "Downlaod")
		self.dload_btn.Bind(wx.EVT_BUTTON, self.OnDloadButtonClick)
		...

	def OnDloadButtonClick(self,event):
		#TODO why isn't add_btn also disabled when launching CLI?
		self.add_btn.Disable()
		self.dload_btn.Disable()
		self.Update() #no change; Not enough time to refresh before running CLI app?
		
		for i in range(self.lb1.GetCount()):
			my_command = fr'{CMD} {URL}'
			p = subprocess.Popen(my_command, stdout=subprocess.PIPE, text=True)
			while (line := p.stdout.readline()) != "":
				self.statusbar.SetStatusText(line)
				self.Update()
			output = f"End of output.  Return code: {p.wait()}"
			print(output)
			self.statusbar.SetStatusText(output)
			self.Update()

		self.add_btn.Enable()
		self.dload_btn.Enable()

Thank you.

try adding wx.Yield() when you want a GUI update, but before your method returns i.e. instead of wx.Update() or possibly just after.

Thanks for the tip. I replaced all the self.Update() with wx.Yield(): I can move the window, but the UI is still frozen with the title saying “… not responding”.

def OnDloadButtonClick(self,event):
	self.add_btn.Disable()
	self.dload_btn.Disable()
	#self.Update() #no change
	wx.Yield()
	
	#TODO refresh UI
	for i in range(self.lb1.GetCount()):
		URL = self.lb1.GetString(i)
		self.statusbar.SetStatusText(URL)
		my_command = fr'{CMD} {URL}'
		p = subprocess.Popen(my_command, stdout=subprocess.PIPE, text=True)
		while (line := p.stdout.readline()) != "":
			self.statusbar.SetStatusText(line)
			#self.Update()
			wx.Yield()
		output = f"End of output.  Return code: {p.wait()}"
		self.statusbar.SetStatusText(output)
		#self.Update()
		wx.Yield()

	self.add_btn.Enable()
	self.dload_btn.Enable()

You are probably going to have to run the CLI task in a separate thread or subprocess. If the main/GUI thread is running that task, the GUI will be unresponsive while it is running.

1 Like

Thanks. I’ll read up on how to launch a thread from a wxPython script.