Example #1
0
class PGGuiSimuPop( pgg.PGGuiApp ):

	'''
	Subclass of PGGuiApp builds a gui,
	creates PGInputSimuPop and PGOutputSimuPop
	objects, then on user command runs a simulation 
	(one or more replicates) based on the parameters 
	supplied by the Input object, and to output files 
	as named by the basename in the PGOutputSimuPop object.  

	Note that for this class I abandoned my intended original 
	organization of the functionality for all  PGGuiApp objects.
	All subclasses of PGGUIApp objects were to contain an 
	AGPOperation object, in this case aPGOpSimuPop object, 
	which contains the code that actually uses Tiago Antao's 
	code to setup and run a SimuPOP session.  However, in 
	implenting replicate runs I encountered errors when
	trying to target a class method when spawning python
	multiprocessing.process objects from this object.  Further,
	I found non-independant instantiation of simupop populations 
	even when instantiating new PGOpSimuPop objects constructed
	in separate python multiprocessing.process instances.	
	Thus, parallel processing of simulation replicates required 
	the use of separate python instances for each replicate. 
	Thus currently (Wed Jul 27 18:35:05 MDT 2016),
	instead of manipulating its own PGOpSimuPop instances,
	this class depends on code in the pgutilities.py
	module, which has a def that makes a system call to 
	instantiate a new python interpreter, and call a script,
	do_sim_replicate.py, which then calls another def in 
	pgutilities.py, which builds and runs a PGOpSimuPop object.
	
	'''

	#possible states of the gui:
	RUN_NOT_READY=0
	RUN_READY=1
	RUN_IN_PROGRESS=2

	MAX_CHARS_BASENAME=18

	def __init__( self,  o_parent,  s_param_names_file=None, 
							s_life_table_file_glob="resources/*life.table.info",
							s_name="simupop_gui",
							i_total_processes_for_sims=1 ):
		'''
		param s_param_names_file is the file that contains short and long param
		names, used to make the PGParamSet object  member of the PGInputSimuPop 
		object
		'''

		#note this call to super does not work -- a look as stackoverflow
		#chatter about this indicates that because PGGUIApp inherits Frame,
		#that Frame must be an "old-style" class (does not inherit from "object")
		#--rem out super( PGGuiSimuPop, self ).__init__( o_parent )
		#instead of call to "super":
		pgg.PGGuiApp.__init__( self, s_name, o_parent )
		self.__config_file=StringVar()
		self.__output_directory=StringVar()
		self.__life_table_files=[]
		self.__get_life_table_file_list( s_life_table_file_glob )

		#we use the tkinter traceable variable
		#so that, when loaded into an entry box
		#we can auto update the value as it is 
		#user-edited:
		self.__output_directory.set( self.__get_default_output_directory() )

		self.output_base_name=self.__get_default_output_base_name() 

		#the PGOpSimuPop object:
		self.__param_names_file=s_param_names_file

		#input object used to make the
		#operation object type PGOpSimuPop
		self.__input=None
		#output object used to
		#name and create the simulation
		#output files
		self.__output=None

		#when commanded to run the simulation(s)
		#a temporary configuration file
		#is created and used by each process
		#that is simulating.  We then want to
		#delete the file when the gui had 
		#ascertained that the sim is or sims
		#are done, so we need instance-level
		#access to the file name
		self.__temp_config_file_for_running_replicates=None

		#simulations performed on separate
		#process -- this ref to it will
		#enable checking whether in progress
		#or finished.  See def runSimulation:
		self.__op_process=None
		#we set this before spawning __op_process,
		#so that on cancel-sim, we can
		#notify op_process and it can 
		#clean up its subprocesses and 
		#output:
		self.__sim_multi_process_event=None

		#we have a default output name,
		#so we can init the output
		#immediately
		self.__setup_output()



		self.__simulation_is_in_progress=False

		self.__total_processes_for_sims=i_total_processes_for_sims

		#we hold references to all subframes
		#except the run-button subframe,
		#so that we can enable/disable
		#according to the simulation-run state
		#(not yet implemented, 2016_08_05

		self.__category_frames=None

		self.__run_state_message=""
		self.__init_interface()
		self.__set_controls_by_run_state( self.__get_run_state() )
	
		return
	#end __init__

	def __init_run_sub_subframe( self, b_force_disable = False ):
		'''
		Setup the run button and the processes text box.
		As of 2016_08_31, these chores are still done in 
		def __init_interface.  Need to implement this to
		simplify def __init_interface,
		'''
		pass
		return
	#end of __init_run_sub_subframe

			
	def __init_interface( self, b_force_disable = False ):
		'''
		param b_force_disable, if set to true,
			will set all the KeyValueFrame text entry
			boxes do state "disabled."  This is useful
			to show user when the interface is busy
			computing a simulation, and keeps user
			from editing values when the sim is 
			being run (though, besides any user
			misperceptions, such editign would not
			affect the current sim, but rather would
			be applied to any subsequent run.)

		'''
		
		ENTRY_WIDTH=50
		LABEL_WIDTH=20
		LOCATIONS_FRAME_PADDING=30
		LOCATIONS_FRAME_LABEL="Load/Run"
		LOCATIONS_FRAME_STYLE="groove"
		RUNBUTTON_PADDING=07	

		o_file_locations_subframe=LabelFrame( self,
				padding=LOCATIONS_FRAME_PADDING,
				relief=LOCATIONS_FRAME_STYLE,
				text=LOCATIONS_FRAME_LABEL )

		i_row=0

		o_run_sub_subframe=Frame( o_file_locations_subframe )

		self.__run_button=Button( o_run_sub_subframe, command=self.__on_click_run_or_cancel_simulation_button )
		
		i_tot_procs=pgut.get_cpu_count()

		o_tot_process_validator=FloatIntStringParamValidity( \
					"proccess total",
					int, self.__total_processes_for_sims, 
					1, i_tot_procs )

		o_tot_process_kv=KeyValFrame( "Processes", 
						self.__total_processes_for_sims,
						o_run_sub_subframe,
						o_associated_attribute_object=self,
						s_associated_attribute="_PGGuiSimuPop" \
									+ "__total_processes_for_sims",
						s_entry_justify='left',
						s_label_justify='left',
						s_button_text="Select",
						b_force_disable=b_force_disable,
						o_validity_tester= o_tot_process_validator,
						s_tooltip = "Simulation can use one process per replicate, " \
								+ "but set to no more than the total number " \
								+ "of processors in your computer." )

		o_tot_process_kv.grid( row=i_row, column=0, sticky=( NW ) )

		self.__run_button.grid( row=i_row, column=1, sticky=( NW ), padx=RUNBUTTON_PADDING )

		self.__run_state_label=Label( o_run_sub_subframe, text=self.__run_state_message )

		self.__run_state_label.grid( row=i_row, column=2, sticky=( SW ) )

		o_run_sub_subframe.grid( row=i_row, sticky=( NW ) )

		i_row += 1
		
		s_curr_config_file=self.__config_file.get()

		v_config_file_val="None" if s_curr_config_file == "" else s_curr_config_file

		o_config_kv=KeyValFrame( "Load Configuration File:", 
						v_config_file_val,
						o_file_locations_subframe,
						i_entrywidth=ENTRY_WIDTH,
						i_labelwidth=LABEL_WIDTH,
						b_is_enabled=False,
						s_entry_justify='left',
						s_label_justify='left',
						s_button_text="Select",
						def_button_command=self.load_config_file,
						b_force_disable=b_force_disable )

		o_config_kv.grid( row=i_row, sticky=( NW ) )

		i_row+=1

		o_outdir_kv=KeyValFrame( "Select Output directory", 
					self.__output_directory.get(), 
					o_file_locations_subframe,
					i_entrywidth=ENTRY_WIDTH,
					i_labelwidth=LABEL_WIDTH,
					b_is_enabled=False,
					s_entry_justify='left',
					s_label_justify='left', 
					s_button_text="Select",
					def_button_command=self.select_output_directory,
					b_force_disable=b_force_disable )

		o_outdir_kv.grid( row= i_row, sticky=( NW ) )

		i_row+=1
	
		o_basename_validity_tester=FloatIntStringParamValidity( "output_base", 
				str, self.output_base_name, 1, 
				PGGuiSimuPop.MAX_CHARS_BASENAME )
		
		self.__outbase_kv=KeyValFrame( s_name="Output files base name: ", 
					v_value=self.output_base_name, 
					o_master=o_file_locations_subframe, 
					o_associated_attribute_object=self,
					s_associated_attribute="output_base_name",
					def_entry_change_command=self.__setup_output,
					i_entrywidth=ENTRY_WIDTH,
					i_labelwidth=LABEL_WIDTH,
					s_entry_justify='left',
					s_label_justify='left',
					o_validity_tester=o_basename_validity_tester,
					b_force_disable=b_force_disable ) 

		self.__outbase_kv.grid( row=i_row, sticky=( NW ) )

		o_file_locations_subframe.grid( row=0, sticky=(NW) )
		o_file_locations_subframe.grid_columnconfigure( 0, weight=1 )
		o_file_locations_subframe.grid_rowconfigure( 0, weight=1 )

		self.grid_columnconfigure( 0, weight=1 )
		self.grid_rowconfigure( 0, weight=1 )

		return
	#end __init_interface

	def __clear_grid_below_row(self, o_frame, i_row ):

		for o_item in o_frame.grid_slaves():
			if int( o_item.grid_info()["row"]) > i_row:
				o_item.destroy()
			#end if row below i_row
		#end for each grid item
		return
	#end __clear_grid_below_row

	def __make_category_subframes( self, set_categories ): 

		PADDING_FOR_CATEGORY_FRAME=8
		STYLE_FOR_CATEGORY_FRAME="groove"
		BORDERWIDTH_FOR_CATEGORY_FRAME=2

		do_category_frames={}

		for s_tag in set_categories:
			if s_tag=="suppress":
				continue
			#end if param is not to be shown in the interface

			#param-name tags have the category text followed by semicolon, then
			#an int, 1,2,... giving its ordinal as to its order of appearance on
			#the interface, top to bottom:
			ls_tag_fields=s_tag.split( ";" )
			s_frame_label=ls_tag_fields[ 0 ]
			do_category_frames[ s_tag ] = LabelFrame( self, 
					padding=PADDING_FOR_CATEGORY_FRAME,
					relief=STYLE_FOR_CATEGORY_FRAME,
					borderwidth=BORDERWIDTH_FOR_CATEGORY_FRAME,
					text=s_frame_label )
		#end for each tag, make a corresponding frame

		#add an "other" frame for params without
		#categories, either missing from a params file
		#or not categoriezed in the file:
		do_category_frames[ "none" ] = LabelFrame( self, 
				padding=PADDING_FOR_CATEGORY_FRAME, 
				relief=STYLE_FOR_CATEGORY_FRAME,
				borderwidth=BORDERWIDTH_FOR_CATEGORY_FRAME,
				text="Other" )
		
		return do_category_frames
	#end __make_category_subframes

	def __place_category_frames( self, do_category_frames ):
		#the file locations labelframe has row 0, 
		#(see __init_interface), so:
		FIRST_ROW_NUMBER_FOR_CATEGORY_FRAMES=1
		GRID_SPACE_VERTICAL=10
	
		di_order_num_by_section_name={}

		#reduncancy in these assignments
		#is ignored, we simply reassign
		#the number each time we encounter
		#the name -- as the two should always
		#be identical
		for s_tag in self.__input.param_names.tags:
			s_section_name=self.__input.param_names.getConfigSectionNameFromTag( s_tag )
			s_order_number=self.__input.param_names.getSectionOrderFromTag( s_tag )
			di_order_num_by_section_name[ s_section_name ]=int( s_order_number )
		#end for each tag
		
		for s_key in do_category_frames:
			o_frame=do_category_frames[ s_key ]
			if s_key=="none":
				i_frame_order_number=len( do_category_frames )
			else:
				i_frame_order_number=di_order_num_by_section_name[ s_key ]
			#end if categore is "none" then place in last row

			i_cat_frame_row=FIRST_ROW_NUMBER_FOR_CATEGORY_FRAMES + ( i_frame_order_number - 1 )
			o_frame.grid( row=i_cat_frame_row, sticky=(NW), pady=GRID_SPACE_VERTICAL )
			o_frame.grid_columnconfigure( 0, weight=1 )
			o_frame.grid_rowconfigure( 0, weight=1 )
			i_cat_frame_row+=1
		#end for each catgory frame
	#end __place_category_frames
			

	def __load_values_into_interface( self, b_force_disable=False ):
		'''
		in isolating the attributes
		that are strictly model params
		(and not defs or other non-param 
		attributes) I found help at:
		http://stackoverflow.com/questions/9058305/getting-attributes-of-a-class

		param b_force_disable, if True, will, for each entry box or radio button
		created by KeyValFrame or KeyCategoricalValueFrame,  override the defalut 
		enabled/disabled state (as set by their attribute "isenabled", so that 
		all are disabled.
		'''
		MAXLABELLEN=200
		WIDTHSMALL=21
		WIDTHBIG=21
		LENSMALL=20

		LABEL_WIDTH = [ WIDTHSMALL if i<LENSMALL else WIDTHBIG for i in range( MAXLABELLEN ) ] 
		
		#for parameters with this tag, we set enabled 
		#flag to false for the KeyValFrame entry box:
		CONFIG_INFO_TAG_KEYWORD="Configuration Info"

		i_row=0

		o_input=self.__input
		
		ls_input_params=[ s_param for s_param in dir( o_input ) if not( s_param.startswith( "_" ) )  ]

		ls_tags=o_input.param_names.tags

		ls_sections=[  o_input.param_names.getConfigSectionNameFromTag( \
											s_tag )  for s_tag in ls_tags ]

		#clear existing category frames (if any): 
		self.__clear_grid_below_row( self, 1 )

		#make frames, one for each category of parameter
		#(currently, "population", "configuration", "genome", "simulation"
		self.__category_frames=self.__make_category_subframes( \
				set( ls_sections ) )	

		for s_param in ls_input_params:
			
			PADPIX=0

			i_row+=1
			s_longname=None
			s_nametag=None
			s_tooltip=""
			i_len_labelname=None

			o_def_to_run_on_value_change=None

			if VERBOSE == True:
				o_def_to_run_on_value_change=self.__test_value
			#end if VERBOSE

			v_val=getattr( o_input, s_param )
			
			#we don't display this if its a def
			#or a function (and so not a parameter)
			if inspect.isroutine( v_val ):
				continue
			#end if def or function

			if  type( v_val ) == str:
				if v_val.startswith( "<bound method" ):
					continue
				#end if method, skip
			#end if the value is a string

			if o_input.param_names is not None:
				#returns None if this param has no longname,
				#and the KeyValFrame will ignore s_label_name if
				#its value is None, using instead the param name 
				#as given by s_param
				s_longname=o_input.param_names.longname( s_param )
				s_nametag=o_input.param_names.tag( s_param )
				s_tooltip=o_input.param_names.getToolTipForParam( s_param )
				s_section_name= \
						o_input.param_names.getConfigSectionNameFromTag( s_nametag )
			#end if we have a set of param names

			#if not in the param names file, we don't want to suppress it,
			#If it is in the param names we don't suppress as long as the
			#param is not tagged as "suppress"
			if s_nametag is None or s_section_name != "suppress":

				o_frame_for_this_param=None

				if s_nametag is None or s_section_name not in self.__category_frames:
					o_frame_for_this_param=self.__category_frames[ "none" ]
				else:
					o_frame_for_this_param=self.__category_frames[ s_section_name ]
				#end if no nametag or name tag not in frame categories

				i_len_labelname=len( s_longname ) if s_longname is not None else len( s_param )
				i_width_labelname=LABEL_WIDTH[ i_len_labelname ]

				b_allow_entry_change = True

				if s_nametag is not None:
					if CONFIG_INFO_TAG_KEYWORD in s_nametag:
						b_allow_entry_change=False
					#end if input param is type config info, disable
				#end if nametag exists

				#we send in the input object to the KeyValFrame (or KeyCategoricalValueFrame 
				#instance so it will be the object whose attribute (with name s_param)
				#is reset when user resets the value in the KeyValFrame:
				if type( v_val ) != bool:

					i_entry_width=len(v_val) if type( v_val ) == str else 7
					s_entry_justify='left' if type( v_val ) == str else 'right' 

					
					o_kv=KeyValFrame( s_param, v_val, o_frame_for_this_param, 
							o_associated_attribute_object=self.__input,
							s_associated_attribute=s_param,
							def_entry_change_command=self.__test_value,
							i_labelwidth=i_width_labelname,	
							s_label_name=s_longname,
							i_entrywidth = i_entry_width,
							s_entry_justify=s_entry_justify,
							b_is_enabled=b_allow_entry_change,
							b_force_disable=b_force_disable,
							s_tooltip=s_tooltip )
						
				else:

					#we construct a KeyCategoricalValueFrame
					#instance, and set the default button to
					#Yes (true) or No (false) according to the
					#current value:
					i_default_mode=1
					if v_val==False:
						i_default_mode = 2
					#end if

					o_kv=KeyCategoricalValueFrame( s_name=s_param, 
							lq_modes=[ ( "Yes", True ), ( "No", False ) ],
							i_default_mode_number=i_default_mode,
							o_value_type=bool,
							o_master=o_frame_for_this_param, 
							s_associated_attribute=s_param,
							o_associated_attribute_object=self.__input,
							def_on_button_change=self.__test_value,
							i_labelwidth=i_width_labelname,
							s_label_name=s_longname,
							b_buttons_in_a_row = True,
							b_is_enabled=b_allow_entry_change,
							b_force_disable=b_force_disable,
							s_tooltip=s_tooltip )

				o_kv.grid( row=i_row, sticky=(NW) )
			#end if param has non-boolean value else boolean
		#end for each input param

		self.__place_category_frames( self.__category_frames )

		#end for each param

		self.grid_columnconfigure( 0, weight=1 )
		self.grid_rowconfigure( 0, weight=1 )

		return
	#end __load_values_into_interface

	def load_config_file( self, event=None ):
		s_current_value=self.__config_file.get()
		s_config_file=tkfd.askopenfilename(  \
				title='Load a configuration file' )

		if pgut.dialog_returns_nothing( s_config_file ):
			return
		#end if no file selected, return

		self.__config_file.set(s_config_file)
		try:
			self.__setup_input()
		except Exception as oe:
			if s_config_file != INIT_ENTRY_CONFIG_FILE: 
				s_msg="Problem loading configuration.\n" \
						+ "File: " + str( s_config_file ) + "\n\n" \
						+ "Details:\n\n" \
						+ "Exception: " + str( oe ) + "."
				o_diag=PGGUIInfoMessage( self, s_msg )
			#end if entry not None
			return
		#end try ... except
		self.__init_interface()
		self.__load_values_into_interface()
		self.__set_controls_by_run_state( self.__get_run_state() )
		return
	#end load_config_file

	def __get_life_table_file_list( self, s_glob_expression ):
		self.__life_table_files = \
			pgut.get_list_files_and_dirs_from_glob( s_glob_expression )
		return
	#end def __get_life_table_file_list

	def select_output_directory( self, event=None ):

		s_outputdir=tkfd.askdirectory( \
				title='Select a directory for file output' )
		
		if pgut.dialog_returns_nothing( s_outputdir ):
			return
		#end if no directory selected, return

		self.__output_directory.set( s_outputdir )
		self.__init_interface()
		self.__set_controls_by_run_state( self.__get_run_state() )
		return
	#end select_output_directory

	def __setup_input( self ):
		o_model_resources=None
		o_param_names=None
		o_pgin=None

		if len( self.__life_table_files ) > 0:
			o_model_resources=pgsr.PGSimuPopResources( self.__life_table_files )
		#end if we have a file list

		if self.__param_names_file is not None:
			o_param_names=pgps.PGParamSet( self.__param_names_file )
		#end if param_names_file is set

		try:
			#note that we can pass None value n for o_model_resources,
			#and as long as the config file has evaluatable values for
			#all of its options, we still get a valid PGInputSimuPop object:
			o_pgin=pgin.PGInputSimuPop( self.__config_file.get(), o_model_resources, o_param_names ) 
			o_pgin.makeInputConfig()
		except Exception as exc:
			self.__config_file.set("")
			self.__input=None
			self.__clear_grid_below_row( self, 0 )
			self.__init_interface()
			self.__set_controls_by_run_state( self.__get_run_state() )
			raise exc
		#end try to make input object

		self.__input=o_pgin
		return

	#end setup_input

	def __setup_output( self ):
		s_dir_and_base=self.__output_directory.get() + "/" + self.output_base_name 
		o_pgout=pgout.PGOutputSimuPop(  s_dir_and_base )
		self.__output=o_pgout
		return
	#end setup_output

	def __validate_output_base_name( self ):

		MAX_CHARS_OUTPUT_FILE_BASENAME=18
		s_msg=""

		if ( len( self.__basename ) > MAX_CHARS_OUTPUT_FILE_BASENAME ):
				s_msg="Please limit the output file base name to " \
						+ str( MAX_CHARS_OUTPUT_FILE_BASENAME ) \
						+ " characters."
		#end if length exceeds maximum

		return s_msg	
	#end __validate_output_base_name

	def __test_value( self, v_keyval_frame_value_update=None ):
		'''
		for debugging
		'''
		if VERY_VERBOSE:

			ls_input_params=[ s_param for s_param in dir( self.__input ) 
									if not( s_param.startswith( "_" ) )  ]
			
			for s_param in ls_input_params:
				print s_param + "\t" + str( getattr( self.__input, s_param ) )
			#end for each param

		#end if VERBOSE			
	#end __test_value

	def __setup_op( self, v_keyval_frame_value_update=None ):
		self.__setup_output()
		return
	#end __make_op

	def __update_run_state_message( self ):
		MAX_DOTS=10

		if self.__run_state_message != "":
			i_len_msg=len( self.__run_state_message )
			
			i_len_dots_stripped=len ( \
					self.__run_state_message.rstrip( "." ) )

			i_curr_num_dots=i_len_msg - i_len_dots_stripped

			if i_curr_num_dots >= MAX_DOTS:
				self.__run_state_message = \
						self.__run_state_message.rstrip( "." ) + "."
			else:
				self.__run_state_message += "."
			#end if max dots reached or exceeded, else not
		return
	#end __update_run_state_message

	def __get_default_output_base_name( self ):

		'''
		Compacting default name, as of 2016_08_11,
		need to make sure the output base name is
		as small as possble, to keep the resulting
		genepop file name under 31 chars, as NeEstimation
		currently can't handle input file names >31 chars.
		'''
		s_mytime=pgut.get_date_time_string_dotted()	
		return "sim." + s_mytime.replace( ".", "" )
	#end __get_default_output_base_name

	def __get_default_output_directory( self ):
		return pgut.get_current_working_directory()
	#end __get_default_output_directory

	def __check_progress_operation_process( self ):
		if self.__op_process is not None:
			if self.__op_process.is_alive():
				if VERY_VERBOSE:
					print ( "checking and process found alive" )
				#endif very verbose, print

				self.__simulation_is_in_progress = True
				self.__update_run_state_message()
				self.__run_state_label.configure( text=self.__run_state_message  )
				self.after( 500, self.__check_progress_operation_process )
			else:
				if VERY_VERBOSE:
					print( "checking and process not None but not alive" )
					print( "value of temp config file attribute: " \
								+ str( self.__temp_config_file_for_running_replicates ) )
				#end if very verbose, print

				self.__simulation_is_in_progress = False

				#found in the neestimator gui that the op_process 
				#as object will persist long past finishing the estimation.  
				#In this case if the user closes the whole gui or the tab 
				#for this instance, then the cleanup will read the 
				#op_process as alive, and will remove output files, even 
				#though the process has finished.  While I did not see this
				#problem here, for the PGGuiSimuPop.op_process, 
				#I'm nonetheless setting the "dead"
				#process and its corresponding event to None:
				self.__op_process=None
				self.__sim_multi_process_event=None
				self.__run_state_message=""
				self.__init_interface( b_force_disable=False )
				self.__load_values_into_interface( b_force_disable=False )

				if VERY_VERBOSE:
					print ( "removing temporary config file" )
				#end if very verbose

				self.__remove_temporary_config_file()

			#end if process alive else not

			self.__set_controls_by_run_state( self.__get_run_state() )

		else:
			if VERY_VERBOSE:	
				print( "process found to be None" )
			#endif very verbose, pring

			self.__simulation_is_in_progress = False
			self.__run_state_message=""
			self.__init_interface( b_force_disable=False )
			self.__load_values_into_interface( b_force_disable=False )

			self.__set_controls_by_run_state( self.__get_run_state() )

		#end if process not None else None

		return
	#end __check_progress_operation_process

	def __remove_temporary_config_file( self ):
			if self.__temp_config_file_for_running_replicates is not None:
				if os.path.exists( self.__temp_config_file_for_running_replicates ):
					os.remove( self.__temp_config_file_for_running_replicates )
					self.__temp_config_file_for_running_replicates = None
				else:
					s_msg = "In PGGuiSimuPop instance, def __remove_temporary_config_file, " \
							+ "attribued __temp_config_file_for_running_replicates is not None, " \
							+ "but is not a valid, existing file. Current value: " \
							+ str( self.__temp_config_file_for_running_replicates ) + "." 
					raise Exception( s_msg )
				#end if path exits
			#end if config file attr is not none
	#end __remove_temporary_config_file

	def __cancel_simulation( self ):
		if self.__op_process is not None:
			if self.__sim_multi_process_event is not None:
				#signal op_process that we want to kill all the sim subprocesses:
				#(see def __do_operation)
				self.__sim_multi_process_event.set()
			else:
				self.__op_process.terminate()
		else:
			s_msg="Could not cancel the simulation.  " \
					+ "The simulation process and/or its communication event was not found." 
			sys.stderr.write( "Warning: " + s_msg + "\n" )
		#end if op_process is not None
		
		return
	#end __cancel_simulation

	def __on_click_run_or_cancel_simulation_button( self, event=None ):
		if self.__simulation_is_in_progress:
			o_mbox=PGGUIYesNoMessage( self , "Are you sure you want to cancel " \
					+ "the simulation and remove all output files?" )
			b_answer = o_mbox.value
			if b_answer:
				self.__cancel_simulation()
				self.__simulation_is_in_progress=False
				self.__set_controls_by_run_state( self.__get_run_state() )

			#end if yes
		else:
			self.runSimulation()
		#end if sim in progress else not
		return
	#end __on_click_run_or_cancel_simulation_button

	def __get_run_state( self ):
		if self.__input is not None \
				and self.__output is not None:
			if self.__simulation_is_in_progress:
				return PGGuiSimuPop.RUN_IN_PROGRESS
			else:
				return PGGuiSimuPop.RUN_READY
			#end if in progress else not
		else:
			return PGGuiSimuPop.RUN_NOT_READY
		#end if input and output are not None else one or both 
	#end __get_run_state

	def __set_controls_by_run_state( self, i_run_state ):

		if i_run_state==PGGuiSimuPop.RUN_NOT_READY:
			self.__run_button.config( text="Run Simulation", state="disabled" ) 
		elif i_run_state==PGGuiSimuPop.RUN_READY:
			self.__run_button.config( text="Run Simulation", state="enabled" )
		elif i_run_state==PGGuiSimuPop.RUN_IN_PROGRESS:
			self.__run_button.config( text="Cancel Simulation", state="enabled" )
		else:
			s_msg="In PGGuiSimuPop instance, __set_run_state: " \
					"unknown run state value: " + str( i_run_state )
			raise Exception( s_msg )
		#end if run state not ready, else ready, else in progress, else error
		
		return
	#end def __set_controls_by_run_state

	def __output_files_exist_with_current_output_basename(self):
		b_files_exist=False
		ls_files=pgut.get_list_files_and_dirs_from_glob( self.__output.basename + "*" )
		if len( ls_files ) != 0:
			b_files_exist=True
		#end if len not zero
		return b_files_exist
	#end __output_files_exist_with_current_output_basename

	def runSimulation( self ):
		if self.__output is None \
			or self.__input is None:
			s_msg="Simulation not ready to run.  Input or Output parameters " \
					+ "have not been set" 
			sys.stderr.write( s_msg + "\n" )
			PGGUIInfoMessage( self, s_msg )
			return	
		#end if output or input is None

		self.__setup_op()

		'''
		this check was added in case a user completed a simulation,
		then accidentally hit "run simulation" button again without
		changing anything in the interface, and then (very quickly) 
		hit the "cancel simulation" button -- initiating a file removal
		before the program has reached the point at which which the
		PGOutputSimuPop object could abort the run by throwing an 
		exception that the output files already exist.
		The following check is meant to halt the sim before any
		separate processes are even started (and so no multiprocessing
		event will cause cancel prompt and cleanup on "yes" ).
		'''
		if self.__output_files_exist_with_current_output_basename():
			s_msg="The program can't start the simulation. Output files " \
					+ "already exist with current " \
					+ "path and basename: " + self.__output.basename + ".  " \
					+ "\nTo start a new simulation please do one of the following:\n" \
					+ "1. Remove or rename the output files.\n" \
					+ "2. Rename the output files base name or output directory." 

			sys.stderr.write( s_msg + "\n" )
			
			PGGUIInfoMessage( self, s_msg )
			return
		#end if output files exist	

		#input file to reaplace the orig config file
		#as well as the attr/value dict originally passed to each 
		#process.  We need to set this temp file name attribute
		#in this instance before spawning a multiprocessing.process
		#in do_operation, else it will not appear during call backs
		#to __check_progress_operation_process, in which we delete
		#the temp file when sim processes are finished
		self.__temp_config_file_for_running_replicates=str( uuid.uuid4() ) 
		
		self.__input.writeInputParamsAsConfigFile( \
			self.__temp_config_file_for_running_replicates )

		#we pass a ref to this object to the target (def in pgutilities, do_operation_outside) 
		#for op_process, #Then, from this object instance can call "set()" on the event, 
		#after, for example #an __on_click_run_or_cancel_simulation_button->__cancel_simulation()
		#sequence, so that the event will to pass True and then the ref in
		#op_process will return True when the op_process interrog 
		#o_event.is_set()  and it can accordingly clean
		#up its collection of subprocess.Popen processes 
		self.__sim_multi_process_event=multiprocessing.Event()

		#our sim run(s) should not block the main gui, so we
		#run it(them) in a new python.multiprocessing.process:
		self.__op_process=multiprocessing.Process( target=pgut.do_operation_outside, 
					 args=( self.__sim_multi_process_event, 
						self.__input.reps, 
						self.__total_processes_for_sims,
						self.__temp_config_file_for_running_replicates,
						self.__life_table_files,
						self.__param_names_file,
						self.__output.basename ) ) 

		self.__op_process.start()

		self.__simulation_is_in_progress=True

		#Need some padding in front, or
		#it's too close to the run button:
		self.__run_state_message="  " + SIM_RUNNING_MSG

		self.__set_controls_by_run_state( self.__get_run_state() )

		self.__init_interface( b_force_disable=True )
		self.__load_values_into_interface( b_force_disable=True )

		self.after( 500, 
				self.__check_progress_operation_process )

		return
	#end runSimulation

	def __show_input( self ):

		o_sim_frame=self.master.children[ 'simulation_input_frame' ]

		for s_key in self.__input.__dict__:
			o_this_label = \
				Label( o_sim_frame, text=s_key)
			o_this_entry=Entry( o_sim_frame )
			o_this_entry.insert( 0, str(  self.__input.__dict__[ s_key ] ) )
			o_sim_frame.add( o_this_label )
			o_sim_frame.add( o_this_entry)
		#end for each key

		return
	#end show_input_config

	def cleanup( self ):
		self.__cancel_simulation()
		self.__remove_temporary_config_file()
