Re: Gnome "Quit" menu is like clicking a button in a wxPython modal dialog

Matthias Brennwald <[email protected]> Sun, 13 Dec 2020 22:59:07 +0100
Newsgroups gmane.comp.python.wxpython
Message-ID <[email protected]>

On Sun, Dec 13, 2020 at 17:05, Dietmar Schwertberger 
<[email protected]> wrote:

> A working minimal code sample is always a good idea. Your previous 
> sample was too minimal.

Alright, I took the code of my real-world application and stripped it 
down in order to better illustrate my situation (see the attached 
demo). I am still a noob when it comes to catching and handling events, 
so it would be great if someone could guide me to fix this demo code 
such that it behaves as intended (see below).


My demo program consists of three sequential steps (mostly implemended 
as mockups, without any real functionality):

A. Configuration: load some configuration parameters from a file, then 
connect to a measurement instrument, and check if the measurement 
instrument is alright.
Then the program shows Show a MessageDialog saying

B. Warmup: run a "warm up" procedure for the measurement instrument to 
make sure the instrument is ready to take good measurements.

C. The user may now control the measurement in order to take 
measurements.

Steps A and B are executed in separate threads in order to avoid 
blocking the GUI. At the end of (A) and (B), a MessageDialog is shown 
to inform the user about the progress, and to ask the user how to 
proceed.

If you run the demo code, the Configuration step (thread of step A) 
will run for a few seconds and then the program will show a first 
MessageDialog to ask if the Warmup step (thread of step B) should run. 
This is a "yes/no" MessageDialog. If at this point the user hits the 
"Quit" menu, things go wrong:
- The MessageDialog gets closed with a "yes" answer, and the program 
starts running the Warmup step (thread B), although the user did not 
give a "yes" or "no" answer.
- The intended behaviour would be to keep the "yes/no" MessageDialog 
until the user answers the question by choosing either "yes" or "no". 
The wx.EVT_CLOSE event should be discarded.

How can I get this to work as intended?

-- 
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion on the web visit https://groups.google.com/d/msgid/wxpython-users/J2TALQ.ZUQEM7WRI7X91%40gmail.com.
demo (text/x-python3, 4.5 KB)
#!/usr/bin/env python3

import wx
from threading import Thread
import time

class my_app(wx.App):

	def __init__(self):
		
		# wx.App init:
		wx.App.__init__(self)
				
		# Startup  thread (load instrument config, connect to instrument):
		self.startup_thread = startup_thread(self)

		# Warmup  thread (instrument warmup):
		self.warmup_thread = warmup_thread(self)

		# Main GUI frame:
		self.frame_main = frame_main(self)

	def startup(self):
		# start running, load instrument configuration:
		self.startup_thread.start()

	def warmup(self):
		# instrument warmup (ask user if warmup needed first):
		
		dlg = wx.MessageDialog(self.frame_main, "The instrument has been configured successfully. Do you want to run the warmup procedure?", "Run Warmup?", wx.YES_NO | wx.ICON_QUESTION )
		result = dlg.ShowModal()
		dlg.Destroy()
		
		if result == wx.ID_NO:
			# user skipped warmup:
			print('*** User skipped warmup!')
			dlg=wx.MessageDialog(self.frame_main, "You skipped warmup, lets hope you know what you are doing!", "Be careful!", wx.OK|wx.ICON_WARNING)
			dlg.ShowModal()
			self.frame_main.enable_measurement()
		else:
			print('*** Starting warmup thread.')
			self.warmup_thread.start()
			
	def exit(self):
		# stop all active threads and destroy frames:
		self.startup_thread.stop()
		self.warmup_thread.stop()
		while self.startup_thread.is_active():
			time.sleep(0.1)
		while self.warmup_thread.is_active():
			time.sleep(0.1)
		wx.CallAfter(self.frame_main.Destroy)

			

###################################################################


class frame_main(wx.Frame):

	def __init__(self, app):
		
		wx.Frame.__init__(self, None, title="Instrument Control", size=(250,200), style=wx.DEFAULT_FRAME_STYLE)

		self._app = app

		self.Bind(wx.EVT_CLOSE, self.on_close_event)
				
		# Add a button and stuff:
		self.panel = wx.Panel(self, wx.ID_ANY)
		sizer = wx.BoxSizer(wx.VERTICAL)
		self.button = wx.Button(self.panel, wx.ID_ANY, "Start measurement")
		self.button.Disable() # start with the button disabled
		sizer.Add(self.button, 0, wx.ALL, 4)
		self.panel.SetSizer(sizer)
		self.Layout()
		self.Bind(wx.EVT_BUTTON, self.on_start_measurement, self.button)
		
		self.Show()	
	
	def enable_measurement(self):
		self._app.frame_main.button.Enable()
		
	def on_start_measurement(self, event):
		print('*** on_start_measurement')
	
	def on_close_event(self, event):
		print('*** on_close_event')
		dlg = wx.MessageDialog(self, "Do you really want to close this application?", "Confirm Exit", wx.OK|wx.CANCEL|wx.ICON_QUESTION)
		result = dlg.ShowModal()
		dlg.Destroy()
		if result == wx.ID_OK:			
			self.exit()
			
	def exit (self):
		wx.CallAfter(self._app.exit)
			

###################################################################


# Startup thread:
class startup_thread(Thread):
	# load instrument configuration, and set up instrument
	
	def __init__(self, app):
		Thread.__init__(self)
		self._app = app
		self._stop = False
		self._active = False
		
	def run(self):
		self._active = True
		msg = [ 'Loading instrument configuration from file...', 'Connecting to instrument...', 'Checking if instrument is alright...', 'Getting serial number...', 'Checking instrument status...' 'Turning on the instrument...' ]
		for m in msg:
			if self._stop:
				break
			print('Thread A: ' + m)
			time.sleep(1)
		if not self._stop:
			print('Thread A: Instrument configuration completed!')
			wx.CallAfter(self._app.warmup)
		self._active = False
							
	def stop(self):
		if self._active:
			print('*** Stopping startup_thread')
			self._stop = True
		
	def is_active(self):
		return self._active


###################################################################


# Warmup thread:
class warmup_thread(Thread):
	# instrument warmup
	
	def __init__(self, app):
		Thread.__init__(self)
		self._app = app
		self._stop = False
		self._active = False
		
	def run(self):
		self._active = True
		for k in range(10):
			if self._stop:
				break
			print('Thread B: Instrument warming up...')
			time.sleep(1)
		if not self._stop:
			print('Thread B: Instrument warmup done!')
			wx.CallAfter(self._app.frame_main.enable_measurement)
		self._active = False
							
	def stop(self):
		if self._active:
			print('*** Stopping warmup thread')
			self._stop = True
		
	def is_active(self):
		return self._active


###################################################################


# Main:

# Init the (wx)App:
app = my_app() # init the app

# Startup process:
app.startup() # start background thread to load instrument config, bring up instrument, and set up GUI accordingly
	
# Main event loop:
app.MainLoop()