def __adjust_button_width_if_on_windows(self, i_button_width): WINDOWS_REQUIRED_WIDTH_INCREASE = 2 i_adjusted_width = i_button_width b_is_windows_platform = pgut.is_windows_platform() if b_is_windows_platform: i_adjusted_width=i_button_width \ + WINDOWS_REQUIRED_WIDTH_INCREASE #end if is windows platform return i_adjusted_width
def __setup_tsv_file_loader(self): ENTRY_WIDTH = 70 LABEL_WIDTH_NONWIN = 10 LABEL_WIDTH_WINDOWS = 12 i_label_width = LABEL_WIDTH_NONWIN if pgut.is_windows_platform(): i_label_width = LABEL_WIDTH_WINDOWS #end if using windows, need wider label o_myc = PGNeEstimationBoxplotInterface self.__tsv_file_loader_subframe = LabelFrame( self.__master_frame, padding=o_myc.SUBFRAME_PADDING, relief=o_myc.SUBFRAME_STYLE, text="*tsv file") self.__tsv_loader_keyval = KeyValFrame( s_name="Load tsv file", v_value=self.__tsv_file_name, o_type=str, v_default_value="", o_master=self.__tsv_file_loader_subframe, i_entrywidth=ENTRY_WIDTH, i_labelwidth=i_label_width, b_is_enabled=False, s_entry_justify='left', s_label_justify='left', s_button_text="Select", def_button_command=self.__load_tsv_file, b_force_disable=False, s_tooltip="Load a tsv file, output from an Nb/Ne Estimation run") #The entry box should be disabled, but #we want the lable non-grayed-out: self.__tsv_loader_keyval.setLabelState("enabled") self.__tsv_loader_keyval.grid(row=0, sticky=(NW)) self.__tsv_file_loader_subframe.grid( row=o_myc.ROW_NUM_SUBFRAME_TSV_FILE_LOADER, column=o_myc.COLNUM_SUBFRAME_TSV_LOADER, columnspan=o_myc.COLSPAN_SUBFRAME_TSV_LOADER, sticky=(N, W)) return
def __make_outfile_name( self ): o_strvar_outputdir=getattr( self.__interface, self.__mangled_attribute_prefix \ + "output_directory" ) s_outputdir=o_strvar_outputdir.get() s_outfile_basename=self.__interface.output_base_name if pgut.is_windows_platform(): s_outputdir=pgut.fix_windows_path( s_outputdir ) ''' In case the GUI user entered a path separator into the basename entry ''' s_outfile_basename=pgut.fix_windows_path( s_outfile_basename ) #end if windows platform self.__config_outfile_name= \ s_outputdir + "/" + s_outfile_basename \ + PGLineRegressConfigFileMaker.CONFIG_FILE_EXT return
def __remove_temporary_directory_and_all_of_its_contents( self, s_temp_dir ): ''' Note: shutil.rmtree fails on encountering readonly files We add a few paranoia-induced checks, in case the arg s_temp_dir is not, as intended, solely the garbage dump for NeEstimator ''' CORRECT_TMP_DIR_PREFIX="tmp" CORRECT_TEMP_FILE_PREFIX="temp" NUMBER_SUBDIRS_EXPECTED=0 SUSPICOUSLY_HIGH_FILE_COUNT=4 tup_path_head_tail = os.path.split( s_temp_dir ) s_dir_name=tup_path_head_tail[ 1 ] if not( s_dir_name.startswith( CORRECT_TMP_DIR_PREFIX ) ): s_msg = "in PGOpNeEstimator instance, " \ + "def __remove_temporary_directory_and_all_its_contents, " \ + "s_temp_dir directory name, " \ + s_temp_dir \ + " should begin with \"tmp\"." raise Exception( s_msg ) #end if non-tmp name ls_files=pgut.get_list_file_objects( s_temp_dir ) ls_directories=pgut.get_list_subdirectories( s_temp_dir ) i_num_subdirs=len( ls_directories ) i_num_files=len( ls_files ) #Abort if path looks wrong: if i_num_subdirs > NUMBER_SUBDIRS_EXPECTED \ or i_num_files >= SUSPICOUSLY_HIGH_FILE_COUNT: s_msg="in PGOpNeEstimator instance, " \ + "def __remove_temporary_directory_and_all_its_contents, " \ + "temp dir: " + s_temp_dir \ + ", not able to remove temp directory " \ + "due to unexpectedly high " \ + "file count, " + str( i_num_files ) \ + ", and/or subdirectory count, " + str(i_num_subdirs ) + "." ''' 2018_04_02. For easier debugging, we add the file and subdirectory lists to the error message. ''' s_msg += " Subdirectory names: " + str( ls_directories ) \ + ". File names: " + str( ls_files ) + "." raise Exception( s_msg ) #end if dir/file counts look wrong #Abort if any file name looks wrong for s_file in ls_files: if not ( s_file.startswith( CORRECT_TEMP_FILE_PREFIX ) ): s_msg="in PGOpNeEstimator instance, " \ + "def __remove_temporary_directory_and_all_its_contents, " \ + "temp dir: " + s_temp_dir \ + ", not able to remove temp directory. " \ + "The program found that it contains a file that does " \ + "not start with the correct prefix. File: " \ + s_file \ + ", correct prefix: " + CORRECT_TMP_DIR_PREFIX + "." raise Exception( s_msg ) #end if file does not appear to be a temp file, abort #end for each file ''' 2018_03_23 Sometimes, for reasons I do not understand, Windows will not remove one of these temp files because of a permissions error, which will then interrupt the program. If we are using windows, we'll try callinbg rmtreecheck the permissions status of the files, and, if we find the program can't delete we call rmtree with the flag set to ignore errors. ''' if pgut.is_windows_platform(): try: pgut.do_shutil_rmtree( s_temp_dir, b_ignore_errors=False ) except WindowsError as owex: s_msg="Warning: In PGOutputNeEstimator instance, def " \ + "__remove_temporary_directory_and_all_its_contents, " \ + "the call to do_shutil_rmtree idn the pgutilities module " \ + "generated a Windows Error with message, " \ + str( owex ) \ + " The program is now calling do_shutil_rmtree again, " \ + "this time with the flag set to ignore errors." sys.stderr.write( s_msg + "\n" ) pgut.do_shutil_rmtree( s_temp_dir, b_ignore_errors=True ) #end try...except else: pgut.do_shutil_rmtree( s_temp_dir, b_ignore_errors=False) #end if using windows os else not return
def do_simulation_reps_in_subprocesses( o_multiprocessing_event, i_input_reps, i_total_processes_for_sims, s_temp_config_file_for_running_replicates, ls_life_table_files, s_param_names_file, s_output_basename, b_use_gui_messaging=True, i_output_mode=pgsim.PGOpSimuPop.OUTPUT_GENEPOP_ONLY): ''' Failed to get independant initialization of population per-replicate when i used python's multiprocessing.process objects as hosts for running replicates in parallel. Solution as of now, Sat Jul 30 22:38:42 MDT 2016, is to use OS processes via python's subprocess module, creating them in this def, which gets its requisite info needed for the sim from the pgguisimupop instance that calls this def (this def is called by the GUI interface instance in a new python.multiprocessing.process instance, in order not to block the gui). Note that this def was originally inside the pgguisimupop instance that calls it, but on windows there was a pickling error not seen on linux or Mac. Moving this target def for the python.multiprocessing.process outside the pgguisimupop instance seems to have solved the problem on windows (win 10). This def manages the replices via creating subprocesses, and calling pgparallelopmanager.prep_and_call_do_pgopsimupop_replicate (note: the call has been updated 2017_11_12, after the defs were moved from pgutilities into new module pgparallelopmanager. 2017_05_30. We added the def param b_use_gui_messaging=True, to allow this def to be called from a command line implentation (new module pgdrivesimulation.py), and set the flag to false, without having to revise the call from the GUI pgguisimupop.py. ''' ''' Running this inside a try block allows us to notify GUI users of exceptions propogated from anywere inside the simulation setup and execution code. When we catch exceptions below, we send an error message to a gui message box, then re-raise the exception. 2017_08_07. I've added the params i_output_mode and s_pop_het_filter_string, to allow callers to this def to set these new parameters in the PGOpSimuPop object. ''' try: s_life_tables_stringified = ",".join(ls_life_table_files) #if we're on windows, out strings of file #paths may be a mangled mix of unix and #windows separators -- with errors resulting #downstream. So we standardize: if pgut.is_windows_platform(): s_life_tables_stringified= \ pgut.fix_windows_path( s_life_tables_stringified ) s_param_names_file= \ pgut.fix_windows_path( s_param_names_file ) s_output_basename= \ pgut.fix_windows_path( s_output_basename ) #end if windows, fix file path strings i_total_replicates_requested = i_input_reps i_max_processes_to_use = i_total_processes_for_sims ''' we use a python command for a -c arg in the Popen command (see below) and these will be the same args for each replicate (we add the rep-number arg below). We stringify so that they can be Popen command args. Some are de-stringified by the pgparallelopmanager (formerly in pgutilities) def before sending to pgop-creator. ''' seq_first_four_args_to_sim_call = ( s_temp_config_file_for_running_replicates, s_life_tables_stringified, s_param_names_file, s_output_basename) i_total_replicates_started = 0 o_subprocess_group = IndependantSubprocessGroup() while i_total_replicates_started < i_total_replicates_requested: #see if we need to cancel: if o_multiprocessing_event.is_set(): if VERBOSE: print("received event in setup loop") #end if VERBOSE o_subprocess_group.terminateAllSubprocesses() remove_simulation_replicate_output_files(s_output_basename) break else: i_number_subprocesses_alive = o_subprocess_group.getTotalAlive( ) i_number_subprocesses_available= \ i_max_processes_to_use-i_number_subprocesses_alive i_number_replicates_to_go = i_total_replicates_requested - i_total_replicates_started i_number_subprocesses_to_start = i_number_subprocesses_available #reduce the number to start if we have fewer reps to go than avail procs: if i_number_subprocesses_available > i_number_replicates_to_go: i_number_subprocesses_to_start = i_number_replicates_to_go #end if fewer needed than available for idx in range(i_number_subprocesses_to_start): i_total_replicates_started += 1 ''' In the Popen call, rather than rely on a driver python script that would need to be in PATH, we'll simply invoke python with a "-c" argument that imports the pgparallelopmanager (formerly code was in pgutilities) module and executes the correct def: complete the set of args used in the command by adding the replicate number (recall need comma in sequence with single item, to delineate sequence versus simple expression): ''' seq_complete_arg_set = seq_first_four_args_to_sim_call + ( str(i_total_replicates_started), ) ''' 2017_05_30. We added this argument to def prep_and_call_do_pgopsimupop_replicate, to propgate to its call to def do_pgopsimupop_replicate_from_files. ''' seq_complete_arg_set = seq_complete_arg_set + ( str(b_use_gui_messaging), ) ''' 2017_08_07. We added args to allow caller to use diffeent output modes, and, if using the filter output mode, to pass a string with parameters, for filtering pops by mean heterozygosity. (See new additions to PGOpSimuPop code.) 2017_09_04. We remove the s_pop_het_filter_string argument. It has been removed from the arglist for initializing PGOpSimuPop instances, and instead those instances will access it directly from the PGInputSimuPop object. ''' seq_complete_arg_set = seq_complete_arg_set + ( str(i_output_mode), ) ''' in case the negui mods are not in the default python install dirs, then we need to add them to the sys.paths in any spawned invocation of python exe -- seems that the PYTHONPATH env var known to this parent process is unknown to the "python" invocation via Popen. We can get for and then add to our new python invocation the path to all negui modules by getting the path to this module: ''' s_curr_mod_path = os.path.abspath(__file__) #path only (stripped off "/utilities.py" ) #-- gives path to all negui mods: s_mod_dir = os.path.dirname(s_curr_mod_path) #need to fix the path if we're on windows: if pgut.is_windows_platform(): s_mod_dir = pgut.fix_windows_path(s_mod_dir) #end if windows, fix path s_path_append_statements="import sys; sys.path.append( \"" \ + s_mod_dir + "\" );" ''' 2017_11_12. The path statements used import pgutilities, but now we are using this new module pgparallelopmanager. ''' s_python_command=s_path_append_statements + "import pgparallelopmanager as pgpar;" \ "pgpar.prep_and_call_do_pgopsimupop_replicate" \ + "( \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\" )" % seq_complete_arg_set o_new_subprocess = subprocess.Popen( [pgut.PYEXE_FOR_POPEN, "-c", s_python_command]) o_subprocess_group.addSubprocess(o_new_subprocess) #end for each idx of new procs #end if event is set else not #end while preplicates need to be started #we don't want to return until all processes are done. #meantime test for cancel-request i_total_still_alive_after_creating_all = o_subprocess_group.getTotalAlive( ) while i_total_still_alive_after_creating_all > 0: if o_multiprocessing_event.is_set(): if VERBOSE: print("received event in final loop") #end if verbose o_subprocess_group.terminateAllSubprocesses() remove_simulation_replicate_output_files(s_output_basename) #end if we are to cancel i_total_still_alive_after_creating_all = o_subprocess_group.getTotalAlive( ) #end while except Exception as oex: o_traceback = sys.exc_info()[2] s_err_info = pgut.get_traceback_info_about_offending_code(o_traceback) s_prefix_msg_with_trace="Error caught by pgparallelopmanager, " \ + "def __do_simulation_reps_in_subprocesses." \ + "\\nError origin info:\\n" \ + s_err_info if b_use_gui_messaging: pgut.show_error_in_messagebox_in_new_process( oex, s_msg_prefix=s_prefix_msg_with_trace) #end if use gui messaging s_msg = s_prefix_msg_with_trace + str(oex) raise Exception(oex) #end try...except... return
def __make_dict_interface_param_values( self ): ls_interface_member_names=dir( self.__interface ) #GUI interface members for viz all begin #with this prefix--we need the trailing delimiter #because a generel attribute "viztype" is used only #by the GUI (not a plotting param) s_viz_prefix=self.__mangled_attribute_prefix + "viz" \ + PGLineRegressConfigFileMaker.GUI_ATTRIBUTE_DELIMIT for s_member_name in ls_interface_member_names: if s_member_name.startswith( s_viz_prefix ): #strip off the mangling: s_member_name_unmangled=s_member_name.replace( self.__mangled_attribute_prefix, "" ) #Extract the param name (used by the viz program): ls_member_name_parts=s_member_name_unmangled.split( \ PGLineRegressConfigFileMaker.GUI_ATTRIBUTE_DELIMIT ) s_viz_section_name=ls_member_name_parts[ \ PGLineRegressConfigFileMaker.IDX_GUI_ATTRIB_CONFIG_FILE_SECTION ] s_viz_param_name=ls_member_name_parts[ \ PGLineRegressConfigFileMaker.IDX_GUI_ATTRIB_CONFIG_FILE_PARAM ] v_value_this_param = getattr( self.__interface, s_member_name ) if s_viz_section_name not in self.__ds_interface_param_values_by_param_names_by_section: self.__ds_interface_param_values_by_param_names_by_section[ s_viz_section_name ] = {} #end if section name new to dict, add it #If its an output file (either one of the plot file names in the "destination" section #or the stats out file, whose param name is "outputFileName" (different section in the #Vis config file -- the "confidence" section). if s_viz_section_name == "destination" or s_viz_param_name == "outputFilename": ''' 2017_05_17. We skip writing the outputFilename param if its plot_type attribute is not the one current in the gui (see comments in __init__). ''' if s_viz_param_name == "outputFilename": s_this_attr_plot_type=ls_member_name_parts[ \ PGLineRegressConfigFileMaker.IDX_GUI_ATTRIB_VIZ_TYPE ] if s_this_attr_plot_type != self.__plot_type: continue #end if wrong type for outputFilename, don't write it into dict #end if this is the outputFilename if v_value_this_param not in [ "show", "none" ]: #returns a StrVal tkinter object v_outdir=getattr( self.__interface, self.__mangled_attribute_prefix + "output_directory" ) s_outdir=v_outdir.get() v_value_this_param = s_outdir + "/" + v_value_this_param if pgut.is_windows_platform(): v_value_this_param=pgut.fix_windows_path( v_value_this_param ) #end if using Windows, fix the path if s_viz_section_name == "destination": if not ( v_value_this_param in [ "show", "none" ] \ or v_value_this_param.endswith( ".png" ) \ or v_value_this_param.endswith( ".pdf" ) ) : v_value_this_param = v_value_this_param + ".png" #end if not show, or already has png or pdf extension #end if this is a plotted file without a known image file ext, default to png #end if this is a destination, or stats out "outputFileName" option if not self.__omit_x_range \ or s_viz_param_name not in [ "xMin", "xMax" ]: self.__ds_interface_param_values_by_param_names_by_section[ s_viz_section_name ] [ \ s_viz_param_name ] = v_value_this_param #end if we're not omitting x range options, or if the option is not an x range option #end if the member is a viz param #end for each member of the interface return
def negui_main(): ''' Note, 2016_12_29: I removed all optional arguments for negui.py, by now unnecessary. The optional args were and int giving total processes (now defaulting to 1 process, and settable in the indivudual interfaces), and a default life table file, which I've discarded in favor of always loading all life tables. ''' WINDOW_MARGIN=0.20 CONTAINER_PADDING=10 s_my_mod_path=os.path.abspath( __file__ ) s_my_mod_dir=os.path.dirname( s_my_mod_path ) i_total_simultaneous_processes=1 s_default_life_tables = s_my_mod_dir + "/resources/*life.table.info" s_menu_config=s_my_mod_dir + "/resources/menu_main_interface.txt" s_param_name_file_dir=s_my_mod_dir + "/resources" s_startup_info_file=s_my_mod_dir + "/resources/startup.info" db_found_files=confirm_resources( s_default_life_tables, s_menu_config, s_param_name_file_dir ) #The program can't run without these files: if not ( db_found_files[ "menu_file" ] and db_found_files[ "param_name_files" ] ): s_msg="In negui, def negui_main, " \ + "there were resource files not found: " \ + "\n".join( db_found_files[ "msgs" ] ) PGGUIErrorMessage( None, s_msg ) raise Exception( s_msg ) #end if required file not found ''' The program can run without life table files loaded, but we'll send a warning to the console that all needed params need to be supplied by the config file. ''' if not db_found_files[ "life_tables" ]: s_msg="In negui, def negui_main, " \ + "warning: " \ + "\n".join( db_found_files[ "msgs" ] ) PGGUIWarningMessage( None, s_msg ) sys.stderr.write( s_msg + "\n" ) s_default_life_tables=None #end if param names ds_param_file_names=get_param_file_names( s_param_name_file_dir ) if pgut.is_windows_platform(): if s_default_life_tables is not None: s_default_life_tables=pgut.fix_windows_path( s_default_life_tables ) #end if life table files exist s_menu_config=pgut.fix_windows_path( s_menu_config ) for s_filekey in ds_param_file_names: ds_param_file_names[ s_filekey ] = \ pgut.fix_windows_path( \ ds_param_file_names[ s_filekey ] ) #end for each param file #end if windows, fix paths o_master=Tk() i_width=o_master.winfo_screenwidth() i_height=o_master.winfo_screenheight() i_geo_width=int( ( old_div(i_width,2) ) * ( 1 - WINDOW_MARGIN ) ) i_geo_height=int( ( old_div(i_height,2) ) * ( 1 - WINDOW_MARGIN ) ) o_host=pgn.PGHostNotebook( o_master, s_menu_config, ds_param_file_names[ "sim" ], ds_param_file_names[ "ne" ], ds_param_file_names[ "viz" ], s_glob_life_tables=s_default_life_tables, i_max_process_total=i_total_simultaneous_processes ) o_host.grid( row=0 ) o_host.grid_rowconfigure( 0, weight=1 ) o_host.grid_columnconfigure( 0, weight=1 ) o_host.grid( row=0, column=0, sticky=( N,W,S,E )) o_master.geometry( str( i_geo_width ) + "x" + str( i_geo_height ) ) s_title=get_title( s_startup_info_file ) o_master.title( s_title ) o_master.grid_rowconfigure( 0, weight=1 ) o_master.grid_columnconfigure( 0, weight=1 ) atexit.register( cleanup_gui, o_host ) def ask_before_exit(): s_msg="Exiting will kill any unfinished analyses " \ + "and remove their output files. Exit anyway?" o_mbox=PGGUIYesNoMessage( None, s_msg ) s_answer=o_mbox.value if s_answer == True: o_master.destroy() #end if do exit #end ask_before_exit o_master.protocol( "WM_DELETE_WINDOW", ask_before_exit ) o_master.mainloop() return