Example #2
0
	def __load_values_into_interface( self, b_force_disable=False ):
		'''
		in isolating the attributes
		that are strictly model params
		(and not defs or other non-param 
		attributes) I found help at:
		http://stackoverflow.com/questions/9058305/getting-attributes-of-a-class

		param b_force_disable, if True, will, for each entry box or radio button
		created by KeyValFrame or KeyCategoricalValueFrame,  override the defalut 
		enabled/disabled state (as set by their attribute "isenabled", so that 
		all are disabled.
		'''
		MAXLABELLEN=200
		WIDTHSMALL=21
		WIDTHBIG=21
		LENSMALL=20

		LABEL_WIDTH = [ WIDTHSMALL if i<LENSMALL else WIDTHBIG for i in range( MAXLABELLEN ) ] 
		
		#for parameters with this tag, we set enabled 
		#flag to false for the KeyValFrame entry box:
		CONFIG_INFO_TAG_KEYWORD="Configuration Info"

		i_row=0

		o_input=self.__input
		
		ls_input_params=[ s_param for s_param in dir( o_input ) if not( s_param.startswith( "_" ) )  ]

		ls_tags=o_input.param_names.tags

		ls_sections=[  o_input.param_names.getConfigSectionNameFromTag( \
											s_tag )  for s_tag in ls_tags ]

		#clear existing category frames (if any): 
		self.__clear_grid_below_row( self, 1 )

		#make frames, one for each category of parameter
		#(currently, "population", "configuration", "genome", "simulation"
		self.__category_frames=self.__make_category_subframes( \
				set( ls_sections ) )	

		for s_param in ls_input_params:
			
			PADPIX=0

			i_row+=1
			s_longname=None
			s_nametag=None
			s_tooltip=""
			i_len_labelname=None

			o_def_to_run_on_value_change=None

			if VERBOSE == True:
				o_def_to_run_on_value_change=self.__test_value
			#end if VERBOSE

			v_val=getattr( o_input, s_param )
			
			#we don't display this if its a def
			#or a function (and so not a parameter)
			if inspect.isroutine( v_val ):
				continue
			#end if def or function

			if  type( v_val ) == str:
				if v_val.startswith( "<bound method" ):
					continue
				#end if method, skip
			#end if the value is a string

			if o_input.param_names is not None:
				#returns None if this param has no longname,
				#and the KeyValFrame will ignore s_label_name if
				#its value is None, using instead the param name 
				#as given by s_param
				s_longname=o_input.param_names.longname( s_param )
				s_nametag=o_input.param_names.tag( s_param )
				s_tooltip=o_input.param_names.getToolTipForParam( s_param )
				s_section_name= \
						o_input.param_names.getConfigSectionNameFromTag( s_nametag )
			#end if we have a set of param names

			#if not in the param names file, we don't want to suppress it,
			#If it is in the param names we don't suppress as long as the
			#param is not tagged as "suppress"
			if s_nametag is None or s_section_name != "suppress":

				o_frame_for_this_param=None

				if s_nametag is None or s_section_name not in self.__category_frames:
					o_frame_for_this_param=self.__category_frames[ "none" ]
				else:
					o_frame_for_this_param=self.__category_frames[ s_section_name ]
				#end if no nametag or name tag not in frame categories

				i_len_labelname=len( s_longname ) if s_longname is not None else len( s_param )
				i_width_labelname=LABEL_WIDTH[ i_len_labelname ]

				b_allow_entry_change = True

				if s_nametag is not None:
					if CONFIG_INFO_TAG_KEYWORD in s_nametag:
						b_allow_entry_change=False
					#end if input param is type config info, disable
				#end if nametag exists

				#we send in the input object to the KeyValFrame (or KeyCategoricalValueFrame 
				#instance so it will be the object whose attribute (with name s_param)
				#is reset when user resets the value in the KeyValFrame:
				if type( v_val ) != bool:

					i_entry_width=len(v_val) if type( v_val ) == str else 7
					s_entry_justify='left' if type( v_val ) == str else 'right' 

					
					o_kv=KeyValFrame( s_param, v_val, o_frame_for_this_param, 
							o_associated_attribute_object=self.__input,
							s_associated_attribute=s_param,
							def_entry_change_command=self.__test_value,
							i_labelwidth=i_width_labelname,	
							s_label_name=s_longname,
							i_entrywidth = i_entry_width,
							s_entry_justify=s_entry_justify,
							b_is_enabled=b_allow_entry_change,
							b_force_disable=b_force_disable,
							s_tooltip=s_tooltip )
						
				else:

					#we construct a KeyCategoricalValueFrame
					#instance, and set the default button to
					#Yes (true) or No (false) according to the
					#current value:
					i_default_mode=1
					if v_val==False:
						i_default_mode = 2
					#end if

					o_kv=KeyCategoricalValueFrame( s_name=s_param, 
							lq_modes=[ ( "Yes", True ), ( "No", False ) ],
							i_default_mode_number=i_default_mode,
							o_value_type=bool,
							o_master=o_frame_for_this_param, 
							s_associated_attribute=s_param,
							o_associated_attribute_object=self.__input,
							def_on_button_change=self.__test_value,
							i_labelwidth=i_width_labelname,
							s_label_name=s_longname,
							b_buttons_in_a_row = True,
							b_is_enabled=b_allow_entry_change,
							b_force_disable=b_force_disable,
							s_tooltip=s_tooltip )

				o_kv.grid( row=i_row, sticky=(NW) )
			#end if param has non-boolean value else boolean
		#end for each input param

		self.__place_category_frames( self.__category_frames )

		#end for each param

		self.grid_columnconfigure( 0, weight=1 )
		self.grid_rowconfigure( 0, weight=1 )

		return
Example #3
0
	def __init_interface( self, b_force_disable = False ):
		'''
		param b_force_disable, if set to true,
			will set all the KeyValueFrame text entry
			boxes do state "disabled."  This is useful
			to show user when the interface is busy
			computing a simulation, and keeps user
			from editing values when the sim is 
			being run (though, besides any user
			misperceptions, such editign would not
			affect the current sim, but rather would
			be applied to any subsequent run.)

		'''
		
		ENTRY_WIDTH=50
		LABEL_WIDTH=20
		LOCATIONS_FRAME_PADDING=30
		LOCATIONS_FRAME_LABEL="Load/Run"
		LOCATIONS_FRAME_STYLE="groove"
		RUNBUTTON_PADDING=07	

		o_file_locations_subframe=LabelFrame( self,
				padding=LOCATIONS_FRAME_PADDING,
				relief=LOCATIONS_FRAME_STYLE,
				text=LOCATIONS_FRAME_LABEL )

		i_row=0

		o_run_sub_subframe=Frame( o_file_locations_subframe )

		self.__run_button=Button( o_run_sub_subframe, command=self.__on_click_run_or_cancel_simulation_button )
		
		i_tot_procs=pgut.get_cpu_count()

		o_tot_process_validator=FloatIntStringParamValidity( \
					"proccess total",
					int, self.__total_processes_for_sims, 
					1, i_tot_procs )

		o_tot_process_kv=KeyValFrame( "Processes", 
						self.__total_processes_for_sims,
						o_run_sub_subframe,
						o_associated_attribute_object=self,
						s_associated_attribute="_PGGuiSimuPop" \
									+ "__total_processes_for_sims",
						s_entry_justify='left',
						s_label_justify='left',
						s_button_text="Select",
						b_force_disable=b_force_disable,
						o_validity_tester= o_tot_process_validator,
						s_tooltip = "Simulation can use one process per replicate, " \
								+ "but set to no more than the total number " \
								+ "of processors in your computer." )

		o_tot_process_kv.grid( row=i_row, column=0, sticky=( NW ) )

		self.__run_button.grid( row=i_row, column=1, sticky=( NW ), padx=RUNBUTTON_PADDING )

		self.__run_state_label=Label( o_run_sub_subframe, text=self.__run_state_message )

		self.__run_state_label.grid( row=i_row, column=2, sticky=( SW ) )

		o_run_sub_subframe.grid( row=i_row, sticky=( NW ) )

		i_row += 1
		
		s_curr_config_file=self.__config_file.get()

		v_config_file_val="None" if s_curr_config_file == "" else s_curr_config_file

		o_config_kv=KeyValFrame( "Load Configuration File:", 
						v_config_file_val,
						o_file_locations_subframe,
						i_entrywidth=ENTRY_WIDTH,
						i_labelwidth=LABEL_WIDTH,
						b_is_enabled=False,
						s_entry_justify='left',
						s_label_justify='left',
						s_button_text="Select",
						def_button_command=self.load_config_file,
						b_force_disable=b_force_disable )

		o_config_kv.grid( row=i_row, sticky=( NW ) )

		i_row+=1

		o_outdir_kv=KeyValFrame( "Select Output directory", 
					self.__output_directory.get(), 
					o_file_locations_subframe,
					i_entrywidth=ENTRY_WIDTH,
					i_labelwidth=LABEL_WIDTH,
					b_is_enabled=False,
					s_entry_justify='left',
					s_label_justify='left', 
					s_button_text="Select",
					def_button_command=self.select_output_directory,
					b_force_disable=b_force_disable )

		o_outdir_kv.grid( row= i_row, sticky=( NW ) )

		i_row+=1
	
		o_basename_validity_tester=FloatIntStringParamValidity( "output_base", 
				str, self.output_base_name, 1, 
				PGGuiSimuPop.MAX_CHARS_BASENAME )
		
		self.__outbase_kv=KeyValFrame( s_name="Output files base name: ", 
					v_value=self.output_base_name, 
					o_master=o_file_locations_subframe, 
					o_associated_attribute_object=self,
					s_associated_attribute="output_base_name",
					def_entry_change_command=self.__setup_output,
					i_entrywidth=ENTRY_WIDTH,
					i_labelwidth=LABEL_WIDTH,
					s_entry_justify='left',
					s_label_justify='left',
					o_validity_tester=o_basename_validity_tester,
					b_force_disable=b_force_disable ) 

		self.__outbase_kv.grid( row=i_row, sticky=( NW ) )

		o_file_locations_subframe.grid( row=0, sticky=(NW) )
		o_file_locations_subframe.grid_columnconfigure( 0, weight=1 )
		o_file_locations_subframe.grid_rowconfigure( 0, weight=1 )

		self.grid_columnconfigure( 0, weight=1 )
		self.grid_rowconfigure( 0, weight=1 )

		return
Example #4
0
	def __load_param_interface( self, b_force_disable=False ):

		PARAMETERS_FRAME_PADDING=30
		PARAMETERS_FRAME_LABEL="Parameters"
		PARAMETERS_FRAME_STYLE="groove"
		PAD_LABEL_WIDTH=-3

		'''
		As of 2016_08_09, not ready for user to
		access these -- currently means the Ne estimation
		will involve no subsampling of pops in the genepop
		files, and output will be the standard table 
		ie DebugMode in pgdriveneestimator.py is set at
		"no_debug")
		'''
		HIDDEN_PARAMS=[ "sampscheme", "sampparam", "runmode" ]

		LABEL_WIDTH=self.__get_max_longname_length() \
				+ PAD_LABEL_WIDTH

		ENTRY_WIDTH_NON_STRING=7

		GRID_SPACE_HORIZ=10
		
		ls_params=self.__param_set.shortnames

		o_params_subframe=LabelFrame( self,
				padding=PARAMETERS_FRAME_PADDING,
				relief=PARAMETERS_FRAME_STYLE,
				text=PARAMETERS_FRAME_LABEL )

		i_row=0

		for s_param in ls_params:

			if s_param in HIDDEN_PARAMS:
				continue
			#end if hidden param

			s_attr_name=ATTRIBUTE_DEMANLGER + s_param
			v_val=eval (  "self." + s_attr_name )
			s_longname=self.__param_set.longname( s_param )
			s_tooltip=self.__param_set.getToolTipForParam( s_param )

			if VERY_VERBOSE:
				print ( "in test, param name: " + s_param )
				print ( "in test, param value type: " +	str( type( v_val ) ) )
				print ( "in test, param value: " +	str(  v_val ) )
			#end if VERY_VERBOSE

			i_width_this_entry= len( v_val ) if type( v_val ) == str \
					else ENTRY_WIDTH_NON_STRING
			s_this_entry_justify="left" if type( v_val ) == str \
					else "right"
			
			o_this_keyval=KeyValFrame( s_longname,
				v_val,
				o_params_subframe,
				o_associated_attribute_object=self,
				def_entry_change_command=self.__test_value,
				s_associated_attribute=s_attr_name,
				i_entrywidth=i_width_this_entry,
				i_labelwidth=LABEL_WIDTH,
				b_is_enabled=True,
				s_entry_justify=s_this_entry_justify,
				s_label_justify='left',
				s_tooltip=s_tooltip,
				b_force_disable=b_force_disable )

			o_this_keyval.grid( row=i_row, sticky=( NW ) )

			i_row += 1
		#end for each param

		o_params_subframe.grid( row=ROW_NUM_PARAMETERS_FRAME, 
					column=COL_NUM_PARAMETERS_FRAME,
					sticky=(NW), padx=GRID_SPACE_HORIZ )

		o_params_subframe.grid_columnconfigure(0, weight=1 )
		o_params_subframe.grid_rowconfigure( 0, weight=1 )

		self.grid_columnconfigure( 1, weight=1 )
		self.grid_rowconfigure( 0, weight=1 )
		return
Example #5
0
	def __init_file_locations_interface( self, b_force_disable=False ):
		ENTRY_WIDTH=30
		LABEL_WIDTH=20
		LOCATIONS_FRAME_PADDING=30
		LOCATIONS_FRAME_LABEL="Load/Run"
		LOCATIONS_FRAME_STYLE="groove"

		o_file_locations_subframe=LabelFrame( self,
				padding=LOCATIONS_FRAME_PADDING,
				relief=LOCATIONS_FRAME_STYLE,
				text=LOCATIONS_FRAME_LABEL )

		i_row=0

		'''
		To keep the run/cancel button close to
		a label that is a (primiive) progress message
		we put themn in a frame that itself, will be
		inside the file-locations subframe.
		'''
		o_run_sub_subframe=Frame( o_file_locations_subframe )

		self.__run_button=Button( o_run_sub_subframe, 
						command= \
							self.__on_click_run_or_cancel_neestimation_button )
		
		self.__run_button.grid( row=i_row, sticky=( NW )	)

		#this label will have text showing when an estimation
		#is running:
		self.__run_state_label=Label( o_run_sub_subframe, text=self.__run_state_message )

		self.__run_state_label.grid( row=i_row, column=2, sticky=( SW ) )

		o_run_sub_subframe.grid( row=i_row, sticky=( NW ) )

		i_row += 1
		
		s_curr_config_file=self.__genepopfiles.get()

		o_config_kv=KeyValFrame( "Load Genepop Files", 
						self.__genepopfiles.get(),
						o_file_locations_subframe,
						i_entrywidth=ENTRY_WIDTH,
						i_labelwidth=LABEL_WIDTH,
						b_is_enabled=False,
						s_entry_justify='left',
						s_label_justify='left',
						s_button_text="Select",
						def_button_command=self.__load_genepop_files,
						b_force_disable=b_force_disable )

		o_config_kv.grid( row=i_row, sticky=( NW ) )

		i_row+=1

		o_outdir_kv=KeyValFrame( "Select Output directory", 
					self.__output_directory.get(), 
					o_file_locations_subframe,
					i_entrywidth=ENTRY_WIDTH,
					i_labelwidth=LABEL_WIDTH,
					b_is_enabled=False,
					s_entry_justify='left',
					s_label_justify='left', 
					s_button_text="Select",
					def_button_command=self.__select_output_directory,
					b_force_disable=b_force_disable )

		o_outdir_kv.grid( row= i_row, sticky=( NW ) )

		i_row+=1

		self.__outbase_kv=KeyValFrame( s_name="Output files base name: ", 
					v_value=self.output_base_name, 
					o_master=o_file_locations_subframe, 
					i_entrywidth=ENTRY_WIDTH,
					i_labelwidth=LABEL_WIDTH,
					s_associated_attribute="output_base_name",
					o_associated_attribute_object=self,
					def_entry_change_command=self.__test_value,
					s_entry_justify='left',
					s_label_justify='left',
					b_force_disable=b_force_disable ) 

		self.__outbase_kv.grid( row=i_row, sticky=( NW ) )

		o_file_locations_subframe.grid( row=ROW_NUM_FILE_LOCATIONS_FRAME,
										sticky=(NW) )
		o_file_locations_subframe.grid_columnconfigure( 0, weight=1 )
		o_file_locations_subframe.grid_rowconfigure( 0, weight=1 )
Example #6
0
class PGGuiNeEstimator( pgg.PGGuiApp ):
	'''
	Subclass of PGGuiApp builds a gui,
	makes a PGInputNeEstimator and PGOutNeEstimator	
	object, then, on user command runs an (Ld)Ne-Estimation
	using a PGOpNeEstimator object.
	objects, then on user command 	
	'''

	#possible states of the gui:
	RUN_NOT_READY=0
	RUN_READY=1
	RUN_IN_PROGRESS=2
	
	def __init__( self, o_parent, 
						s_param_names_file,
						s_name="ne_estimator_gui" ):

		'''
		param o_parent is the tkinter window that hosts this gui.  I is meant to be the same parent as that which hosts other pg interfaces, like pgguisimupop objects.
		param s_param_names_file is the file that contains short and long param names, used to make a PGParamSet object, in turn used to setup the gpdriveneestimator call.
		param s_name gives the parent PGGuiApp's name (currently, Fri Aug  5 16:48:08 MDT 2016, of no importance).
		param i_total_processes_for_est, integer given the number of processes to assign to the ne-estimation, which does each population in each genepop file in a separate python, mulitiprocessing.process.
		'''
				
		pgg.PGGuiApp.__init__( self, s_name, o_parent )

		self.__param_names_file=s_param_names_file

		self.__simulation_is_in_progress=False

		#ref to the PGParamSet object
		#created using the param_names_file:
		self.__param_set=None
		
		#a string giving a list 
		#of input files:
		self.__genepopfiles=StringVar()

		#variables for the output
		self.__output_directory=StringVar()
		#no StringVar because we want
		#the KeyValFrame to set this
		#directly:
		self.output_base_name=""

		#attributes for the parameters
		self.__sampscheme=None
		self.__sampparam=None
		self.__minpopsize=None
		self.__minallelefreq=None		
		self.__replicates=None
		self.__processes=None
		self.__runmode=None

		#displays the current list of
		#genepop files loaded and ready
		#to run:
		self.__genepop_files_listbox=None

		#python multiprocessing.Process
		#spawned in def runEstimator:
		self.__op_process=None

		self.__estimation_in_progress=False
		self.__run_state_message=""

		#create interface
		self.__get_param_set( s_param_names_file )
		self.__set_initial_file_info()
		self.__set_initial_param_values()
		self.__init_interface()
		self.__load_param_interface()
		
		self.__set_controls_by_run_state( self.__get_run_state() )
		return
	#end __init__


	def __get_param_set( self, s_param_names_file ):
		self.__param_set=pgps.PGParamSet( s_param_names_file )
		return
	#end __get_param_set

	def __set_initial_file_info( self ):
		
		INIT_GENEPOP_FILE_NAME="none"
		INIT_OUTPUT_DIRECTORY=pgut.get_current_working_directory()
		INIT_OUTPUT_FILES_BASE_NAME="nb.out." \
				+ pgut.get_date_time_string_dotted()


		self.__genepopfiles.set( INIT_GENEPOP_FILE_NAME )
		self.__output_directory.set( INIT_OUTPUT_DIRECTORY )
		self.output_base_name=INIT_OUTPUT_FILES_BASE_NAME

		return
	#end __set_initial_file_info


	def __set_initial_param_values( self ):

		for s_param_name in self.__param_set.shortnames:

			s_section_name=self.__param_set.getConfigSectionNameForParam( 
					s_param_name )
			
						
			s_init_val_as_string= \
					self.__param_set.getDefaultValueForParam( \
					s_param_name )

			s_attr_name_for_this_param=ATTRIBUTE_DEMANLGER + s_param_name

			#assign the value to the attribute name 	
			setattr ( self,   s_attr_name_for_this_param, 
						eval(  s_init_val_as_string  ) )
			#end if 

		#end for each param name, set inti val

		return
	#end __set_initial_param_values

	def __on_click_run_or_cancel_neestimation_button( self ):
		if self.__estimation_in_progress:
			o_mbox=PGGUIYesNoMessage( self , 
						"Are you sure you want to cancel " \
					+ "the Ne estimation and remove all of its output files?" )
			b_answer = o_mbox.value
			if b_answer:
				self.__cancel_neestimation()
				self.__estimation_in_progress=False
				self.__set_controls_by_run_state( self.__get_run_state() )
			#end if yes
		else:
			if self.__params_look_valid( b_show_message=True ):
				self.runEstimator()
			#end if params look valid

		#end if sim in progress else not
		return

	#end __on_click_run_or_cancel_neestimation_button

	def __get_path_to_genepop_files( self ):

		s_genepop_files=self.__genepopfiles.get()

		ls_genepop_files=s_genepopfiles.split( DELIMITER_GENEPOP_FILES )

		ls_paths=[]

		for s_file in ls_genepop_files:
			s_path_no_filename=pgut.get_dirname_from_filename( s_file )

			ls_paths.append( s_path_no_filename )
		#end for each genepop file, get path

		set_uniq_paths=set( ls_paths )

		if len( set_uniq_paths ) != 1:
			s_msg="In PGGuiNeEstimator instance, def pgguineestimator, " \
					+ "non-uniq path to genepop files.  List of paths: " \
					+ str( ls_paths ) + "."
			sys.stderr.write( s_msg + "\n" )
		#end if nonuniq path

		#we don't abort even in non-uniq paths, but simply return the first:
		return ls_paths[0]
	#end __get_path_to_genepop_files

	def __convert_genepop_file_to_neestimator_basename( self, s_genepop_file ):

			'''
			For intermediate NeEstimator input/output files,
			pgdriveneestimator replaces dot chars in the file
			name with underscores (adaptation to the Ne2L
			programs file-truncation in nameing output files
			(see pgdriveneestimator)
			'''

			s_filename_no_path=pgut.get_basename_from_path( s_genepop_file )
			s_filename_dots_replaced=s_filename_no_path.replace( ".", 
							pgdn.INPUT_FILE_DOT_CHAR_REPLACEMENT )

			s_dirname_genepop_file = \
					pgut.get_dirname_from_path( s_genepop_file )

			s_pathsep=pgut.get_separator_character_for_current_os()	

			s_reformatted_genepop_file_base=s_pathsep.join( \
					[ s_dirname_genepop_file, s_filename_dots_replaced ] )

			return s_reformatted_genepop_file_base
	#end __convert_genepop_file_to_neestimator_basename

	def __get_output_file_names( self ):
		s_path_sep=pgut.get_separator_character_for_current_os()
		s_output_base_with_path=self.__output_directory.get()  \
				+ s_path_sep \
				+ self.output_base_name

		ls_output_file_names=[ s_output_base_with_path + "." \
				+ pgut.NE_ESTIMATION_MAIN_TABLE_FILE_EXT, 
				s_output_base_with_path + "." \
				+ pgut.NE_ESTIMATION_SECONDARY_OUTPUT_FILE_EXT ]


		s_genepop_files=self.__genepopfiles.get()

		ls_genepop_files=s_genepop_files.split( DELIMITER_GENEPOP_FILES )

		for s_genepop_file in ls_genepop_files:	

			for s_tag in pgdn.NE_ESTIMATOR_OUTPUT_FILE_TAGS:

				s_reformatted_filename = \
						self.__convert_genepop_file_to_neestimator_basename ( \
																	s_genepop_file )	

				#glob basename plus wildcard, ending
				#with ne-estimator file tag.   (Wildcard
				#is needed because pgdriveneestimator.py
				#will have added sample/replicate/popnun fields
				#to the ouput file basename:
				s_glob=s_reformatted_filename + "*" + s_tag 

				ls_nefiles=pgut.get_list_files_and_dirs_from_glob( s_glob )

				if VERY_VERBOSE:
					print ( "with glob: " + s_glob )
					print ( "got files: " + str( ls_nefiles ) )
				#end if very verbose

				ls_output_file_names += ls_nefiles

			#end for each NeEstimator file tag

			#to remove the intermediate genepop file names
			#(note that this glob, if preceeded by the
			#reformatted genepop filename, would match
			#all of the intermediate files, but we keep the 
			#NE_ESTIMATOR_OUTPUT_FILE_TAGS loop in order
			#to retain that precision, to make it easier
			#to simply remove this code, to retain the
			#intermediate genepop files
			s_glob=s_reformatted_filename + "*" \
					+ pgdn.GLOB_INTERMEDIATE_GENEPOP_FILE_TAGS
			ls_intermediate_genepop_files_and_other_matches = \
					pgut.get_list_files_and_dirs_from_glob( s_glob )

			for s_file in ls_intermediate_genepop_files_and_other_matches:
				#we only want files not already fetched using the NE_ESTIMATOR_OUTPUT_FILE_TAGS:
				if s_file not in ls_output_file_names:
					ls_output_file_names.append( s_file )
				#end if new intermediate file (we infer it must be 
				#an intermediate genepop file, if it matches the glob, but was not
				#already caught using the tags), then add
			#end for each file matched
		#end for each genepop file input, make glob for 
		#ne-estimator intermediate output files

		return ls_output_file_names
	#end __get_output_file_names

	def __remove_output_files( self, ls_files_to_remove):

		if VERY_VERBOSE:
			print( "removing files: " + str( ls_files_to_remove ) )
		#end if very verbose

		pgut.remove_files( ls_files_to_remove )
		return
	#end __remove_output_files

	def __cancel_neestimation( self ):

		SLEEPTIME_WAITING_FOR_EVENT_CLEAR=0.25

		TIMEOUT_WAITING_FOR_EVENT_TO_CLEAR=05

		if self.__op_process is not None:
			
			#Process event created in def runEstimator,
			#passed to pgutitlities.run_driveneestimator_in_new_process,
			#which then passes into pgdriveneestimator.py -- the latter
			#will test it while the process.Pool def map_async is running,
			#and if set will kill the process pool:
			if self.__neest_multi_process_event is not None:
				try:

					if VERY_VERBOSE==TRUE:
						print( "in cancel_neestimation, setting event" )
					#end if very verbose

					self.__neest_multi_process_event.set()
				except o_err as Exception:
					s_msg = "in PGGuiNeEstimator instance in def " \
							+ "__cancel_neestimation, Exception after " \
							+ "setting multi process event: " \
							+ str( o_err ) + "."
					sys.stderr.write( s_msg + "\n" )
				#end try, except
			#end if event is not none

			#after we hear back from op_process via the event being
			#cleared (after we set it), we can terminate op_process:
			try:
				if VERY_VERBOSE:
					print( "sleeping before terminating proc" )
				#end if very verbose

				f_starttime=time.time()

				while self.__neest_multi_process_event.is_set() \
						and time.time() - f_starttime < \
						TIMEOUT_WAITING_FOR_EVENT_TO_CLEAR:

					if VERY_VERBOSE:
						print( "in while loop in gui while event is set" )
					#end if very verbose

					time.sleep( SLEEPTIME_WAITING_FOR_EVENT_CLEAR )
				#end while

				if VERY_VERBOSE:
					if self.__neest_multi_process_event.is_set():
						print( "timed out waiting for event to clear -- even still set. " \
								+ "Terminating op_process " )
					else:
						print( "event now clear. Terminating op_process..." )
					#end if op_process' eval in pgdriveneestimator did not clear the event, else did
				#end if very verbose

				self.__op_process.terminate()
			except o_err as Exception:
					s_msg = "in PGGuiNeEstimator instance in def " \
							+ "__cancel_neestimation, Exception after " \
							+ "terminating the process that starts " \
							+ " the estimation: "  \
							+ str( o_err ) + "."
					sys.stderr.write( s_msg + "\n" )
			#end try, except

			ls_output_files=self.__get_output_file_names()

			self.__remove_output_files( ls_output_files )

			if VERY_VERBOSE:
				print ( "removing the following output files: " \
						+ str( ls_output_files ) )
			#end if very vergbose

		else:
			s_msg="Could not cancel the Ne estimation " \
					+ "run.  The process is not found."
			sys.stderr.write( "Warning: " + s_msg  + "\n"  )
		#end if process exists cancel, else message
		return
	#end __cancel_neestimation


	def __get_list_bad_genepop_file_names( self ):

		MSG_NOT_EXIST=": file does not exist"
		MSG_NAME_TOO_LONG=": file name cannot exceed " \
					+ str( MAX_GENEPOP_FILE_NAME_LENGTH ) \
					+ " characters."
		ls_bad_filenames=[]
		s_genepopfiles=self.__genepopfiles.get()	
		
		ls_genepopfiles=s_genepopfiles.split( DELIMITER_GENEPOP_FILES )
		s_list_of_bad_filenames_with_messages=[]

		for s_file in ls_genepopfiles:

			s_basename=pgut.get_basename_from_path( s_file )
			if not ( pgut.does_exist_and_is_file( s_file ) ):
				ls_bad_filenames.append( s_file + MSG_NOT_EXIST )	
			elif len( s_basename )\
					> MAX_GENEPOP_FILE_NAME_LENGTH:
				ls_bad_filenames.append( s_basename + MSG_NAME_TOO_LONG )
			#end if bad filename
		#end for each file
	
		return ls_bad_filenames
	#end __get_list_bad_genepop_file_names

	def __params_look_valid( self, b_show_message=False ):
		'''
		As of 2016_08_09, this is rudimentary
		validation.  May need to be more
		fine-grained checking, as users encounter
		problem using the interface.
		'''

		b_valid=True

		s_base_msg="The program is not ready for Ne estimation.  " \
				+ "Please note the invalid parameter values:\n\n"

		ls_bad_filenames=self.__get_list_bad_genepop_file_names()

		ls_invalidity_messages=[]

		'''
		Should collect all errors and present all to user at once,
		but for now will just abort on the first encountered 
		invalid param.
		'''

		BULLET="**"

		if len( ls_bad_filenames ) > 0:

			ls_invalidity_messages.append( BULLET +  "Genepop files:\n" \
					+ "\n".join( [ s_name for s_name in ls_bad_filenames ] ) )
			b_valid=False

		#end if one or more bad file names

		s_seperator=pgut.get_separator_character_for_current_os()
		s_current_output_dir=self.__output_directory.get()

		s_dir_and_basename=s_seperator.join( [ s_current_output_dir, 
												self.output_base_name ] )

		ls_existing_outfiles=pgut.get_list_files_and_dirs_from_glob( \
				s_dir_and_basename + "*" ) 

		if len( ls_existing_outfiles ) > 0:

			ls_invalidity_messages.append( \
					BULLET + "Files matching the output " \
					+ "directory and basename already exist.\n" \
					+ "Please rename either the existing files " \
					+ "or the basename/directory params.  " \
					+ "Existing files: " \
					+ "\n".join( ls_existing_outfiles ) )
			b_valid=False
		#end if outfiles exist

		#If one or more of the loaded genepop files
		#has existing intermiediate files produced
		#by a pgdriveneestimator.py run, then we want
		#to abort, since behavior of the NeEstimator given
		#existing files can cause errors, and intermediate
		#genepop files may have been (however unlikely)
		#truncated or mangled in an interrupted run:
		ls_existing_intermediate_files=self.__get_output_file_names()

		if len( ls_existing_outfiles ) > 0:
			ls_invalidity_messages.append( BULLET + "Intermedate files from " \
					+ "an interrupted run exist:\n" \
					+ "\n".join( ls_existing_intermediate_files )
					+ "\nPlease either move/remove the intermediate files " \
					+ "or load genepop files whose names do not prefix the "
					+ "intermediate files." )
		#end if we have existing intermediate files

		if self.__sampscheme not in ( "percent", "remove" ):
			ls_invalidity_messages.append( BULLET + "Unknown sample scheme value: " \
					+ self.__sampscheme + "." )
			b_valid=False
		#end if bad sample scheme
		
		if self.__minpopsize < 0:
			ls_invalidity_messages.append( BULLET + "Minimum population size (must be >= 0). " \
					+ "Current value: " + str( self.__minpopsize ) + "." )
			b_valid=False
		#end if invalid min pop size

		if self.__minallelefreq > 1.0 or self.__minallelefreq < 0:
			ls_invalidity_messages.append( BULLET + "Minimum allele frequence " \
					+ "value must be > 0.0 and <= 1.0.\n" \
					+ "Current value: " + str( self.__minallelefreq ) + "." )
			b_valid=False
		#end if invalid min allele freq

		i_num_cpus=pgut.get_cpu_count()

		#if no reported cpus, assume 1
		if i_num_cpus < 1:
			i_num_cpus = 1
		#end if nu cpus < 1

		if self.__processes < 1 or self.__processes > i_num_cpus:
			ls_invalidity_messages.append(  BULLET + "Number of prcesses must be at least 1 and " \
					+ "no more than " + str( i_num_cpus ) + ".  " \
					+ "Current value: " + str( self.__processes ) + "." )
			b_valid=False
		#end if process total invalid

		if self.__runmode not in [ "default" ] +  pgdn.DebugMode.MODES:
			ls_invalidity_messages.append( BULLET + "Unknown run mode: " + self.__runmode + "." )
			b_valid=False
		#end if 

		if not b_valid:
			s_info_msg=s_base_msg + "\n\n".join( ls_invalidity_messages )
			PGGUIInfoMessage( self, s_info_msg )
		#end if any invalidity

		return b_valid
	#end __validate_run

	def runEstimator( self ):

		'''
		Creates a multiprocessing.Process instance, assigns to member
		self.__op_process.  UsCreates a multiprocessing.event and passes
		it to th
		'''

		self.__neest_multi_process_event=multiprocessing.Event()

		self.__op_process=multiprocessing.Process( \

				target=pgut.run_driveneestimator_in_new_process,
					args= ( \
						self.__neest_multi_process_event,
						self.__genepopfiles.get(),
						self.__sampscheme,
						self.__sampparam,
						self.__minpopsize,
						self.__minallelefreq,
						self.__replicates,
						self.__processes,
						self.__runmode,
						self.__output_directory.get() \
								+ "/" \
								+ self.output_base_name ) )
		

		self.__op_process.start()
		self.__set_controls_by_run_state( self.__get_run_state() )
		self.__init_interface( b_force_disable=True )
		self.__load_param_interface( b_force_disable=True )
		self.__run_state_message="  " + ESTIMATION_RUNNING_MSG
		self.after( 500, self.__check_progress_operation_process )

		if VERY_VERBOSE:
			print( "running estimator" )
		#end if very verbose

		return

	#end runEstimator

	def __init_file_locations_interface( self, b_force_disable=False ):
		ENTRY_WIDTH=30
		LABEL_WIDTH=20
		LOCATIONS_FRAME_PADDING=30
		LOCATIONS_FRAME_LABEL="Load/Run"
		LOCATIONS_FRAME_STYLE="groove"

		o_file_locations_subframe=LabelFrame( self,
				padding=LOCATIONS_FRAME_PADDING,
				relief=LOCATIONS_FRAME_STYLE,
				text=LOCATIONS_FRAME_LABEL )

		i_row=0

		'''
		To keep the run/cancel button close to
		a label that is a (primiive) progress message
		we put themn in a frame that itself, will be
		inside the file-locations subframe.
		'''
		o_run_sub_subframe=Frame( o_file_locations_subframe )

		self.__run_button=Button( o_run_sub_subframe, 
						command= \
							self.__on_click_run_or_cancel_neestimation_button )
		
		self.__run_button.grid( row=i_row, sticky=( NW )	)

		#this label will have text showing when an estimation
		#is running:
		self.__run_state_label=Label( o_run_sub_subframe, text=self.__run_state_message )

		self.__run_state_label.grid( row=i_row, column=2, sticky=( SW ) )

		o_run_sub_subframe.grid( row=i_row, sticky=( NW ) )

		i_row += 1
		
		s_curr_config_file=self.__genepopfiles.get()

		o_config_kv=KeyValFrame( "Load Genepop Files", 
						self.__genepopfiles.get(),
						o_file_locations_subframe,
						i_entrywidth=ENTRY_WIDTH,
						i_labelwidth=LABEL_WIDTH,
						b_is_enabled=False,
						s_entry_justify='left',
						s_label_justify='left',
						s_button_text="Select",
						def_button_command=self.__load_genepop_files,
						b_force_disable=b_force_disable )

		o_config_kv.grid( row=i_row, sticky=( NW ) )

		i_row+=1

		o_outdir_kv=KeyValFrame( "Select Output directory", 
					self.__output_directory.get(), 
					o_file_locations_subframe,
					i_entrywidth=ENTRY_WIDTH,
					i_labelwidth=LABEL_WIDTH,
					b_is_enabled=False,
					s_entry_justify='left',
					s_label_justify='left', 
					s_button_text="Select",
					def_button_command=self.__select_output_directory,
					b_force_disable=b_force_disable )

		o_outdir_kv.grid( row= i_row, sticky=( NW ) )

		i_row+=1

		self.__outbase_kv=KeyValFrame( s_name="Output files base name: ", 
					v_value=self.output_base_name, 
					o_master=o_file_locations_subframe, 
					i_entrywidth=ENTRY_WIDTH,
					i_labelwidth=LABEL_WIDTH,
					s_associated_attribute="output_base_name",
					o_associated_attribute_object=self,
					def_entry_change_command=self.__test_value,
					s_entry_justify='left',
					s_label_justify='left',
					b_force_disable=b_force_disable ) 

		self.__outbase_kv.grid( row=i_row, sticky=( NW ) )

		o_file_locations_subframe.grid( row=ROW_NUM_FILE_LOCATIONS_FRAME,
										sticky=(NW) )
		o_file_locations_subframe.grid_columnconfigure( 0, weight=1 )
		o_file_locations_subframe.grid_rowconfigure( 0, weight=1 )
	#end __init_file_locations_interface

	def __init_file_info_interface( self ):

		LISTBOX_WIDTH=50
		LISTBOX_HEIGHT=5

		#as of 2016_08_08, tried to set foreground before and after
		#enabling/disabling in def __update_genepop_file_listbox(),
		#and also changing foreground using listbox.configitem, but
		#i guess when in disabled state, these values are ignored,
		#and the font color defaults to a barely readable light gray
		#(in Linux at least):

		LISTBOX_FOREGROUND="black"

		#this will affect the disabled state's background, 
		#but due to having found no way to change the light grey
		#forground of the disabled font, I'll default here to
		#white for now:
		LISTBOX_BACKGROUND="white"

		LOCATIONS_FRAME_PADDING=10
		LOCATIONS_FRAME_LABEL="Genepop Files Loaded for Estimation"
		LOCATIONS_FRAME_STYLE="groove"

		o_file_info_subframe=LabelFrame( self,
				padding=LOCATIONS_FRAME_PADDING,
				relief=LOCATIONS_FRAME_STYLE,
				text=LOCATIONS_FRAME_LABEL )

		i_row=0


		o_scrollbar_vert=FredLundhsAutoScrollbar( o_file_info_subframe,
												orient=VERTICAL )

		self.__genepop_files_listbox=Listbox( o_file_info_subframe, 
										width=LISTBOX_WIDTH,
										height=LISTBOX_HEIGHT,
										background=LISTBOX_BACKGROUND,
										foreground=LISTBOX_FOREGROUND )

		o_scrollbar_vert.config( command=self.__genepop_files_listbox.yview )
		o_scrollbar_vert.grid( row=0, column=2, sticky=( N, S ) )

		self.__update_genepop_file_listbox()

		self.__genepop_files_listbox.grid( row=i_row, sticky=( NW ) )

		o_file_info_subframe.grid( row=ROW_NUM_FILE_INFO_FRAME,
										sticky=(NW) )

		o_file_info_subframe.grid_columnconfigure( 0, weight=1 )
		o_file_info_subframe.grid_rowconfigure( 0, weight=1 )

		
	#end __init_file_info_interface

	def __update_basename( self, s_val ):
		self.__basename.set( s_val )
		return
	#end __update_basename

	def __update_genepop_file_listbox( self ):
		self.__genepop_files_listbox.config( state="normal" )

		#clear the list box of current entries:

		self.__genepop_files_listbox.delete( 0, END )

		s_genepopfiles=self.__genepopfiles.get()

		ls_files=s_genepopfiles.split( DELIMITER_GENEPOP_FILES )	

		ls_basenames=[ pgut.get_basename_from_path( s_file ) \
										for s_file in ls_files ]

		ls_basenames.sort()

		for s_basename  in ls_basenames:
			self.__genepop_files_listbox.insert( END, s_basename )
			self.__genepop_files_listbox.itemconfig( END, foreground="white" )
		#end for each basename

		self.__genepop_files_listbox.config( state="disabled" )

		return

	#end __update_genepop_file_listbox

	def __init_interface( self, b_force_disable=False ):
		'''
		Create the controls first needed, 
		like those to get file locations,
		and the listbox that diplays the loaded
		genepop files.

		param b_force_disable, when true, is passed to
		the file-locations interface controls, which
		are subsequencly all set to disable.
		'''
		
		self.__init_file_locations_interface( b_force_disable )
		self.__init_file_info_interface()	

		self.grid_columnconfigure( 0, weight=1 )
		self.grid_rowconfigure( 0, weight=1 )

		return
	#end __init_interface

	def __get_max_longname_length( self ):
		'''
		long names are the parameter names
		used as labels in the interface.  
		We size all parameter labels to the longest.
		'''

		i_max_len=0

		i_max_len=max( [  len(s_name) for s_name in \
				self.__param_set.longnames ] ) 

		return i_max_len

	#end __get_max_longname_length

	def __load_param_interface( self, b_force_disable=False ):

		PARAMETERS_FRAME_PADDING=30
		PARAMETERS_FRAME_LABEL="Parameters"
		PARAMETERS_FRAME_STYLE="groove"
		PAD_LABEL_WIDTH=-3

		'''
		As of 2016_08_09, not ready for user to
		access these -- currently means the Ne estimation
		will involve no subsampling of pops in the genepop
		files, and output will be the standard table 
		ie DebugMode in pgdriveneestimator.py is set at
		"no_debug")
		'''
		HIDDEN_PARAMS=[ "sampscheme", "sampparam", "runmode" ]

		LABEL_WIDTH=self.__get_max_longname_length() \
				+ PAD_LABEL_WIDTH

		ENTRY_WIDTH_NON_STRING=7

		GRID_SPACE_HORIZ=10
		
		ls_params=self.__param_set.shortnames

		o_params_subframe=LabelFrame( self,
				padding=PARAMETERS_FRAME_PADDING,
				relief=PARAMETERS_FRAME_STYLE,
				text=PARAMETERS_FRAME_LABEL )

		i_row=0

		for s_param in ls_params:

			if s_param in HIDDEN_PARAMS:
				continue
			#end if hidden param

			s_attr_name=ATTRIBUTE_DEMANLGER + s_param
			v_val=eval (  "self." + s_attr_name )
			s_longname=self.__param_set.longname( s_param )
			s_tooltip=self.__param_set.getToolTipForParam( s_param )

			if VERY_VERBOSE:
				print ( "in test, param name: " + s_param )
				print ( "in test, param value type: " +	str( type( v_val ) ) )
				print ( "in test, param value: " +	str(  v_val ) )
			#end if VERY_VERBOSE

			i_width_this_entry= len( v_val ) if type( v_val ) == str \
					else ENTRY_WIDTH_NON_STRING
			s_this_entry_justify="left" if type( v_val ) == str \
					else "right"
			
			o_this_keyval=KeyValFrame( s_longname,
				v_val,
				o_params_subframe,
				o_associated_attribute_object=self,
				def_entry_change_command=self.__test_value,
				s_associated_attribute=s_attr_name,
				i_entrywidth=i_width_this_entry,
				i_labelwidth=LABEL_WIDTH,
				b_is_enabled=True,
				s_entry_justify=s_this_entry_justify,
				s_label_justify='left',
				s_tooltip=s_tooltip,
				b_force_disable=b_force_disable )

			o_this_keyval.grid( row=i_row, sticky=( NW ) )

			i_row += 1
		#end for each param

		o_params_subframe.grid( row=ROW_NUM_PARAMETERS_FRAME, 
					column=COL_NUM_PARAMETERS_FRAME,
					sticky=(NW), padx=GRID_SPACE_HORIZ )

		o_params_subframe.grid_columnconfigure(0, weight=1 )
		o_params_subframe.grid_rowconfigure( 0, weight=1 )

		self.grid_columnconfigure( 1, weight=1 )
		self.grid_rowconfigure( 0, weight=1 )
		return
	#end __load_param_interface

	def __clear_grid_below_row(self, o_frame, i_row ):

		for o_item in o_frame.grid_slaves():
			if int( o_item.grid_info()["row"]) > i_row:
				o_item.destroy()
			#end if row below i_row
		#end for each grid item
		return
	#end __clear_grid_below_row
			
	def __get_run_state( self ):
		if self.__estimation_in_progress:
			return PGGuiNeEstimator.RUN_IN_PROGRESS
		else:
			return PGGuiNeEstimator.RUN_READY
		#end if in progress else not
		return
	#end __get_run_state

	def __place_category_frames( self, do_category_frames ):
		#the file locations labelframe has row 0, 
		#(see __init_interface), so:
		FIRST_ROW_NUMBER_FOR_CATEGORY_FRAMES=1
		GRID_SPACE_VERTICAL=10
	
		for s_key in do_category_frames:
			o_frame=do_category_frames[ s_key ]
			if s_key=="none":
				i_frame_order_number=len( do_category_frames )
			else:
				s_frame_label=\
						self.__param_set.getConfigSectionNameFromTag( s_key ) 
				i_frame_order_number=int( \
							self.__param_set.getSectionOrderFromTag( s_key ) )
			#end if categore is "none" then place in last row

			i_cat_frame_row=FIRST_ROW_NUMBER_FOR_CATEGORY_FRAMES \
										+ ( i_frame_order_number - 1 )
			o_frame.grid( row=i_cat_frame_row, sticky=(NW), \
										pady=GRID_SPACE_VERTICAL )
			o_frame.grid_columnconfigure( 0, weight=1 )
			o_frame.grid_rowconfigure( 0, weight=1 )
			i_cat_frame_row+=1
		#end for each catgory frame
	#end __place_category_frames

	def __test_value( self ):
		'''
		for debugging
		'''

		if VERBOSE:
			ls_input_params=self.__param_set.shortnames		

			for s_param in ls_input_params:
				s_attrname=ATTRIBUTE_DEMANLGER + s_param
				print ( s_param + "\t" \
						+ str( getattr( self, s_attrname ) ) )
			#end for each param

			print( "output_base_name\t" + self.output_base_name )
		#end if verbose

		return
	#end __test_value

	def __load_genepop_files( self ):

		s_current_value=self.__genepopfiles.get()

		#dialog returns sequence:
		q_genepop_filesconfig_file=tkfd.askopenfilenames(  \
				title='Load a genepop file' )

	
		if len( q_genepop_filesconfig_file ) == 0:
			return
		#end if no files selected

		#string of files delimited, for member attribute 
		#that has type tkinter.StringVar
		s_genepop_files=DELIMITER_GENEPOP_FILES.join( \
								q_genepop_filesconfig_file )
		self.__genepopfiles.set(s_genepop_files)

		self.__init_interface()
		self.__load_param_interface()
		self.__set_controls_by_run_state( self.__get_run_state() )

		if VERY_VERBOSE:
			print ( "new genepop files value: " \
					+ self.__genepopfiles.get() )
		#end if very verbose

		return
	#end load_genepop_files

	def __select_output_directory( self ):
		s_outputdir=tkfd.askdirectory( \
				title='Select a directory for file output' )
		#if no dir selected, return	

		if s_outputdir=="":
			return
		#end if no dir selected
		
		self.__output_directory.set( s_outputdir )
		self.__init_interface()
		self.__set_controls_by_run_state( self.__get_run_state() )

		return
	#end __select_output_directory

	def __setup_output( self ):
		return
	#end __setup_output

	def __enable_or_disable_frames( self, b_do_enable ):
		'''
		As of 2016_08_08, not yet implemented.
		'''
		return
	#end __enable_or_disable_frames

	def __set_controls_by_run_state( self, i_run_state ):

		if i_run_state==PGGuiNeEstimator.RUN_NOT_READY:
			self.__run_button.config( text="Run Nb Estimation", 
													state="disabled" ) 
			self.__enable_or_disable_frames( b_do_enable=False )
		elif i_run_state==PGGuiNeEstimator.RUN_READY:
			self.__run_button.config( text="Run Nb Estimation", 
													state="enabled" )
			self.__enable_or_disable_frames( b_do_enable=True )
		elif i_run_state==PGGuiNeEstimator.RUN_IN_PROGRESS:
			self.__run_button.config( text="Cancel Nb Estimation", 
														state="enabled" )
			self.__enable_or_disable_frames( b_do_enable=False )
		else:
			s_msg="In PGGuiNeEstimator instance, " \
					+ "__set_controls_by_run_state: " \
					"unknown run state value: " + str( i_run_state )
			raise Exception( s_msg )
		#end if run state not ready, else ready, else in progress, 
		#else error
		return
	#end __set_controls_by_run_state

	def __update_run_state_message( self ):
		MAX_DOTS=10

		if self.__run_state_message != "":
			i_len_msg=len( self.__run_state_message )
			
			i_len_dots_stripped=len ( \
					self.__run_state_message.rstrip( "." ) )

			i_curr_num_dots=i_len_msg - i_len_dots_stripped

			if i_curr_num_dots >= MAX_DOTS:
				self.__run_state_message = \
						self.__run_state_message.rstrip( "." ) + "."
			else:
				self.__run_state_message += "."
			#end if max dots reached or exceeded, else not
		return
	#end __update_run_state_message

	def __check_progress_operation_process( self ):

		if self.__op_process is not None:
			if self.__op_process.is_alive():
				if VERY_VERBOSE:
					print ( "checking and process found alive" )
				#endif very verbose, pring
				self.__estimation_in_progress = True
				self.__update_run_state_message()
				self.__run_state_label.configure( text=self.__run_state_message )
				self.after( 500, self.__check_progress_operation_process )
			else:
				if VERY_VERBOSE:
					print( "checking and process not None but not alive" )
				#end if very verbose, print
				
				self.__estimation_in_progress = False

				#found that the process as object will persist
				#long past finishing.  In this case if the user
				#closes the whole gui or the tab for this instance,
				#then the cleanup will read the op_process as alive,
				#and will remove output files, even though the process
				#has finished:
				self.__op_process=None
				self.__neest_multi_process_event=None
				self.__run_state_message=""
				self.__init_interface( b_force_disable = False )
				self.__load_param_interface( b_force_disable = False )

			#end if process alive else not

			self.__set_controls_by_run_state( self.__get_run_state() )

		else:
			if VERY_VERBOSE:	
				print( "process found to be None" )
			#endif very verbose, pring

			self.__estimation_in_progress_is_in_progress = False
			self.__run_state_message=""
			self.__init_interface( b_force_disable = False )
			self.__load_param_interface( b_force_disable = False )


			self.__set_controls_by_run_state( self.__get_run_state() )

		#end if process not None else None

		return
	#end __check_progress_operation_process

	def cleanup( self ):
		self.__cancel_neestimation()
		return