class Analyze(wx.Panel): def __init__(self,parent): wx.Panel.__init__(self, parent) self.figure = Figure() self.canv = FigCanvas(self,-1, self.figure) self.toolbar = NavigationToolbar(self.canv) self.toolbar.Hide() self.home = wx.lib.buttons.GenBitmapButton(self, -1, wx.Bitmap('icons/home.jpg')) self.Bind(wx.EVT_BUTTON, self.Home, self.home) self.pan = wx.lib.buttons.GenBitmapButton(self, -1, wx.Bitmap('icons/pan.png')) self.Bind(wx.EVT_BUTTON, self.Pan, self.pan) self.zoom = wx.lib.buttons.GenBitmapButton(self, -1, wx.Bitmap('icons/zoom.jpg')) self.Bind(wx.EVT_BUTTON, self.Zoom, self.zoom) self.plot = wx.lib.buttons.GenBitmapButton(self, -1, wx.Bitmap('icons/plot.png')) self.Bind(wx.EVT_BUTTON, self.Plot_a, self.plot) self.process = wx.lib.buttons.GenBitmapButton(self, -1, wx.Bitmap('icons/processing.png')) self.Bind(wx.EVT_BUTTON, self.Processing, self.process) self.hbox1 = wx.BoxSizer(wx.HORIZONTAL) self.hbox1.Add(self.home, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.hbox1.Add(self.pan, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.hbox1.Add(self.zoom, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.hbox1.Add(self.plot, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.hbox1.Add(self.process, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.vbox = wx.BoxSizer(wx.VERTICAL) self.vbox.Add(self.canv, 1, flag=wx.LEFT | wx.TOP | wx.GROW) self.vbox.Add(self.hbox1, 0, flag=wx.ALIGN_CENTER | wx.TOP) self.SetSizer(self.vbox) def Home(self,e): self.toolbar.home() def Pan(self,e): self.toolbar.pan() def Zoom(self,e): self.toolbar.zoom() def Plot_a(self,e): data = [np.random.random() for i in range(25)] ax = self.figure.add_subplot(111) ax.hold(False) ax.plot(data, '*-') self.canv.draw() def Processing(self,e): print "run process"
class RTPWindow(wx.Frame): #########################Init Funcions############################# def __init__(self,parent=None,dpi=100,geoid=Geodesic.WGS84,resolution="50m",center_lon=0.,fontsize=8, verbose=False): """Constructor""" #call init of super class default_style = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.NO_FULL_REPAINT_ON_RESIZE | wx.WS_EX_CONTEXTHELP | wx.FRAME_EX_CONTEXTHELP wx.Frame.__init__(self, parent, title="Pole Plot %s"%parent.__version__,style=default_style, size=(600*2,600*2)) self.Bind(wx.EVT_CLOSE, self.on_close_main) self.parent=parent if not pymax_found: self.parent.user_warning("PyRot module PyMax not found, pole plot viewer will not be usable"); self.on_close_window(-1) else: self.parent.rtp_open=True self.center_lon = center_lon self.dpi = dpi self.geoid = geoid self.resolution = resolution self.fontsize = fontsize self.verbose = verbose self.poles_to_plot = [] self.panel = wx.Panel(self,-1,size=(400*2,300*2)) #Populate UI and Menu self.init_UI() self.create_menu() self.configure() self.update() def init_UI(self): spacing = 10 #------------------------------------Make DropDown Box-----------------------------------------------------# latlon_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "Window Boundaries"), wx.VERTICAL) proj_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "Choose Projection"), wx.VERTICAL) refresh_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "Refresh Figure"), wx.HORIZONTAL) projs = ["North Polar Stereographic","South Polar Stereographic","Orthographic"] self.proj_box = wx.ComboBox(self.panel, id=wx.ID_ANY,size=(100, 25), value=projs[0], choices=projs, style=wx.CB_DROPDOWN|wx.TE_READONLY) self.Bind(wx.EVT_COMBOBOX, self.on_select_proj,self.proj_box) self.max_lat_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(25,25)) self.min_lat_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(25,25)) self.max_lon_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(25,25)) self.min_lon_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(25,25)) # self.down_sample_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(50,25))\ self.re_render_button = wx.Button(self.panel, id=wx.ID_ANY, label='Refresh Figure',size=(50,25)) self.Bind(wx.EVT_BUTTON, self.on_re_render_button, self.re_render_button) self.add_pole_button = wx.Button(self.panel, id=wx.ID_ANY, label='Add Pole',size=(50,25)) self.Bind(wx.EVT_BUTTON, self.on_add_pole_button, self.add_pole_button) #Projection sizer proj_sizer.Add(self.proj_box, 1, wx.ALIGN_LEFT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing) #Lat-Lon Sizer lat_sizer = wx.BoxSizer(wx.HORIZONTAL) lat_sizer.AddMany([(self.min_lat_box, 1, wx.ALIGN_LEFT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing), (self.max_lat_box, 1, wx.ALIGN_RIGHT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing)]) lon_sizer = wx.BoxSizer(wx.HORIZONTAL) lon_sizer.AddMany([(self.min_lon_box, 1, wx.ALIGN_LEFT|wx.ALIGN_BOTTOM|wx.EXPAND|wx.ALL, spacing), (self.max_lon_box, 1, wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM|wx.EXPAND|wx.ALL, spacing)]) latlon_sizer.AddMany([(lat_sizer, 1, wx.ALIGN_TOP|wx.EXPAND, spacing), (lon_sizer, 1, wx.ALIGN_BOTTOM|wx.EXPAND, spacing)]) #Downsample sizer with downsample box and refresh button refresh_sizer.AddMany([(self.re_render_button, 1, wx.ALIGN_LEFT|wx.ALIGN_BOTTOM|wx.EXPAND|wx.ALL, spacing), (self.add_pole_button, 1, wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM|wx.EXPAND|wx.ALL, spacing)]) #Combine projection and downsample sizers proj_ds_sizer = wx.BoxSizer(wx.VERTICAL) proj_ds_sizer.AddMany([(proj_sizer, 1, wx.ALIGN_TOP|wx.EXPAND, spacing), (refresh_sizer, 1, wx.ALIGN_BOTTOM|wx.EXPAND, spacing)]) #Combine all in final sizer all_txt_btn_sizer = wx.BoxSizer(wx.HORIZONTAL) all_txt_btn_sizer.AddMany([(proj_ds_sizer, 1, wx.ALIGN_LEFT|wx.EXPAND, spacing), (latlon_sizer, 1, wx.ALIGN_RIGHT|wx.EXPAND, spacing)]) #-------------------------------------Make Figure----------------------------------------------------------# self.fig = Figure((2, 2), dpi=self.dpi) self.canvas = FigCanvas(self.panel, -1, self.fig) self.toolbar = NavigationToolbar(self.canvas) self.ax = self.fig.add_subplot(111) psk.remove_axis_lines_and_ticks(self.ax) self.toolbar.Hide() self.plot_setting = "Zoom" self.toolbar.zoom() self.canvas.Bind(wx.EVT_MIDDLE_DOWN,self.on_middle_click_plot) # self.canvas.Bind(wx.EVT_MOTION,self.on_move_mouse_plot) self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.on_select_dleft_click) self.canvas.Bind(wx.EVT_RIGHT_DCLICK, self.on_select_dright_click) #----------------------------------Build UI and Fit--------------------------------------------------------# outer_sizer = wx.BoxSizer(wx.VERTICAL) outer_sizer.AddMany([#(grd_sizer,1,wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND|wx.LEFT|wx.RIGHT,spacing), (all_txt_btn_sizer,1,wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND), (self.canvas,10,wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND)]) self.panel.SetSizerAndFit(outer_sizer) def create_menu(self): """ Generates Menu """ self.menubar = wx.MenuBar() #----------------- # File Menu #----------------- menu_file = wx.Menu() m_import_poles = menu_file.Append(-1, "&Import Poles From CSV", "ImportPoles") self.Bind(wx.EVT_MENU, self.on_import_poles, m_import_poles) menu_file.AppendSeparator() submenu_save_plots = wx.Menu() m_save_plot = submenu_save_plots.Append(-1, "&Save Plot", "") self.Bind(wx.EVT_MENU, self.on_save_plot, m_save_plot,"save-plot") m_new_sub_plots = menu_file.Append(-1, "&Save Result", submenu_save_plots) menu_file.AppendSeparator() m_exit = menu_file.Append(-1, "&Exit\tCtrl-Q", "Exit") self.Bind(wx.EVT_MENU, self.on_close_main, m_exit) #----------------- # Edit Menu #----------------- menu_edit = wx.Menu() self.m_remove_pole = menu_edit.Append(-1, "&Remove Last Pole\tCtrl-Z", "RemovePole") self.Bind(wx.EVT_MENU, self.on_remove_pole, self.m_remove_pole) self.m_remove_all_poles = menu_edit.Append(-1, "&Remove All Poles\tCtrl-Z", "RemovePolesAll") self.Bind(wx.EVT_MENU, self.on_remove_all_poles, self.m_remove_all_poles) self.m_add_strike_unc = menu_edit.AppendCheckItem(-1, "&Add Strike Uncertainty\tCtrl-R", "CalcStrikes") self.Bind(wx.EVT_MENU, self.on_add_strike_unc, self.m_add_strike_unc) self.m_solve_askw = menu_edit.AppendCheckItem(-1, "&Solve for Anomalous Skewness\tCtrl-A-R", "SolveAskw") self.Bind(wx.EVT_MENU, self.on_solve_askw, self.m_solve_askw) #----------------- # View Menu #----------------- menu_view = wx.Menu() self.m_show_lunes = menu_view.AppendCheckItem(-1, "&Show Lunes", "ShowLunes") self.m_show_lunes.Check() self.Bind(wx.EVT_MENU, self.on_show_lunes, self.m_show_lunes) self.m_show_pole = menu_view.AppendCheckItem(-1, "&Show Pole", "ShowPole") # self.m_show_pole.Check() self.Bind(wx.EVT_MENU, self.on_show_pole, self.m_show_pole) self.m_show_a95 = menu_view.AppendCheckItem(-1, "&Show A95", "ShowA95") self.m_show_a95.Check() self.Bind(wx.EVT_MENU, self.on_show_a95, self.m_show_a95) self.m_show_selected = menu_view.AppendCheckItem(-1, "&Show Selected", "ShowSelected") self.m_show_selected.Check() self.Bind(wx.EVT_MENU, self.on_show_selected, self.m_show_selected) #----------------- self.menubar.Append(menu_file, "&File") self.menubar.Append(menu_edit, "&Edit") self.menubar.Append(menu_view, "&View") self.SetMenuBar(self.menubar) def configure(self): self.min_lat_box.SetValue("%.1f"%60.) self.max_lat_box.SetValue("%.1f"%90.) self.min_lon_box.SetValue("%.1f"%-180.) self.max_lon_box.SetValue("%.1f"%180.) self.window = [None,None,None,None] #########################Update UI Funcions############################# def update(self): #Populates Logger and makes plot self.make_map() #Make Background Map if self.m_show_pole.IsChecked(): if len(self.parent.deskew_df[self.parent.deskew_df["track_type"]=="ship"]) > 0: self.parent.save_max_file(".tmp.max",ship_only=True) #Save tmp max file to disk for debugging purposes and to more easily punch data into format using previous functions comment,header,ship_data = pymax.read_max_file(".tmp.max") #Read max file if len(ship_data["phs"])>2: #If more than 2 profiles (even-determined) invert ship (plat,plon,pmag,maj_se,min_se,phi),chisq,dof = pymax.max_likelihood_pole(ship_data, trial_pole=header[:3], out_path="synth_mag_gui.maxout", save_full_data_kernel=self.verbose, step=header[-1], max_steps=100, comment=comment) s1_ship = np.sqrt(chisq/dof)*ship_data["phs"][0][1][1] #ship 1sigma else: s1_ship = 0 else: ship_data,s1_ship = {"phs":[["none",[0.,0.]]]},0 if len(self.parent.deskew_df[self.parent.deskew_df["track_type"]=="aero"]) > 0: self.parent.save_max_file(".tmp.max",aero_only=True) #Do same for aero only data comment,header,aero_data = pymax.read_max_file(".tmp.max") if len(aero_data["phs"])>2: (plat,plon,pmag,maj_se,min_se,phi),chisq,dof = pymax.max_likelihood_pole(aero_data, trial_pole=header[:3], out_path="synth_mag_gui.maxout", save_full_data_kernel=self.verbose, step=header[-1], max_steps=100, comment=comment) s1_aero = np.sqrt(chisq/dof)*aero_data["phs"][0][1][1] else: s1_aero = 0 else: aero_data,s1_aero = {"phs":[["none",[0.,0.]]]},0 self.parent.save_max_file(".tmp.max") #now read all data and change s1 to match above comment,header,data = pymax.read_max_file(".tmp.max") if len(data["phs"])==0: return for i in range(len(data["phs"])): if len(ship_data["phs"]) > 0 and data["phs"][i][1][1]==ship_data["phs"][0][1][1]: data["phs"][i][1][1] = s1_ship elif len(aero_data["phs"]) > 0 and data["phs"][i][1][1]==aero_data["phs"][0][1][1]: data["phs"][i][1][1] = s1_aero if self.m_solve_askw.IsChecked(): (plat,plon,pmag,askw,maj_se,min_se,phi),chisq,dof = pymax.max_likelihood_pole(data, trial_pole=header[:3], out_path="synth_mag_gui.maxout", save_full_data_kernel=self.verbose, step=header[-1], max_steps=100, comment=comment, solve_anom_skew=self.m_solve_askw.IsChecked()) else: (plat,plon,pmag,maj_se,min_se,phi),chisq,dof = pymax.max_likelihood_pole(data, trial_pole=header[:3], out_path="synth_mag_gui.maxout", save_full_data_kernel=self.verbose, step=header[-1], max_steps=100, comment=comment, solve_anom_skew=self.m_solve_askw.IsChecked()) if self.m_add_strike_unc.IsChecked(): #If strike unc is to be included calculate it!!! (maj_se,min_se,phi) = cs.calc_strikes_and_add_err(self.parent.deskew_path,mlat=plat,mlon=plon,ma=maj_se,mb=min_se,mphi=phi,geoid=self.geoid,outfile=".tmp_dsk_cs",filter_by_quality=False,visualize=False,convergence_level=1e-5) os.remove(".tmp_dsk_cs") #write pole coordinates and 1sigmas to plot for user if phi<0: phi = phi+180 elif phi>180: phi = phi%180 if self.m_show_a95.IsChecked(): f_factor = f.ppf(.95,2,dof) print(f_factor) maj_se,min_se = maj_se*np.sqrt(f_factor),min_se*np.sqrt(f_factor) if self.m_solve_askw.IsChecked(): self.ax.annotate(r"%.1f$^\circ$N, %.1f$^\circ$E"%(plat,plon)+"\n"+r"%.1f$^\circ$, %.1f$^\circ$, N%.1fE"%(maj_se,min_se,phi)+"\n"+"Anom. Skw. = %.1f"%askw+"\n"+r"$\chi^2_\nu$ = %.2f"%(chisq/dof)+"\n"+r"$1\sigma_{aero}$=%.1f"%(s1_aero)+"\n"+r"$1\sigma_{ship}$=%.1f"%(s1_ship),xy=(1-0.02,1-0.02),xycoords="axes fraction",bbox=dict(boxstyle="round", fc="w",alpha=.5),fontsize=self.fontsize,ha='right',va='top') else: self.ax.annotate(r"%.1f$^\circ$N, %.1f$^\circ$E"%(plat,plon)+"\n"+r"%.1f$^\circ$, %.1f$^\circ$, N%.1fE"%(maj_se,min_se,phi)+"\n"+r"$\chi^2_\nu$ = %.2f"%(chisq/dof)+"\n"+r"$1\sigma_{aero}$=%.1f"%(s1_aero)+"\n"+r"$1\sigma_{ship}$=%.1f"%(s1_ship),xy=(1-0.02,1-0.02),xycoords="axes fraction",bbox=dict(boxstyle="round", fc="w",alpha=.5),fontsize=self.fontsize,ha='right',va='top') #plot inverted pole self.ax = psk.plot_pole(plon,plat,phi,np.sqrt(chisq/dof)*maj_se,np.sqrt(chisq/dof)*min_se,m=self.ax, alpha=.5, zorder=10000) if self.m_show_lunes.IsChecked(): #filter deskew_df to only data labeled "good" and plot lunes if self.m_solve_askw.IsChecked(): srf,asf = self.parent.get_srf_asf() new_asf = lambda sr: asf(sr)+askw self.parent.deskew_df = sk.calc_aei(self.parent.deskew_df,srf,new_asf) else: self.parent.deskew_df = sk.calc_aei(self.parent.deskew_df,*self.parent.get_srf_asf()) dsk_to_plot = self.parent.deskew_df[self.parent.deskew_df["quality"]=="g"] if self.m_show_selected.IsChecked(): try: self.ax = psk.plot_lunes(dsk_to_plot,self.ax,idx_selected=self.parent.dsk_idx) except AttributeError: self.ax = psk.plot_lunes(dsk_to_plot,self.ax) #catch no selected data case else: self.ax = psk.plot_lunes(dsk_to_plot,self.ax) # os.remove(".tmp.max") #remove the deskew file on disk #plot any additional poles for pole_rec in self.poles_to_plot: print(pole_rec) self.ax = psk.plot_pole(*pole_rec[0],color=pole_rec[1],m=self.ax,zorder=1000) #set the map extent to match user input print([float(self.min_lon_box.GetValue()),float(self.max_lon_box.GetValue()),float(self.min_lat_box.GetValue()),float(self.max_lat_box.GetValue())]) self.ax.set_extent([float(self.min_lon_box.GetValue()),float(self.max_lon_box.GetValue()),float(self.min_lat_box.GetValue()),float(self.max_lat_box.GetValue())], ccrs.PlateCarree()) self.canvas.draw() #rerender def on_close_main(self,event): self.parent.rtp_open=False self.Destroy() ############################Menu Funcions################################ def on_import_poles(self,event): dlg = wx.FileDialog( self, message="Choose CSV File", defaultDir=self.parent.WD, wildcard="Files (*.csv)|*.csv|All Files (*.*)|*.*", style=wx.FD_OPEN ) if dlg.ShowModal() == wx.ID_OK: import_csv=dlg.GetPath() df_poles = pd.read_csv(import_csv,sep=None) dlg.Destroy() uniform_cols = list(map(lambda x: str(x).lower(), df_poles.columns)) df_poles.columns = uniform_cols try: lat_col = next(filter(lambda x: x.startswith("lat"), uniform_cols)) lon_col = next(filter(lambda x: x.startswith("lon"), uniform_cols)) maj_col = next(filter(lambda x: x.startswith("maj"), uniform_cols)) min_col = next(filter(lambda x: x.startswith("min"), uniform_cols)) azi_col = next(filter(lambda x: x.startswith("azi"), uniform_cols)) except: self.parent.user_warning("""Couldn't find a required column. There must be at least 5 columns. These 5 columns must have labels that start with lat, lon, maj, min, azi in any order and case insensitive. If more than one column fits these conditions then the first column is taken.""") return try: color_col = next(filter(lambda x: "color" in x, uniform_cols)) except: color_col=None for i,row in df_poles.iterrows(): if isinstance(color_col,type(None)): self.parent.user_warning("No Color for Pole (%.1f,%.1f), please specify"%(row[lat_col],row[lon_col])) cdlg = wx.ColourDialog(self) if cdlg.ShowModal() == wx.ID_OK: color = tuple(np.array(cdlg.GetColourData().GetColour().Get())/255) else: color = "tab:blue" else: color = row[color_col] self.poles_to_plot.append([row[[lon_col,lat_col,azi_col,maj_col,min_col]].values,color]) self.update() def on_save_plot(self,event): self.toolbar.save_figure() def on_remove_pole(self,event): self.poles_to_plot = self.poles_to_plot[:-1] self.update() def on_remove_all_poles(self,event): self.poles_to_plot = [] self.update() def on_add_strike_unc(self,event): self.update() def on_solve_askw(self,event): self.update() def on_show_lunes(self,event): self.update() def on_show_pole(self,event): self.update() def on_show_a95(self,event): self.update() def on_show_selected(self,event): self.update() ###################Button and Dropdown Functions######################### # def on_change_grd_btn(self,event): # dlg = wx.FileDialog( # self, message="Choose Grid File", # defaultDir=self.parent.WD, # defaultFile="", # wildcard="Grid Files (*.grd,*.nc,*.ncf)|*.grd;*.nc;*.ncf|All Files (*.*)|*.*", # style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST # ) # if dlg.ShowModal() == wx.ID_OK: # self.grd_file = dlg.GetPath() # self.grd_path.SetValue(self.grd_file) # self.update() # dlg.Destroy() # else: dlg.Destroy() def on_select_proj(self,event): self.update() def on_re_render_button(self,event): self.update() def on_add_pole_button(self,event): pdlg = PoleDialog(self) #run text entry dialog if pdlg.ShowModal() == wx.ID_OK: new_pole = [pdlg.lon,pdlg.lat,pdlg.phi,pdlg.a,pdlg.b] else: return pdlg.Destroy() cdata = wx.ColourData() cdata.SetChooseFull(True) # cdata.SetChooseAlpha(True) cdata.SetColour(wx.Colour(255, 0, 0, 128)) cdlg = wx.ColourDialog(self,cdata) if cdlg.ShowModal() == wx.ID_OK: new_color = tuple(np.array(cdlg.GetColourData().GetColour().Get())/255) else: color = "#00FFFF88" if len(new_color)==3 or new_color[3]==1.: new_color = (new_color[0],new_color[1],new_color[2],.5) elif len(new_color)<3: raise RuntimeError("If you're looking at this error in the terminal while running SynthMag GUI, you shouldn't be able to get here and something is significantly wrong with the color picker. Contact the dev on github.") cdlg.Destroy() self.poles_to_plot.append([new_pole,new_color]) #add new pole to list self.update() #update figure ###########################Figure Funcions############################### def on_middle_click_plot(self,event): if event.LeftIsDown() or event.ButtonDClick(): event.Skip(); return elif self.plot_setting == "Zoom": self.plot_setting = "Pan" self.toolbar.pan('off') elif self.plot_setting == "Pan": self.plot_setting = "Zoom" self.toolbar.zoom() event.Skip() # def on_move_mouse_plot(self,event): # try: dsk_row = self.parent.dsk_row # except AttributeError: event.Skip(); return # pos=event.GetPosition() # width, height = self.canvas.get_width_height() # pos = [pos[0],height-pos[1]] # pos = self.ax.transData.inverted().transform(pos) # lonlat = ccrs.PlateCarree().transform_point(*pos,self.proj) # self.plot_tracer_on_self_and_parent(dsk_row,lonlat) # self.parent.canvas.draw() # self.canvas.draw() # event.Skip() def on_select_dleft_click(self,event): #TODO make rtp try: self.parent.dsk_row except AttributeError: event.Skip(); return pos=event.GetPosition() width, height = self.canvas.get_width_height() pos = [pos[0],height-pos[1]] pos = self.ax.transData.inverted().transform(pos) plonlat = ccrs.PlateCarree().transform_point(*pos,self.proj) srf,asf = self.parent.get_srf_asf() reduced_skewness,rel_reduced_amplitude = sk.reduce_dsk_row_to_pole(self.parent.dsk_row,*plonlat,asf,srf) self.parent.phase_shift_box.SetValue("%.1f"%reduced_skewness) self.parent.deskew_df.at[self.parent.dsk_idx,'phase_shift'] = reduced_skewness self.parent.deskew_df.at[self.parent.dsk_idx,'rel_amp'] = rel_reduced_amplitude self.parent.dsk_row = self.parent.deskew_df.loc[self.parent.dsk_idx].iloc[0] self.parent.update(event) self.update() def on_select_dright_click(self,event): #TODO make rtp try: self.parent.deskew_df except AttributeError: event.Skip(); return pos=event.GetPosition() width, height = self.canvas.get_width_height() pos = [pos[0],height-pos[1]] pos = self.ax.transData.inverted().transform(pos) plonlat = ccrs.PlateCarree().transform_point(*pos,self.proj) srf,asf = self.parent.get_srf_asf() for i,row in self.parent.deskew_df.iterrows(): reduced_skewness,rel_reduced_amplitude = sk.reduce_dsk_row_to_pole(row,*plonlat,asf,srf) self.parent.deskew_df.at[i,'phase_shift'] = reduced_skewness self.parent.deskew_df.at[i,'rel_amp'] = rel_reduced_amplitude self.parent.deskew_df = sk.calc_aei(self.parent.deskew_df,srf,asf) try: self.parent.dsk_row = self.parent.deskew_df.loc[self.parent.dsk_idx].iloc[0] except (AttributeError,KeyError) as e: pass self.parent.update(event) self.update() ##########################Additional Plotting and Backend Functions################ def on_parent_select_track(self): self.update() def make_map(self): #set basemap try: self.fig.delaxes(self.ax) except AttributeError: self.parent.user_warning("Unable to remove previous axis and refresh map, raise issue with Dev.") #TODO: ADD TRANSVERSE MERCATOR AT STRIKE AS OPTION if self.proj_box.GetValue() == 'North Polar Stereographic': self.proj = ccrs.NorthPolarStereo(central_longitude=self.center_lon,true_scale_latitude=None,globe=None) self.ax = self.fig.add_subplot(111,projection=self.proj) # pgeo.make_circular_ax(self.ax) elif self.proj_box.GetValue() == 'South Polar Stereographic': self.proj = ccrs.SouthPolarStereo(central_longitude=self.center_lon,true_scale_latitude=None,globe=None) self.ax = self.fig.add_subplot(111,projection=self.proj) # pgeo.make_circular_ax(self.ax) elif self.proj_box.GetValue() == 'Orthographic': self.proj = ccrs.Orthographic(central_longitude=self.center_lon) self.ax = self.fig.add_subplot(111,projection=self.proj) else: self.parent.user_warning("Projection %s not supported"%str(self.proj_box.GetValue())); return # self.ax.set_xticks(np.arange(0, 370, 10.), crs=ccrs.PlateCarree()) # self.ax.set_yticks(np.arange(-80, 90, 10.), crs=ccrs.PlateCarree()) # self.ax.tick_params(grid_linewidth=.5,grid_linestyle=":",color="k",labelsize=8) # lon_formatter = LongitudeFormatter(zero_direction_label=True) # lat_formatter = LatitudeFormatter() # self.ax.xaxis.set_major_formatter(lon_formatter) # self.ax.yaxis.set_major_formatter(lat_formatter) self.ax.gridlines(color='grey', alpha=0.5, linestyle='--',linewidth=.5) land = cfeature.NaturalEarthFeature('physical', 'land', self.resolution, edgecolor="black", facecolor="grey", linewidth=2) self.ax.add_feature(land)
def pan(self, *args): self.ToggleTool(self.wx_ids["Zoom"], False) self.ToggleTool(self.ON_MARKRINGS, False) self.ToggleTool(self.ON_MARKSPOTS, False) NavigationToolbar2WxAgg.pan(self, *args)
def pan(self, *args): self.ToggleTool(self.wx_ids['Zoom'], False) self.ToggleTool(self.ON_MARKRINGS, False) self.ToggleTool(self.ON_MARKSPOTS, False) NavigationToolbar2WxAgg.pan(self, *args)
class SRMWindow(wx.Frame): #########################Init Funcions############################# def __init__(self, spreading_rate_path, parent=None, starting_sz="", fontsize=8, dpi=200): """Constructor""" #call init of super class default_style = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.NO_FULL_REPAINT_ON_RESIZE | wx.WS_EX_CONTEXTHELP | wx.FRAME_EX_CONTEXTHELP wx.Frame.__init__(self, parent, title="Spreading Rate Model %s" % parent.__version__, style=default_style, size=(400 * 2, 300 * 2)) self.Bind(wx.EVT_CLOSE, self.on_close_main) self.parent = parent self.dpi = dpi self.spreading_rate_path = spreading_rate_path self.panel = wx.Panel(self, -1, size=(400 * 2, 300 * 2)) #Populate UI and Menu self.init_UI() self.create_menu() self.sz = starting_sz try: self.open_srm() except: self.srm = None self.update() def init_UI(self): spacing = 10 #---------------------------------Make ListCtrl for SR---------------------------------------------------# self.logger = EditableListCtrl(self.panel, ID=wx.ID_ANY, size=(300, 300), style=wx.LC_REPORT) self.logger.InsertColumn(0, 'End Age', width=150) self.logger.InsertColumn(1, 'Half Rate', width=150) # self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.on_click_listctrl, self.logger) # self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK,self.on_right_click_listctrl,self.logger) # self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_select_measurement, self.logger) #-----------------------------Make DropDown Box and Update-----------------------------------------------# sz_sizer = wx.StaticBoxSizer( wx.StaticBox(self.panel, wx.ID_ANY, "Choose Spreading Zone"), wx.VERTICAL) self.sz_box = wx.ComboBox(self.panel, id=wx.ID_ANY, size=(150, 25), choices=[], style=wx.CB_DROPDOWN | wx.TE_PROCESS_ENTER) self.Bind(wx.EVT_COMBOBOX, self.on_select_sz, self.sz_box) self.Bind(wx.EVT_TEXT_ENTER, self.on_enter_sz, self.sz_box) add_rate_sizer = wx.StaticBoxSizer( wx.StaticBox(self.panel, wx.ID_ANY, "Add Rate to Model"), wx.HORIZONTAL) self.add_end_age_box = wx.TextCtrl(self.panel, id=wx.ID_ANY, size=(50, 25)) self.add_half_rate_box = wx.TextCtrl(self.panel, id=wx.ID_ANY, size=(50, 25)) self.add_rate_btn = wx.Button(self.panel, id=wx.ID_ANY, label='Add Rate', size=(50, 25)) self.Bind(wx.EVT_BUTTON, self.on_add_rate_btn, self.add_rate_btn) add_rate_sizer.AddMany([ (self.add_end_age_box, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.BOTTOM | wx.EXPAND, spacing), (self.add_half_rate_box, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.BOTTOM | wx.EXPAND, spacing), (self.add_rate_btn, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, spacing), ]) update_delete_sizer = wx.BoxSizer(wx.HORIZONTAL) delete_btn = wx.Button(self.panel, id=wx.ID_ANY, label='Delete Selected', size=(75, 25)) self.Bind(wx.EVT_BUTTON, self.on_delete_btn, delete_btn) update_button = wx.Button(self.panel, id=wx.ID_ANY, label='Save and Update', size=(75, 25)) self.Bind(wx.EVT_BUTTON, self.on_update_button, update_button) update_delete_sizer.AddMany([ (update_button, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.EXPAND, spacing), (delete_btn, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT | wx.EXPAND, spacing) ]) sz_sizer.AddMany([ (self.sz_box, 2, wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT | wx.TOP | wx.EXPAND, spacing), (add_rate_sizer, 3, wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT | wx.TOP | wx.EXPAND, spacing), (update_delete_sizer, 2, wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM | wx.EXPAND, spacing) ]) #-------------------------------------Make Figure----------------------------------------------------------# self.fig = Figure((2, 2), dpi=self.dpi) self.canvas = FigCanvas(self.panel, -1, self.fig) self.toolbar = NavigationToolbar(self.canvas) self.ax = self.fig.add_subplot(111) psk.remove_axis_lines_and_ticks(self.ax) self.toolbar.Hide() self.plot_setting = "Zoom" self.toolbar.zoom() self.canvas.Bind(wx.EVT_MIDDLE_DOWN, self.on_middle_click_plot) #----------------------------------Build UI and Fit--------------------------------------------------------# side_bar_sizer = wx.BoxSizer(wx.VERTICAL) side_bar_sizer.AddMany([ (sz_sizer, 1, wx.ALIGN_LEFT | wx.ALIGN_TOP | wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.TOP | wx.EXPAND, spacing), (self.canvas, 8, wx.ALIGN_LEFT | wx.ALIGN_TOP | wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, spacing) ]) outer_sizer = wx.BoxSizer(wx.HORIZONTAL) outer_sizer.AddMany([ (self.logger, 1, wx.ALIGN_LEFT | wx.ALIGN_TOP | wx.EXPAND), (side_bar_sizer, 3, wx.ALIGN_LEFT | wx.ALIGN_TOP | wx.EXPAND) ]) self.panel.SetSizerAndFit(outer_sizer) def create_menu(self): """ Generates Menu """ self.menubar = wx.MenuBar() #----------------- # File Menu #----------------- menu_file = wx.Menu() menu_file.AppendSeparator() m_exit = menu_file.Append(-1, "&Exit\tCtrl-Q", "Exit") self.Bind(wx.EVT_MENU, self.on_close_main, m_exit) #----------------- self.menubar.Append(menu_file, "&File") self.SetMenuBar(self.menubar) #########################Update UI Funcions############################# def update_self_and_parent(self): utl.write_sr_model_file(self.srm, self.spreading_rate_path) try: srf, _ = sk.generate_spreading_rate_model(self.spreading_rate_path) self.parent.sr_box.SetValue( "%.1f" % (srf(self.parent.dsk_row["sz_name"], (self.parent.dsk_row["age_min"] + self.parent.dsk_row["age_max"]) / 2))) except (AttributeError, KeyError) as e: pass self.update() self.parent.update_synth = True self.parent.update(-1) def update(self): #Populates Logger and makes plot if self.parent.spreading_rate_path != self.spreading_rate_path: self.spreading_rate_path = self.parent.spreading_rate_path self.open_srm() if self.spreading_rate_path == None: return self.logger.DeleteAllItems() points = [[], []] if self.sz in self.srm.keys(): prev_age = 0 for i, (end_age, half_rate) in enumerate(self.srm[self.sz]): self.logger.InsertItem(i, "%.3f" % end_age) self.logger.SetItem(i, 1, "%.3f" % half_rate) if end_age > 1e3: end_age = prev_age + 10 if i == 0 and end_age > 10: prev_age = end_age - 10 if prev_age == 0 and end_age <= 0: prev_age = end_age - 10 points[0].append(prev_age) points[0].append(end_age) points[1].append(half_rate) points[1].append(half_rate) prev_age = end_age self.ax.clear() self.ax.plot(points[0], points[1], 'k-') self.canvas.draw() def on_close_main(self, event): self.parent.srmw_open = False self.Destroy() ###################Button and Dropdown Functions######################### def on_select_sz(self, event): self.sz = self.sz_box.GetValue() self.update() def on_enter_sz(self, event): sz_tmp = self.sz_box.GetValue() if sz_tmp in sz_box.GetItems(): self.sz = sz_tmp else: if self.srm != None and self.parent.user_warning( "Spreading Zone provided not in spreading rate model would you like to add to model?" ): self.sz = sz_tmp self.srm[self.sz] = [1e10, 40] else: self.sz_box.SetValue("") self.sz = None self.update() def on_update_button( self, event): #Saves the edits and calls update on self and parent new_sz_srm = [] for i in range(self.logger.GetItemCount()): try: new_sz_srm.append([ float(self.logger.GetItemText(i, 0)), float(self.logger.GetItemText(i, 1)) ]) except ValueError: self.parent.user_warning( "Half Rate and Age to add must be numbers got %s,%s instead" % (self.logger.GetItemText( i, 0), self.logger.GetItemText(i, 1))) return self.srm[self.sz] = new_sz_srm self.update_self_and_parent() def on_add_rate_btn(self, event): try: new_end_age, new_half_rate = float( self.add_end_age_box.GetValue()), float( self.add_half_rate_box.GetValue()) except ValueError: self.parent.user_warning( "Half Rate and Age to add must be numbers got %s,%s instead" % (str(new_end_age), str(new_half_rate))) return try: if new_end_age in np.array(self.srm[self.sz])[:, 0]: self.srm[self.sz] = [ [new_end_age, new_half_rate] if self.srm[self.sz][i][0] == new_end_age else self.srm[self.sz][i] for i in range(len(self.srm[self.sz])) ] else: self.srm[self.sz] += [[new_end_age, new_half_rate]] self.srm[self.sz].sort(key=cmp_to_key(lambda x, y: x[0] - y[0])) except (KeyError, IndexError) as e: self.srm[self.sz] = [[new_end_age, new_half_rate]] self.update_self_and_parent() def on_delete_btn(self, event): next_i, del_idxs = self.logger.GetNextSelected(-1), [] if next_i == -1: return while next_i != -1: del_idxs.append(next_i) next_i = self.logger.GetNextSelected(next_i) new_sz_srm = [] for i in range(self.logger.GetItemCount()): if i in del_idxs: continue #Skip deleted data try: new_sz_srm.append([ float(self.logger.GetItemText(i, 0)), float(self.logger.GetItemText(i, 1)) ]) except ValueError: self.parent.user_warning( "Half Rate and Age to add must be numbers got %s,%s instead" % (self.logger.GetItemText( i, 0), self.logger.GetItemText(i, 1))) return self.srm[self.sz] = new_sz_srm self.update_self_and_parent() ###########################Figure Funcions############################### def on_middle_click_plot(self, event): if event.LeftIsDown() or event.ButtonDClick(): event.Skip() return elif self.plot_setting == "Zoom": self.plot_setting = "Pan" self.toolbar.pan('off') elif self.plot_setting == "Pan": self.plot_setting = "Zoom" self.toolbar.zoom() event.Skip() ###########################Utility Funcions############################### def open_srm(self): self.srm = utl.open_sr_model_file(self.spreading_rate_path) self.sz_box.SetItems([""] + sorted(list(self.srm.keys()))) self.sz_box.SetValue(self.sz)
class InterpretationEditorFrame(wx.Frame): #########################Init Funcions############################# def __init__(self,parent): """Constructor""" #set parent and resolution self.parent = parent self.GUI_RESOLUTION=self.parent.GUI_RESOLUTION #call init of super class default_style = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.NO_FULL_REPAINT_ON_RESIZE | wx.WS_EX_CONTEXTHELP | wx.FRAME_EX_CONTEXTHELP wx.Frame.__init__(self, self.parent, title="Interpretation Editor version:%s"%CURRENT_VERSION,style=default_style, size=(675*self.GUI_RESOLUTION,425*self.GUI_RESOLUTION)) self.Bind(wx.EVT_CLOSE, self.on_close_edit_window) #setup wx help provider class to give help messages provider = wx.SimpleHelpProvider() wx.HelpProvider.Set(provider) self.helper = wx.ContextHelp(doNow=False) #make the Panel self.panel = wx.Panel(self,-1,size=(700*self.GUI_RESOLUTION,450*self.GUI_RESOLUTION)) #set icon self.SetIcon(self.parent.icon) # icon = wx.Icon() # icon_path = os.path.join(IMG_DIRECTORY, 'PmagPy.ico') # if os.path.exists(icon_path): # icon.CopyFromBitmap(wx.Bitmap(icon_path), wx.BITMAP_TYPE_ANY) # self.SetIcon(icon) self.specimens_list=self.parent.specimens self.current_fit_index = None self.search_query = "" self.font_type = self.parent.font_type #build UI and menu self.init_UI() self.create_menu() #update with stuff self.on_select_level_name(None) def init_UI(self): """ Builds User Interface for the interpretation Editor """ #set fonts FONT_WEIGHT=1 if sys.platform.startswith('win'): FONT_WEIGHT=-1 font1 = wx.Font(9+FONT_WEIGHT, wx.SWISS, wx.NORMAL, wx.NORMAL, False, self.font_type) font2 = wx.Font(12+FONT_WEIGHT, wx.SWISS, wx.NORMAL, wx.NORMAL, False, self.font_type) #if you're on mac do some funny stuff to make it look okay is_mac = False if sys.platform.startswith("darwin"): is_mac = True self.search_bar = wx.SearchCtrl(self.panel, size=(350*self.GUI_RESOLUTION,25) ,style=wx.TE_PROCESS_ENTER | wx.TE_PROCESS_TAB | wx.TE_NOHIDESEL) self.Bind(wx.EVT_TEXT_ENTER, self.on_enter_search_bar,self.search_bar) self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, self.on_enter_search_bar,self.search_bar) self.search_bar.SetHelpText(dieh.search_help) # self.Bind(wx.EVT_TEXT, self.on_complete_search_bar,self.search_bar) #build logger self.logger = wx.ListCtrl(self.panel, -1, size=(100*self.GUI_RESOLUTION,475*self.GUI_RESOLUTION),style=wx.LC_REPORT) self.logger.SetFont(font1) self.logger.InsertColumn(0, 'specimen',width=75*self.GUI_RESOLUTION) self.logger.InsertColumn(1, 'fit name',width=65*self.GUI_RESOLUTION) self.logger.InsertColumn(2, 'max',width=55*self.GUI_RESOLUTION) self.logger.InsertColumn(3, 'min',width=55*self.GUI_RESOLUTION) self.logger.InsertColumn(4, 'n',width=25*self.GUI_RESOLUTION) self.logger.InsertColumn(5, 'fit type',width=60*self.GUI_RESOLUTION) self.logger.InsertColumn(6, 'dec',width=45*self.GUI_RESOLUTION) self.logger.InsertColumn(7, 'inc',width=45*self.GUI_RESOLUTION) self.logger.InsertColumn(8, 'mad',width=45*self.GUI_RESOLUTION) self.logger.InsertColumn(9, 'dang',width=45*self.GUI_RESOLUTION) self.logger.InsertColumn(10, 'a95',width=45*self.GUI_RESOLUTION) self.logger.InsertColumn(11, 'K',width=45*self.GUI_RESOLUTION) self.logger.InsertColumn(12, 'R',width=45*self.GUI_RESOLUTION) self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnClick_listctrl, self.logger) self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK,self.OnRightClickListctrl,self.logger) self.logger.SetHelpText(dieh.logger_help) #set fit attributes boxsizers self.display_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "display options"), wx.HORIZONTAL) self.name_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "fit name/color"), wx.VERTICAL) self.bounds_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "fit bounds"), wx.VERTICAL) self.buttons_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY), wx.VERTICAL) #logger display selection box UPPER_LEVEL = self.parent.level_box.GetValue() if UPPER_LEVEL=='sample': name_choices = self.parent.samples if UPPER_LEVEL=='site': name_choices = self.parent.sites if UPPER_LEVEL=='location': name_choices = self.parent.locations if UPPER_LEVEL=='study': name_choices = ['this study'] self.level_box = wx.ComboBox(self.panel, -1, size=(110*self.GUI_RESOLUTION, 25), value=UPPER_LEVEL, choices=['sample','site','location','study'], style=wx.CB_DROPDOWN|wx.TE_READONLY) self.Bind(wx.EVT_COMBOBOX, self.on_select_high_level,self.level_box) self.level_box.SetHelpText(dieh.level_box_help) self.level_names = wx.ComboBox(self.panel, -1, size=(110*self.GUI_RESOLUTION, 25), value=self.parent.level_names.GetValue(), choices=name_choices, style=wx.CB_DROPDOWN|wx.TE_READONLY) self.Bind(wx.EVT_COMBOBOX, self.on_select_level_name,self.level_names) self.level_names.SetHelpText(dieh.level_names_help) #mean type and plot display boxes self.mean_type_box = wx.ComboBox(self.panel, -1, size=(110*self.GUI_RESOLUTION, 25), value=self.parent.mean_type_box.GetValue(), choices=['Fisher','Fisher by polarity','None'], style=wx.CB_DROPDOWN|wx.TE_READONLY, name="high_type") self.Bind(wx.EVT_COMBOBOX, self.on_select_mean_type_box,self.mean_type_box) self.mean_type_box.SetHelpText(dieh.mean_type_help) self.mean_fit_box = wx.ComboBox(self.panel, -1, size=(110*self.GUI_RESOLUTION, 25), value=self.parent.mean_fit, choices=(['None','All'] + self.parent.fit_list), style=wx.CB_DROPDOWN|wx.TE_READONLY, name="high_type") self.Bind(wx.EVT_COMBOBOX, self.on_select_mean_fit_box,self.mean_fit_box) self.mean_fit_box.SetHelpText(dieh.mean_fit_help) #show box if UPPER_LEVEL == "study" or UPPER_LEVEL == "location": show_box_choices = ['specimens','samples','sites'] if UPPER_LEVEL == "site": show_box_choices = ['specimens','samples'] if UPPER_LEVEL == "sample": show_box_choices = ['specimens'] self.show_box = wx.ComboBox(self.panel, -1, size=(110*self.GUI_RESOLUTION, 25), value='specimens', choices=show_box_choices, style=wx.CB_DROPDOWN|wx.TE_READONLY,name="high_elements") self.Bind(wx.EVT_COMBOBOX, self.on_select_show_box,self.show_box) self.show_box.SetHelpText(dieh.show_help) #coordinates box self.coordinates_box = wx.ComboBox(self.panel, -1, size=(110*self.GUI_RESOLUTION, 25), choices=self.parent.coordinate_list, value=self.parent.coordinates_box.GetValue(), style=wx.CB_DROPDOWN|wx.TE_READONLY, name="coordinates") self.Bind(wx.EVT_COMBOBOX, self.on_select_coordinates,self.coordinates_box) self.coordinates_box.SetHelpText(dieh.coordinates_box_help) #bounds select boxes self.tmin_box = wx.ComboBox(self.panel, -1, size=(80*self.GUI_RESOLUTION, 25), choices=[''] + self.parent.T_list, style=wx.CB_DROPDOWN|wx.TE_READONLY, name="lower bound") self.tmin_box.SetHelpText(dieh.tmin_box_help) self.tmax_box = wx.ComboBox(self.panel, -1, size=(80*self.GUI_RESOLUTION, 25), choices=[''] + self.parent.T_list, style=wx.CB_DROPDOWN|wx.TE_READONLY, name="upper bound") self.tmax_box.SetHelpText(dieh.tmax_box_help) #color box self.color_dict = self.parent.color_dict self.color_box = wx.ComboBox(self.panel, -1, size=(80*self.GUI_RESOLUTION, 25), choices=[''] + sorted(self.color_dict.keys()), style=wx.CB_DROPDOWN|wx.TE_PROCESS_ENTER, name="color") self.Bind(wx.EVT_TEXT_ENTER, self.add_new_color, self.color_box) self.color_box.SetHelpText(dieh.color_box_help) #name box self.name_box = wx.TextCtrl(self.panel, -1, size=(80*self.GUI_RESOLUTION, 25), name="name") self.name_box.SetHelpText(dieh.name_box_help) #more mac stuff h_size_buttons,button_spacing = 25,5.5 if is_mac: h_size_buttons,button_spacing = 18,0. #buttons self.add_all_button = wx.Button(self.panel, id=-1, label='add new fit to all specimens',size=(160*self.GUI_RESOLUTION,h_size_buttons)) self.add_all_button.SetFont(font1) self.Bind(wx.EVT_BUTTON, self.add_fit_to_all, self.add_all_button) self.add_all_button.SetHelpText(dieh.add_all_help) self.add_fit_button = wx.Button(self.panel, id=-1, label='add fit to highlighted specimens',size=(160*self.GUI_RESOLUTION,h_size_buttons)) self.add_fit_button.SetFont(font1) self.Bind(wx.EVT_BUTTON, self.add_highlighted_fits, self.add_fit_button) self.add_fit_button.SetHelpText(dieh.add_fit_btn_help) self.delete_fit_button = wx.Button(self.panel, id=-1, label='delete highlighted fits',size=(160*self.GUI_RESOLUTION,h_size_buttons)) self.delete_fit_button.SetFont(font1) self.Bind(wx.EVT_BUTTON, self.delete_highlighted_fits, self.delete_fit_button) self.delete_fit_button.SetHelpText(dieh.delete_fit_btn_help) self.apply_changes_button = wx.Button(self.panel, id=-1, label='apply changes to highlighted fits',size=(160*self.GUI_RESOLUTION,h_size_buttons)) self.apply_changes_button.SetFont(font1) self.Bind(wx.EVT_BUTTON, self.apply_changes, self.apply_changes_button) self.apply_changes_button.SetHelpText(dieh.apply_changes_help) #windows display_window_0 = wx.GridSizer(2, 1, 10*self.GUI_RESOLUTION, 19*self.GUI_RESOLUTION) display_window_1 = wx.GridSizer(2, 1, 10*self.GUI_RESOLUTION, 19*self.GUI_RESOLUTION) display_window_2 = wx.GridSizer(2, 1, 10*self.GUI_RESOLUTION, 19*self.GUI_RESOLUTION) name_window = wx.GridSizer(2, 1, 10*self.GUI_RESOLUTION, 19*self.GUI_RESOLUTION) bounds_window = wx.GridSizer(2, 1, 10*self.GUI_RESOLUTION, 19*self.GUI_RESOLUTION) buttons1_window = wx.GridSizer(4, 1, 5*self.GUI_RESOLUTION, 19*self.GUI_RESOLUTION) display_window_0.AddMany( [(self.coordinates_box, wx.ALIGN_LEFT), (self.show_box, wx.ALIGN_LEFT)] ) display_window_1.AddMany( [(self.level_box, wx.ALIGN_LEFT), (self.level_names, wx.ALIGN_LEFT)] ) display_window_2.AddMany( [(self.mean_type_box, wx.ALIGN_LEFT), (self.mean_fit_box, wx.ALIGN_LEFT)] ) name_window.AddMany( [(self.name_box, wx.ALIGN_LEFT), (self.color_box, wx.ALIGN_LEFT)] ) bounds_window.AddMany( [(self.tmin_box, wx.ALIGN_LEFT), (self.tmax_box, wx.ALIGN_LEFT)] ) buttons1_window.AddMany( [(self.add_fit_button, wx.ALL|wx.ALIGN_CENTER|wx.SHAPED, 0), (self.add_all_button, wx.ALL|wx.ALIGN_CENTER|wx.SHAPED, 0), (self.delete_fit_button, wx.ALL|wx.ALIGN_CENTER|wx.SHAPED, 0), (self.apply_changes_button, wx.ALL|wx.ALIGN_CENTER|wx.SHAPED, 0)]) self.display_sizer.Add(display_window_0, 1, wx.TOP|wx.EXPAND, 8) self.display_sizer.Add(display_window_1, 1, wx.TOP | wx.LEFT|wx.EXPAND, 8) self.display_sizer.Add(display_window_2, 1, wx.TOP | wx.LEFT|wx.EXPAND, 8) self.name_sizer.Add(name_window, 1, wx.TOP, 5.5) self.bounds_sizer.Add(bounds_window, 1, wx.TOP, 5.5) self.buttons_sizer.Add(buttons1_window, 1, wx.TOP, 0) #duplicate high levels plot self.fig = Figure((2.5*self.GUI_RESOLUTION, 2.5*self.GUI_RESOLUTION), dpi=100) self.canvas = FigCanvas(self.panel, -1, self.fig, ) self.toolbar = NavigationToolbar(self.canvas) self.toolbar.Hide() self.toolbar.zoom() self.high_EA_setting = "Zoom" self.canvas.Bind(wx.EVT_LEFT_DCLICK,self.on_equalarea_high_select) self.canvas.Bind(wx.EVT_MOTION,self.on_change_high_mouse_cursor) self.canvas.Bind(wx.EVT_MIDDLE_DOWN,self.home_high_equalarea) self.canvas.Bind(wx.EVT_RIGHT_DOWN,self.pan_zoom_high_equalarea) self.canvas.SetHelpText(dieh.eqarea_help) self.eqarea = self.fig.add_subplot(111) draw_net(self.eqarea) #Higher Level Statistics Box self.stats_sizer = wx.StaticBoxSizer( wx.StaticBox( self.panel, wx.ID_ANY,"mean statistics" ), wx.VERTICAL) for parameter in ['mean_type','dec','inc','alpha95','K','R','n_lines','n_planes']: COMMAND="self.%s_window=wx.TextCtrl(self.panel,style=wx.TE_CENTER|wx.TE_READONLY,size=(100*self.GUI_RESOLUTION,25))"%parameter exec(COMMAND) COMMAND="self.%s_window.SetBackgroundColour(wx.WHITE)"%parameter exec(COMMAND) COMMAND="self.%s_window.SetFont(font2)"%parameter exec(COMMAND) COMMAND="self.%s_outer_window = wx.GridSizer(1,2,5*self.GUI_RESOLUTION,15*self.GUI_RESOLUTION)"%parameter exec(COMMAND) COMMAND="""self.%s_outer_window.AddMany([ (wx.StaticText(self.panel,label='%s',style=wx.TE_CENTER),wx.EXPAND), (self.%s_window, wx.EXPAND)])"""%(parameter,parameter,parameter) exec(COMMAND) COMMAND="self.stats_sizer.Add(self.%s_outer_window, 1, wx.ALIGN_LEFT|wx.EXPAND, 0)"%parameter exec(COMMAND) self.switch_stats_button = wx.SpinButton(self.panel, id=wx.ID_ANY, style=wx.SP_HORIZONTAL|wx.SP_ARROW_KEYS|wx.SP_WRAP, name="change stats") self.Bind(wx.EVT_SPIN, self.on_select_stats_button,self.switch_stats_button) self.switch_stats_button.SetHelpText(dieh.switch_stats_btn_help) #construct panel hbox0 = wx.BoxSizer(wx.HORIZONTAL) hbox0.Add(self.name_sizer,flag=wx.ALIGN_TOP|wx.EXPAND,border=8) hbox0.Add(self.bounds_sizer,flag=wx.ALIGN_TOP|wx.EXPAND,border=8) vbox0 = wx.BoxSizer(wx.VERTICAL) vbox0.Add(hbox0,flag=wx.ALIGN_TOP,border=8) vbox0.Add(self.buttons_sizer,flag=wx.ALIGN_TOP,border=8) hbox1 = wx.BoxSizer(wx.HORIZONTAL) hbox1.Add(vbox0,flag=wx.ALIGN_TOP,border=8) hbox1.Add(self.stats_sizer,flag=wx.ALIGN_TOP,border=8) hbox1.Add(self.switch_stats_button,flag=wx.ALIGN_TOP|wx.EXPAND,border=8) vbox1 = wx.BoxSizer(wx.VERTICAL) vbox1.Add(self.display_sizer,flag=wx.ALIGN_TOP,border=8) vbox1.Add(hbox1,flag=wx.ALIGN_TOP,border=8) vbox1.Add(self.canvas,proportion=1,flag=wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,border=8) vbox2 = wx.BoxSizer(wx.VERTICAL) vbox2.Add(self.search_bar,proportion=.5,flag=wx.ALIGN_LEFT | wx.ALIGN_BOTTOM | wx.EXPAND, border=8) vbox2.Add(self.logger,proportion=1,flag=wx.ALIGN_LEFT|wx.EXPAND,border=8) hbox2 = wx.BoxSizer(wx.HORIZONTAL) hbox2.Add(vbox2,proportion=1,flag=wx.ALIGN_LEFT|wx.EXPAND) hbox2.Add(vbox1,flag=wx.ALIGN_TOP|wx.EXPAND) self.panel.SetSizerAndFit(hbox2) hbox2.Fit(self) def create_menu(self): menubar = wx.MenuBar() #-------------------------------------------------------------------- menu_file = wx.Menu() m_change_WD = menu_file.Append(-1, "Change Working Directory\tCtrl-W","") self.Bind(wx.EVT_MENU, self.parent.on_menu_change_working_directory, m_change_WD) m_make_MagIC_results_tables = menu_file.Append(-1, "&Save MagIC pmag tables\tCtrl-Shift-S", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_make_MagIC_results_tables, m_make_MagIC_results_tables) submenu_save_plots = wx.Menu() m_save_high_level = submenu_save_plots.Append(-1, "&Save high level plot", "") self.Bind(wx.EVT_MENU, self.parent.on_save_high_level, m_save_high_level,"Eq") m_new_sub_plots = menu_file.AppendSubMenu(submenu_save_plots, "&Save plot") menu_file.AppendSeparator() m_exit = menu_file.Append(-1, "E&xit\tCtrl-Q", "Exit") self.Bind(wx.EVT_MENU, self.on_close_edit_window, m_exit) #-------------------------------------------------------------------- menu_Analysis = wx.Menu() submenu_criteria = wx.Menu() m_change_criteria_file = submenu_criteria.Append(-1, "&Change acceptance criteria", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_change_criteria, m_change_criteria_file) m_import_criteria_file = submenu_criteria.Append(-1, "&Import criteria file", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_criteria_file, m_import_criteria_file) m_new_sub = menu_Analysis.AppendSubMenu(submenu_criteria, "Acceptance criteria") m_import_LSQ = menu_Analysis.Append(-1, "&Import Interpretations from LSQ file\tCtrl-L", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_read_from_LSQ, m_import_LSQ) m_previous_interpretation = menu_Analysis.Append(-1, "&Import previous interpretations from a redo file\tCtrl-R", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_previous_interpretation, m_previous_interpretation) m_save_interpretation = menu_Analysis.Append(-1, "&Save current interpretations to a redo file\tCtrl-S", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_save_interpretation, m_save_interpretation) #-------------------------------------------------------------------- menu_Tools = wx.Menu() m_view_VGP = menu_Tools.Append(-1, "&View VGPs\tCtrl-Shift-V", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_view_vgps, m_view_VGP) #-------------------------------------------------------------------- menu_Help = wx.Menu() m_help = menu_Help.Append(-1, "&Usage and Tips\tCtrl-H", "") self.Bind(wx.EVT_MENU, self.on_menu_help, m_help) m_cookbook = menu_Help.Append(-1, "&PmagPy Cookbook\tCtrl-Shift-W", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_cookbook, m_cookbook) m_docs = menu_Help.Append(-1, "&Open Docs\tCtrl-Shift-H", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_docs, m_docs) m_git = menu_Help.Append(-1, "&Github Page\tCtrl-Shift-G", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_git, m_git) m_debug = menu_Help.Append(-1, "&Open Debugger\tCtrl-Shift-D", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_debug, m_debug) #-------------------------------------------------------------------- menu_edit = wx.Menu() m_new = menu_edit.Append(-1, "&New interpretation\tCtrl-N", "") self.Bind(wx.EVT_MENU, self.parent.on_btn_add_fit, m_new) m_delete = menu_edit.Append(-1, "&Delete interpretation\tCtrl-D", "") self.Bind(wx.EVT_MENU, self.parent.on_btn_delete_fit, m_delete) m_next_interp = menu_edit.Append(-1, "&Next interpretation\tCtrl-Up", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_next_interp, m_next_interp) m_previous_interp = menu_edit.Append(-1, "&Previous interpretation\tCtrl-Down", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_prev_interp, m_previous_interp) m_next_specimen = menu_edit.Append(-1, "&Next Specimen\tCtrl-Right", "") self.Bind(wx.EVT_MENU, self.parent.on_next_button, m_next_specimen) m_previous_specimen = menu_edit.Append(-1, "&Previous Specimen\tCtrl-Left", "") self.Bind(wx.EVT_MENU, self.parent.on_prev_button, m_previous_specimen) menu_coordinates = wx.Menu() m_speci = menu_coordinates.Append(-1, "&Specimen Coordinates\tCtrl-P", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_change_speci_coord, m_speci) if "geographic" in self.parent.coordinate_list: m_geo = menu_coordinates.Append(-1, "&Geographic Coordinates\tCtrl-G", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_change_geo_coord, m_geo) if "tilt-corrected" in self.parent.coordinate_list: m_tilt = menu_coordinates.Append(-1, "&Tilt-Corrected Coordinates\tCtrl-T", "") self.Bind(wx.EVT_MENU, self.parent.on_menu_change_tilt_coord, m_tilt) m_coords = menu_edit.AppendSubMenu(menu_coordinates, "&Coordinate Systems") #-------------------------------------------------------------------- #self.menubar.Append(menu_preferences, "& Preferences") menubar.Append(menu_file, "&File") menubar.Append(menu_edit, "&Edit") menubar.Append(menu_Analysis, "&Analysis") menubar.Append(menu_Tools, "&Tools") menubar.Append(menu_Help, "&Help") self.SetMenuBar(menubar) ################################Logger Functions################################## def update_editor(self): """ updates the logger and plot on the interpretation editor window """ self.fit_list = [] self.search_choices = [] for specimen in self.specimens_list: if specimen not in self.parent.pmag_results_data['specimens']: continue self.fit_list += [(fit,specimen) for fit in self.parent.pmag_results_data['specimens'][specimen]] self.logger.DeleteAllItems() offset = 0 for i in range(len(self.fit_list)): i -= offset v = self.update_logger_entry(i) if v == "s": offset += 1 def update_logger_entry(self,i): """ helper function that given a index in this objects fit_list parameter inserts a entry at that index @param: i -> index in fit_list to find the (specimen_name,fit object) tup that determines all the data for this logger entry. """ if i < len(self.fit_list): tup = self.fit_list[i] elif i < self.logger.GetItemCount(): self.logger.DeleteItem(i) return else: return coordinate_system = self.parent.COORDINATE_SYSTEM fit = tup[0] pars = fit.get(coordinate_system) fmin,fmax,n,ftype,dec,inc,mad,dang,a95,sk,sr2 = "","","","","","","","","","","" specimen = tup[1] if coordinate_system=='geographic': block_key = 'zijdblock_geo' elif coordinate_system=='tilt-corrected': block_key = 'zijdblock_tilt' else: block_key = 'zijdblock' name = fit.name if pars == {} and self.parent.Data[specimen][block_key] != []: fit.put(specimen, coordinate_system, self.parent.get_PCA_parameters(specimen,fit,fit.tmin,fit.tmax,coordinate_system,fit.PCA_type)) pars = fit.get(coordinate_system) if self.parent.Data[specimen][block_key]==[]: spars = fit.get('specimen') fmin = fit.tmin fmax = fit.tmax if 'specimen_n' in list(spars.keys()): n = str(spars['specimen_n']) else: n = 'No Data' if 'calculation_type' in list(spars.keys()): ftype = spars['calculation_type'] else: ftype = 'No Data' dec = 'No Data' inc = 'No Data' mad = 'No Data' dang = 'No Data' a95 = 'No Data' sk = 'No Data' sr2 = 'No Data' else: if 'measurement_step_min' in list(pars.keys()): fmin = str(fit.tmin) else: fmin = "N/A" if 'measurement_step_max' in list(pars.keys()): fmax = str(fit.tmax) else: fmax = "N/A" if 'specimen_n' in list(pars.keys()): n = str(pars['specimen_n']) else: n = "N/A" if 'calculation_type' in list(pars.keys()): ftype = pars['calculation_type'] else: ftype = "N/A" if 'specimen_dec' in list(pars.keys()): dec = "%.1f"%pars['specimen_dec'] else: dec = "N/A" if 'specimen_inc' in list(pars.keys()): inc = "%.1f"%pars['specimen_inc'] else: inc = "N/A" if 'specimen_mad' in list(pars.keys()): mad = "%.1f"%pars['specimen_mad'] else: mad = "N/A" if 'specimen_dang' in list(pars.keys()): dang = "%.1f"%pars['specimen_dang'] else: dang = "N/A" if 'specimen_alpha95' in list(pars.keys()): a95 = "%.1f"%pars['specimen_alpha95'] else: a95 = "N/A" if 'specimen_k' in list(pars.keys()): sk = "%.1f"%pars['specimen_k'] else: sk = "N/A" if 'specimen_r' in list(pars.keys()): sr2 = "%.1f"%pars['specimen_r'] else: sr2 = "N/A" if self.search_query != "": entry = (specimen+name+fmin+fmax+n+ftype+dec+inc+mad+dang+a95+sk+sr2).replace(" ","").lower() if self.search_query not in entry: self.fit_list.pop(i) if i < self.logger.GetItemCount(): self.logger.DeleteItem(i) return "s" for e in (specimen,name,fmin,fmax,n,ftype,dec,inc,mad,dang,a95,sk,sr2): if e not in self.search_choices: self.search_choices.append(e) if i < self.logger.GetItemCount(): self.logger.DeleteItem(i) self.logger.InsertItem(i, str(specimen)) self.logger.SetItem(i, 1, name) self.logger.SetItem(i, 2, fmin) self.logger.SetItem(i, 3, fmax) self.logger.SetItem(i, 4, n) self.logger.SetItem(i, 5, ftype) self.logger.SetItem(i, 6, dec) self.logger.SetItem(i, 7, inc) self.logger.SetItem(i, 8, mad) self.logger.SetItem(i, 9, dang) self.logger.SetItem(i, 10, a95) self.logger.SetItem(i, 11, sk) self.logger.SetItem(i, 12, sr2) self.logger.SetItemBackgroundColour(i,"WHITE") a,b = False,False if fit in self.parent.bad_fits: self.logger.SetItemBackgroundColour(i,"red") b = True if self.parent.current_fit == fit: self.logger.SetItemBackgroundColour(i,"LIGHT BLUE") self.logger_focus(i) self.current_fit_index = i a = True if a and b: self.logger.SetItemBackgroundColour(i,"red") def update_current_fit_data(self): """ updates the current_fit of the parent Zeq_GUI entry in the case of it's data being changed """ if self.current_fit_index: self.update_logger_entry(self.current_fit_index) def change_selected(self,new_fit): """ updates passed in fit or index as current fit for the editor (does not affect parent), if no parameters are passed in it sets first fit as current @param: new_fit -> fit object to highlight as selected """ if len(self.fit_list)==0: return if self.search_query and self.parent.current_fit not in [x[0] for x in self.fit_list]: return if self.current_fit_index == None: if not self.parent.current_fit: return for i,(fit,specimen) in enumerate(self.fit_list): if fit == self.parent.current_fit: self.current_fit_index = i break i = 0 if isinstance(new_fit, Fit): for i, (fit,speci) in enumerate(self.fit_list): if fit == new_fit: break elif type(new_fit) is int: i = new_fit elif new_fit != None: print(('cannot select fit of type: ' + str(type(new_fit)))) if self.current_fit_index != None and \ len(self.fit_list) > 0 and \ self.fit_list[self.current_fit_index][0] in self.parent.bad_fits: self.logger.SetItemBackgroundColour(self.current_fit_index,"") else: self.logger.SetItemBackgroundColour(self.current_fit_index,"WHITE") self.current_fit_index = i if self.fit_list[self.current_fit_index][0] in self.parent.bad_fits: self.logger.SetItemBackgroundColour(self.current_fit_index,"red") else: self.logger.SetItemBackgroundColour(self.current_fit_index,"LIGHT BLUE") def logger_focus(self,i,focus_shift=16): """ focuses the logger on an index 12 entries below i @param: i -> index to focus on """ if self.logger.GetItemCount()-1 > i+focus_shift: i += focus_shift else: i = self.logger.GetItemCount()-1 self.logger.Focus(i) def OnClick_listctrl(self, event): """ Edits the logger and the Zeq_GUI parent object to select the fit that was newly selected by a double click @param: event -> wx.ListCtrlEvent that triggered this function """ i = event.GetIndex() if self.parent.current_fit == self.fit_list[i][0]: return self.parent.initialize_CART_rot(self.fit_list[i][1]) si = self.parent.specimens.index(self.fit_list[i][1]) self.parent.specimens_box.SetSelection(si) self.parent.select_specimen(self.fit_list[i][1]) self.change_selected(i) fi = 0 while (self.parent.s == self.fit_list[i][1] and i >= 0): i,fi = (i-1,fi+1) self.parent.update_fit_box() self.parent.fit_box.SetSelection(fi-1) self.parent.update_selection() def OnRightClickListctrl(self, event): """ Edits the logger and the Zeq_GUI parent object so that the selected interpretation is now marked as bad @param: event -> wx.ListCtrlEvent that triggered this function """ i = event.GetIndex() fit,spec = self.fit_list[i][0],self.fit_list[i][1] if fit in self.parent.bad_fits: if not self.parent.mark_fit_good(fit,spec=spec): return if i == self.current_fit_index: self.logger.SetItemBackgroundColour(i,"LIGHT BLUE") else: self.logger.SetItemBackgroundColour(i,"WHITE") else: if not self.parent.mark_fit_bad(fit): return if i == self.current_fit_index: self.logger.SetItemBackgroundColour(i,"red") else: self.logger.SetItemBackgroundColour(i,"red") self.parent.calculate_high_levels_data() self.parent.plot_high_levels_data() self.logger_focus(i) ##################################Search Bar Functions############################### def on_enter_search_bar(self,event): self.search_query = self.search_bar.GetValue().replace(" ","").lower() self.update_editor() # def on_complete_search_bar(self,event): # self.search_bar.AutoComplete(self.search_choices) ###################################ComboBox Functions################################ def update_bounds_boxes(self,B_list): self.tmin_box.SetItems(B_list) self.tmax_box.SetItems(B_list) def add_new_color(self,event): new_color = self.color_box.GetValue() if ':' in new_color: color_list = new_color.split(':') color_name = color_list[0] if len(color_list[1])==7 and color_list[1].startswith('#'): for c in color_list[1][1:]: if ord(c) < 48 or ord(c) > 70: self.parent.user_warning('invalid hex color must be of form #0F0F0F');return color_val = color_list[1] elif '(' in color_list[1] and ')' in color_list[1]: color_val = list(map(eval, tuple(color_list[1].strip('( )').split(',')))) for val in color_val: if val > 1 or val < 0: self.parent.user_warning("invalid RGB sequence"); return else: self.parent.user_warning("colors must be given as a valid hex color or rgb tuple"); return else: self.parent.user_warning("New colors must be passed in as $colorname:$colorval where $colorval is a valid hex color or rgb tuple"); return self.color_dict[color_name] = color_val #clear old box self.color_box.Clear() #update fit box self.color_box.SetItems([''] + sorted(self.color_dict.keys())) def on_select_coordinates(self,event): self.parent.coordinates_box.SetStringSelection(self.coordinates_box.GetStringSelection()) self.parent.onSelect_coordinates(event) def on_select_show_box(self,event): """ Changes the type of mean shown on the high levels mean plot so that single dots represent one of whatever the value of this box is. @param: event -> the wx.COMBOBOXEVENT that triggered this function """ self.parent.UPPER_LEVEL_SHOW=self.show_box.GetValue() self.parent.calculate_high_levels_data() self.parent.plot_high_levels_data() def on_select_high_level(self,event,called_by_parent=False): """ alters the possible entries in level_names combobox to give the user selections for which specimen interpretations to display in the logger @param: event -> the wx.COMBOBOXEVENT that triggered this function """ UPPER_LEVEL=self.level_box.GetValue() if UPPER_LEVEL=='sample': self.level_names.SetItems(self.parent.samples) self.level_names.SetStringSelection(self.parent.Data_hierarchy['sample_of_specimen'][self.parent.s]) if UPPER_LEVEL=='site': self.level_names.SetItems(self.parent.sites) self.level_names.SetStringSelection(self.parent.Data_hierarchy['site_of_specimen'][self.parent.s]) if UPPER_LEVEL=='location': self.level_names.SetItems(self.parent.locations) self.level_names.SetStringSelection(self.parent.Data_hierarchy['location_of_specimen'][self.parent.s]) if UPPER_LEVEL=='study': self.level_names.SetItems(['this study']) self.level_names.SetStringSelection('this study') if not called_by_parent: self.parent.level_box.SetStringSelection(UPPER_LEVEL) self.parent.onSelect_high_level(event,True) self.on_select_level_name(event) def on_select_level_name(self,event,called_by_parent=False): """ change this objects specimens_list to control which specimen interpretatoins are displayed in this objects logger @param: event -> the wx.ComboBoxEvent that triggered this function """ high_level_name=str(self.level_names.GetValue()) if self.level_box.GetValue()=='sample': self.specimens_list=self.parent.Data_hierarchy['samples'][high_level_name]['specimens'] elif self.level_box.GetValue()=='site': self.specimens_list=self.parent.Data_hierarchy['sites'][high_level_name]['specimens'] elif self.level_box.GetValue()=='location': self.specimens_list=self.parent.Data_hierarchy['locations'][high_level_name]['specimens'] elif self.level_box.GetValue()=='study': self.specimens_list=self.parent.Data_hierarchy['study']['this study']['specimens'] if not called_by_parent: self.parent.level_names.SetStringSelection(high_level_name) self.parent.onSelect_level_name(event,True) self.specimens_list.sort(key=spec_key_func) self.update_editor() def on_select_mean_type_box(self, event): """ set parent Zeq_GUI to reflect change in this box and change the @param: event -> the wx.ComboBoxEvent that triggered this function """ new_mean_type = self.mean_type_box.GetValue() if new_mean_type == "None": self.parent.clear_high_level_pars() self.parent.mean_type_box.SetStringSelection(new_mean_type) self.parent.onSelect_mean_type_box(event) def on_select_mean_fit_box(self, event): """ set parent Zeq_GUI to reflect the change in this box then replot the high level means plot @param: event -> the wx.COMBOBOXEVENT that triggered this function """ new_mean_fit = self.mean_fit_box.GetValue() self.parent.mean_fit_box.SetStringSelection(new_mean_fit) self.parent.onSelect_mean_fit_box(event) ###################################Button Functions################################## def on_select_stats_button(self,event): """ """ i = self.switch_stats_button.GetValue() self.parent.switch_stats_button.SetValue(i) self.parent.update_high_level_stats() def add_highlighted_fits(self, evnet): """ adds a new interpretation to each specimen highlighted in logger if multiple interpretations are highlighted of the same specimen only one new interpretation is added @param: event -> the wx.ButtonEvent that triggered this function """ specimens = [] next_i = self.logger.GetNextSelected(-1) if next_i == -1: return while next_i != -1: fit,specimen = self.fit_list[next_i] if specimen in specimens: next_i = self.logger.GetNextSelected(next_i) continue else: specimens.append(specimen) next_i = self.logger.GetNextSelected(next_i) for specimen in specimens: self.add_fit_to_specimen(specimen) self.update_editor() self.parent.update_selection() def add_fit_to_all(self,event): for specimen in self.parent.specimens: self.add_fit_to_specimen(specimen) self.update_editor() self.parent.update_selection() def add_fit_to_specimen(self,specimen): if specimen not in self.parent.pmag_results_data['specimens']: self.parent.pmag_results_data['specimens'][specimen] = [] new_name = self.name_box.GetLineText(0) new_color = self.color_box.GetValue() new_tmin = self.tmin_box.GetValue() new_tmax = self.tmax_box.GetValue() if not new_name: next_fit = str(len(self.parent.pmag_results_data['specimens'][specimen]) + 1) while ("Fit " + next_fit) in [x.name for x in self.parent.pmag_results_data['specimens'][specimen]]: next_fit = str(int(next_fit) + 1) new_name = ("Fit " + next_fit) if not new_color: next_fit = str(len(self.parent.pmag_results_data['specimens'][specimen]) + 1) new_color = self.parent.colors[(int(next_fit)-1) % len(self.parent.colors)] else: new_color = self.color_dict[new_color] if not new_tmin: new_tmin = None if not new_tmax: new_tmax = None if new_name in [x.name for x in self.parent.pmag_results_data['specimens'][specimen]]: print(('-E- interpretation called ' + new_name + ' already exsists for specimen ' + specimen)) return self.parent.add_fit(specimen, new_name, new_tmin, new_tmax, color=new_color,suppress_warnings=True) def delete_highlighted_fits(self, event): """ iterates through all highlighted fits in the logger of this object and removes them from the logger and the Zeq_GUI parent object @param: event -> the wx.ButtonEvent that triggered this function """ next_i = -1 deleted_items = [] while True: next_i = self.logger.GetNextSelected(next_i) if next_i == -1: break deleted_items.append(next_i) deleted_items.sort(reverse=True) for item in deleted_items: self.delete_entry(index=item) self.parent.update_selection() def delete_entry(self, fit = None, index = None): """ deletes the single item from the logger of this object that corrisponds to either the passed in fit or index. Note this function mutaits the logger of this object if deleting more than one entry be sure to pass items to delete in from highest index to lowest or else odd things can happen. @param: fit -> Fit object to delete from this objects logger @param: index -> integer index of the entry to delete from this objects logger """ if type(index) == int and not fit: fit,specimen = self.fit_list[index] if fit and type(index) == int: for i, (f,s) in enumerate(self.fit_list): if fit == f: index,specimen = i,s break if index == self.current_fit_index: self.current_fit_index = None if fit not in self.parent.pmag_results_data['specimens'][specimen]: print(("cannot remove item (entry #: " + str(index) + ") as it doesn't exist, this is a dumb bug contact devs")) self.logger.DeleteItem(index) return self.parent.pmag_results_data['specimens'][specimen].remove(fit) del self.fit_list[index] self.logger.DeleteItem(index) def apply_changes(self, event): """ applies the changes in the various attribute boxes of this object to all highlighted fit objects in the logger, these changes are reflected both in this object and in the Zeq_GUI parent object. @param: event -> the wx.ButtonEvent that triggered this function """ new_name = self.name_box.GetLineText(0) new_color = self.color_box.GetValue() new_tmin = self.tmin_box.GetValue() new_tmax = self.tmax_box.GetValue() next_i = -1 changed_i = [] while True: next_i = self.logger.GetNextSelected(next_i) if next_i == -1: break specimen = self.fit_list[next_i][1] fit = self.fit_list[next_i][0] if new_name: if new_name not in [x.name for x in self.parent.pmag_results_data['specimens'][specimen]]: fit.name = new_name if new_color: fit.color = self.color_dict[new_color] #testing not_both = True if new_tmin and new_tmax: if fit == self.parent.current_fit: self.parent.tmin_box.SetStringSelection(new_tmin) self.parent.tmax_box.SetStringSelection(new_tmax) fit.put(specimen,self.parent.COORDINATE_SYSTEM, self.parent.get_PCA_parameters(specimen,fit,new_tmin,new_tmax,self.parent.COORDINATE_SYSTEM,fit.PCA_type)) not_both = False if new_tmin and not_both: if fit == self.parent.current_fit: self.parent.tmin_box.SetStringSelection(new_tmin) fit.put(specimen,self.parent.COORDINATE_SYSTEM, self.parent.get_PCA_parameters(specimen,fit,new_tmin,fit.tmax,self.parent.COORDINATE_SYSTEM,fit.PCA_type)) if new_tmax and not_both: if fit == self.parent.current_fit: self.parent.tmax_box.SetStringSelection(new_tmax) fit.put(specimen,self.parent.COORDINATE_SYSTEM, self.parent.get_PCA_parameters(specimen,fit,fit.tmin,new_tmax,self.parent.COORDINATE_SYSTEM,fit.PCA_type)) changed_i.append(next_i) offset = 0 for i in changed_i: i -= offset v = self.update_logger_entry(i) if v == "s": offset += 1 self.parent.update_selection() ###################################Canvas Functions################################## def scatter(self,*args,**kwargs): # args_corrected = self.eqarea.transAxes.transform(vstack(args).T) # x,y = args_corrected.T return self.eqarea.scatter(*args,**kwargs) def plot(self,*args,**kwargs): # args_corrected = self.eqarea.transAxes.transform(vstack(args).T) # x,y = args_corrected.T return self.eqarea.plot(*args,**kwargs) def write(self,text): return self.eqarea.text(-1.2,1.15,text,{'family':self.font_type, 'fontsize':10*self.GUI_RESOLUTION, 'style':'normal','va':'center', 'ha':'left' }) def draw_net(self): draw_net(self.eqarea) def draw(self): self.toolbar.home() self.eqarea.set_xlim(-1., 1.) self.eqarea.set_ylim(-1., 1.) self.eqarea.axes.set_aspect('equal') self.eqarea.axis('off') self.canvas.draw() def pan_zoom_high_equalarea(self,event): """ Uses the toolbar for the canvas to change the function from zoom to pan or pan to zoom @param: event -> the wx.MouseEvent that triggered this funciton """ if event.LeftIsDown() or event.ButtonDClick(): return elif self.high_EA_setting == "Zoom": self.high_EA_setting = "Pan" try: self.toolbar.pan('off') except TypeError: pass elif self.high_EA_setting == "Pan": self.high_EA_setting = "Zoom" try: self.toolbar.zoom() except TypeError: pass else: self.high_EA_setting = "Zoom" try: self.toolbar.zoom() except TypeError: pass def home_high_equalarea(self,event): """ returns high equal area to it's original position @param: event -> the wx.MouseEvent that triggered the call of this function @alters: toolbar setting """ self.toolbar.home() def on_change_high_mouse_cursor(self,event): """ If mouse is over data point making it selectable change the shape of the cursor @param: event -> the wx Mouseevent for that click """ if self.show_box.GetValue() != "specimens": return if not self.parent.high_EA_xdata or not self.parent.high_EA_ydata: return pos=event.GetPosition() width, height = self.canvas.get_width_height() pos[1] = height - pos[1] xpick_data,ypick_data = pos xdata_org = self.parent.high_EA_xdata ydata_org = self.parent.high_EA_ydata data_corrected = self.eqarea.transData.transform(vstack([xdata_org,ydata_org]).T) xdata,ydata = data_corrected.T xdata = list(map(float,xdata)) ydata = list(map(float,ydata)) e = 4e0 if self.high_EA_setting == "Zoom": self.canvas.SetCursor(wx.Cursor(wx.CURSOR_CROSS)) else: self.canvas.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) for i,(x,y) in enumerate(zip(xdata,ydata)): if 0 < sqrt((x-xpick_data)**2. + (y-ypick_data)**2.) < e: self.canvas.SetCursor(wx.Cursor(wx.CURSOR_HAND)) break event.Skip() def on_equalarea_high_select(self,event): self.parent.on_equalarea_high_select(event,fig = self.eqarea, canvas = self.canvas) ###############################Menu Functions###################################### def on_menu_help(self,event): """ Toggles the GUI's help mode which allows user to click on any part of the dialog and get help @param: event -> wx.MenuEvent that triggers this function """ self.helper.BeginContextHelp(None) ###############################Window Functions###################################### def on_close_edit_window(self, event): """ the function that is triggered on the close of the interpretation editor window @param: event -> wx.WindowEvent that triggered this function """ self.parent.ie_open = False self.Destroy()
class graphManager(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=u"ReGenesis", pos=wx.DefaultPosition, size=wx.Size(MAXWIDTH, MAXHEIGHT), style=wx.DEFAULT_FRAME_STYLE & ~ (wx.RESIZE_BORDER | wx.MAXIMIZE_BOX)) self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) self.sizer = wx.BoxSizer(wx.VERTICAL) self.renderer = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL) self.sizer.Add(self.renderer, 1, wx.EXPAND | wx.ALL, 5) # toolbar self.toolbar = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL) self.toolbar.SetMinSize(wx.Size(1200, 40)) self.toolbar.SetMaxSize(wx.Size(1200,40)) self.toolbarSizer = wx.BoxSizer(wx.HORIZONTAL) # adding buttons self.homeButton = wx.Button(self.toolbar, wx.ID_ANY, u"Reset", wx.DefaultPosition, wx.DefaultSize, 0) self.toolbarSizer.Add(self.homeButton, 0, wx.ALL, 5) self.backButton = wx.Button(self.toolbar, wx.ID_ANY, u"Undo", wx.DefaultPosition, wx.DefaultSize, 0) self.toolbarSizer.Add(self.backButton, 0, wx.ALL, 5) self.forwardButton = wx.Button(self.toolbar, wx.ID_ANY, u"Redo", wx.DefaultPosition, wx.DefaultSize, 0) self.toolbarSizer.Add(self.forwardButton, 0, wx.ALL, 5) self.panButton = wx.Button(self.toolbar, wx.ID_ANY, u"Pan", wx.DefaultPosition, wx.DefaultSize, 0) self.toolbarSizer.Add(self.panButton, 0, wx.ALL, 5) self.zoomButton = wx.Button(self.toolbar, wx.ID_ANY, u"Zoom", wx.DefaultPosition, wx.DefaultSize, 0) self.toolbarSizer.Add(self.zoomButton, 0, wx.ALL, 5) self.appearanceButton = wx.Button(self.toolbar, wx.ID_ANY, u"Edit Appearance", wx.DefaultPosition, wx.DefaultSize, 0) self.toolbarSizer.Add(self.appearanceButton, 0, wx.ALL, 5) self.saveButton = wx.Button(self.toolbar, wx.ID_ANY, u"Save", wx.DefaultPosition, wx.DefaultSize, 0) self.toolbarSizer.Add(self.saveButton, 0, wx.ALL, 5) self.loadButton = wx.Button(self.toolbar, wx.ID_ANY, u"Load", wx.DefaultPosition, wx.DefaultSize, 0) self.toolbarSizer.Add(self.loadButton, 0, wx.ALL, 5) self.exportButton = wx.Button(self.toolbar, wx.ID_ANY, u"Export", wx.DefaultPosition, wx.DefaultSize, 0) self.toolbarSizer.Add(self.exportButton, 0, wx.ALL, 5) self.toolbar.SetSizer(self.toolbarSizer) self.toolbar.Layout() self.toolbarSizer.Fit(self.toolbar) self.sizer.Add(self.toolbar, 1, wx.EXPAND | wx.ALL, 5) self.SetSizer(self.sizer) self.Layout() self.MenuBar = wx.MenuBar(0) #new graph menu options self.newGraph = wx.Menu() self.newPCA = wx.MenuItem(self.newGraph, wx.ID_ANY, u"PCA Graph", wx.EmptyString, wx.ITEM_NORMAL) self.newGraph.Append(self.newPCA) self.newAdmixture = wx.MenuItem(self.newGraph, wx.ID_ANY, u"Admixture Graph", wx.EmptyString, wx.ITEM_NORMAL) self.newGraph.Append(self.newAdmixture) self.MenuBar.Append(self.newGraph, u"New Graph") #Manage Graphs Menu self.manageGraphs = wx.Menu() self.saveGraph = wx.MenuItem(self.manageGraphs, wx.ID_ANY, u"Save", wx.EmptyString, wx.ITEM_NORMAL) self.manageGraphs.Append(self.saveGraph) self.loadGraph = wx.MenuItem(self.manageGraphs, wx.ID_ANY, u"Load", wx.EmptyString, wx.ITEM_NORMAL) self.manageGraphs.Append(self.loadGraph) self.exportGraph = wx.MenuItem(self.manageGraphs, wx.ID_ANY, u"Export", wx.EmptyString, wx.ITEM_NORMAL) self.manageGraphs.Append(self.exportGraph) self.MenuBar.Append(self.manageGraphs, u"Manage Graphs") self.SetMenuBar(self.MenuBar) self.Centre(wx.BOTH) # Connect Events # New graph events self.Bind(wx.EVT_MENU, self.newPCAOnMenuSelection, id=self.newPCA.GetId()) self.Bind(wx.EVT_MENU, self.newAdmixtureOnMenuSelection, id=self.newAdmixture.GetId()) # Manage graph events self.Bind(wx.EVT_MENU, self.saveGraphOnMenuSelection, id=self.saveGraph.GetId()) self.Bind(wx.EVT_MENU, self.loadGraphOnMenuSelection, id=self.loadGraph.GetId()) self.Bind(wx.EVT_MENU, self.exportGraphOnMenuSelection, id=self.exportGraph.GetId()) # Toolbar Events self.saveButton.Bind(wx.EVT_BUTTON, self.saveGraphOnMenuSelection) self.loadButton.Bind(wx.EVT_BUTTON, self.loadGraphOnMenuSelection) self.exportButton.Bind(wx.EVT_BUTTON, self.onExportButtonClick) self.zoomButton.Bind(wx.EVT_BUTTON, self.onZoomButtonClick) self.appearanceButton.Bind(wx.EVT_BUTTON, self.onAppearanceButtonClick) self.homeButton.Bind(wx.EVT_BUTTON, self.onHomeButtonClick) self.panButton.Bind(wx.EVT_BUTTON, self.onPanButtonClick) self.forwardButton.Bind(wx.EVT_BUTTON, self.onForwardButtonClick) self.backButton.Bind(wx.EVT_BUTTON, self.onBackButtonClick) # Variable to store which graph type is currently loaded for saving purposes self.graphType = None def __del__(self): pass def createFigure(self): """ Creates figure or clears figure and adds a new subplot to the figure. """ # checking if figure exists to avoid reinitializing if not hasattr(self, 'figure'): self.figure = mpl.figure.Figure(figsize=(FIGUREWIDTH, FIGUREHEIGHT), dpi=FIGUREDPI) else: self.figure.clf() # add subplot to newly created/cleared figure self.axes = self.figure.add_subplot(111) def showGraph(self): """ Produces key and canvas. """ self.axes.legend() self.canvas = FigureCanvas(self.renderer, wx.ID_ANY, self.figure) # Initialises navToolbar instance to use built in functions for custom toolbar if not hasattr(self, "navToolbar"): locale = wx.Locale(wx.LANGUAGE_ENGLISH) self.navToolbar = NavigationToolbar(self.canvas) self.navToolbar.Hide() def CreatePCAPlot(self, data): """ Creates pca graph and draws it. """ self.graph = pcaGraph(data) pcaData = self.graph.findPcaData(True) self.plotPcaData(pcaData) def CreateAdmixturePlot(self, data): """ Creates admixture graph and draws it. """ self.graph = admixGraph(data) admixData = self.graph.genDataDictionary() self.plotAdmixData(admixData) def plotPcaData(self, pcaData): """ Plots PCA data to figure. """ pcaAppearance.groupNames = self.graph.getGroups() pcaAppearance.groupColours = self.graph.getColours() pcaAppearance.groupShapes = self.graph.getShapes() pcaAppearance.groupSizes = self.graph.getSize() pcaAppearance.title = self.graph.getTitle() pcaAppearance.hasGrid = self.graph.getHasGrid() pcaAppearance.hasLabels = self.graph.getHasLabels() self.createFigure() for group in pcaData: x = pcaData[group]['x'] y = pcaData[group]['y'] # getting the colours of the groups colourList = self.graph.getColours() # getting the shapes of the groups shapeList = self.graph.getShapes() #getting the sizes of the groups sizeList = self.graph.getSize() #getting the title of the graph title = self.graph.getTitle() hasGrid = self.graph.getHasGrid() hasLabels = self.graph.getHasLabels() # plotting the graph self.axes.scatter(x, y, label=group, s=sizeList[group], color=colourList[group], marker=shapeList[group]) self.axes.set_title(title) if(hasGrid): self.axes.grid() if(hasLabels): self.axes.set_xlabel(self.graph.getXLabel()) self.axes.set_ylabel(self.graph.getYLabel()) self.showGraph() return def plotAdmixData(self, admixData): """ Plots admixture data to figure. """ groups = list(admixData.keys()) ancestryLabels = list(admixData[groups[0]].keys()) individualCount = 0 groupCenters = [] # creating figure self.createFigure() fullRatios = [] self.axes.set_xticklabels([]) for group in admixData: ratios = [] for ancestry in admixData[group]: anc = admixData[group][ancestry] ratios.append(anc) numAncestries = len(ratios) numIndividuals = len(ratios[0]) # Normalising ratios so they add up to 1 tempRatios = list(zip(*ratios)) sums = list(map(sum, tempRatios)) for ancestry in ratios: for i in range(len(ancestry)): ancestry[i] /= sums[i] # storing values used for separating groups at presentation groupCenters.append(individualCount + (numIndividuals/2)) individualCount += numIndividuals self.axes.axvline(individualCount, color='w') if len(fullRatios) > 0: for i in range(len(fullRatios)): fullRatios[i].extend(ratios[i]) else: fullRatios = ratios # plotting bars indexes = [i for i in range(individualCount)] self.axes.bar(indexes, fullRatios[0], 1, label=ancestryLabels[0]) # barBottom keeps track of the current height of each bar so subsequent bars can be plotted above barBottom = [0 for i in range(individualCount)] for i in range(0, numAncestries - 1): # increasing height of barBottom barBottom = [x + y for x, y in zip(barBottom, fullRatios[i])] self.axes.bar(indexes, fullRatios[i + 1], 1, bottom=barBottom, label=ancestryLabels[i + 1]) # setting labels for better readability self.axes.set_xticks(groupCenters) self.axes.set_xticklabels(groups) self.showGraph() # Event Handlers def newPCAOnMenuSelection(self, event): """ Event handler for new PCA menu selection. """ self.child = pcaCreator(self) self.Disable() self.child.ShowModal() if self.child.result == "CANCEL": event.Skip() elif self.child.result == "CONFIRM": self.CreatePCAPlot(self.child._dataDict) def newAdmixtureOnMenuSelection(self, event): """ Event handler for new Admixture menu selection. """ self.child = admixCreator(self) self.Disable() self.child.ShowModal() if self.child.result == "CANCEL": event.Skip() elif self.child.result == "CONFIRM": self.CreateAdmixturePlot(self.child._dataDict) def saveGraphOnMenuSelection(self, event): """ Event handler for save Graph menu selection. """ with wx.FileDialog(self, "Save Graph file", wildcard="Regenesis Graph File files (*.rgf)|*.rgf", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return # the user changed their mind # save the current contents in the file pathname = fileDialog.GetPath() try: with open(pathname, 'w', encoding='utf-8') as file: self.doSaveData(file) except IOError: wx.LogError("Cannot save current data in file '%s'." % pathname) def doSaveData(self, f): """ Saves project state to file. """ # find dictionary of values to plot if self.graph.getGraphType() == 'admix': data = self.graph.getSaveFileData() # Add a graph type key to the data dict data.update({"GraphType" : 'admix'}) elif self.graph.getGraphType() == 'pca': data = self.graph.getSaveFileData() # Add a graph type key to the data dict data.update({"GraphType" : "pca"}) json.dump(data, f, ensure_ascii=False) f.close() def loadGraphOnMenuSelection(self, event): """ Event handler for load Graph menu selection. """ # otherwise ask the user what new file to open with wx.FileDialog(self, "Load Graph file", wildcard="Regenesis Graph File files (*.rgf)|*.rgf", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return # the user changed their mind # Proceed loading the file chosen by the user pathname = fileDialog.GetPath() try: with open(pathname, 'r') as file: self.doLoadData(file) except IOError: wx.LogError("Cannot open file '%s'." % file) def doLoadData(self,f): """ Loads project state from file. """ data = json.load(f) if data.get("GraphType") == 'admix': # Delete the Graph Type key from the data dict del data["GraphType"] self.CreateAdmixturePlot(data) elif data.get("GraphType") == 'pca': # Delete the Graph Type key from the data dict del data["GraphType"] self.CreatePCAPlot(data) f.close() def exportGraphOnMenuSelection(self, event): """ Exports graph as image. """ self.navToolbar.save_figure() def onZoomButtonClick(self, event): """ Activates zoom mode on graph. """ self.navToolbar.zoom() def onAppearanceButtonClick(self, event): """ Opens appearance editor. """ if self.graph.getGraphType() == 'pca': self.child = pcaAppearance(self) self.Disable() self.child.ShowModal() else: msgDlg = wx.MessageDialog(parent=self, message="Appearance editing functionality for admixture graph still to come in future releases", caption="Edit Admixture graph appearance", style=wx.ICON_INFORMATION) val = msgDlg.ShowModal() msgDlg.Show() if self.child.result == "CANCEL": event.Skip() elif self.child.result == "CONFIRM": if self.graph.getGraphType() == 'pca': #re-plotting the graph with the newly chosen colours newColours = self.child.GetColours() self.graph.setColours(newColours) #re-plotting the graph with the newly chosen shape newShapes = self.child.GetShapes() self.graph.setShapes(newShapes) #re-plotting the graph with the new group size newSize = self.child.GetSize() self.graph.setSize(newSize) #re-plotting graph with new title newTitle = self.child.GetTitle() self.graph.setTitle(newTitle) #re-plotting graph with grid settings grid = self.child.GetHasGrid() self.graph.setHasGrid(grid) #re-plotting graph with labels settings labels = self.child.GetHasLabels() self.graph.setHasLabels(labels) pcaData = self.graph.findPcaData(False) self.plotPcaData(pcaData) #self.CreatePCAPlot(self.child._dataDict) elif self.graph.getGraphType() == "admix": wx.MessageDialog(None, "Admixture Appearance editing to b") def onHomeButtonClick(self, event): """ Resets graph panning and zooming """ self.navToolbar.home() def onPanButtonClick(self, event): """ Activates Pan mode on graph. """ self.navToolbar.pan() def onConfigureButtonClick(self, event): self.navToolbar.configure_subplots() def onBackButtonClick(self, event): """ Undoes latest panning or zooming change. """ self.navToolbar.back() def onForwardButtonClick(self, event): """ Redoes latest undone panning or zooming change. """ self.navToolbar.forward() def onExportButtonClick(self, event): """ Exports graph as image. """ self.navToolbar.save_figure()
class ImageLabeler(wx.App): ''' The Main Application Class ''' def __init__(self, starting_image=None, image_dir=None, conf_dir=None): wx.App.__init__(self) self.labeler_dir = labeler.__path__[0] self.starting_image = starting_image self.image_dir = image_dir # Mode that the labeler should use: single or batch self.labeler_mode = "single" # Frame that will contain image and grid self.frame = wx.Frame(None, title='Image Display') #What is the display of the monitor self.monitor_size = wx.GetDisplaySize() # Where does our code live self.bin_dir = os.path.dirname(os.path.abspath(__file__)) self.CanvasPanel = wx.Panel(self.frame, style=wx.BORDER_SUNKEN | wx.CLOSE_BOX | wx.SYSTEM_MENU | wx.CAPTION) self.CanvasPanel.SetBackgroundColour("dark gray") self.frame.Bind(wx.EVT_CLOSE, self.OnFileExit) # Intitialise the matplotlib figure self.figure = Figure() # Create an axes, turn off the labels and add them to the figure self.axes = plt.Axes(self.figure, [0, 0, 1, 1]) self.axes.set_axis_off() self.figure.add_axes(self.axes) # Add the figure to the wxFigureCanvas self.canvas = FigureCanvas(self.CanvasPanel, -1, self.figure) self.toolbar = NavigationToolbar(self.canvas) self.toolbar.Realize() self.toolbar.Hide() # What mode is the cursor in: bb,toolbar self.cursor_mode = "nobb" # Connect the mouse events to their relevant callbacks self.canvas.mpl_connect('button_press_event', self.OnLeftDown) self.canvas.mpl_connect('button_release_event', self.OnLeftUp) self.canvas.mpl_connect('motion_notify_event', self.OnMotion) self.canvas.mpl_connect('key_press_event', self.OnKeyDown) # Lock to stop the motion event from behaving badly when the mouse isn't pressed self.frame.pressed = False # Setting up the file menu filemenu = wx.Menu() menuAbout = filemenu.Append(wx.ID_ABOUT, "&About", "Information About This Program") menuOpenGrid = filemenu.Append(wx.ID_FILE, "&Open Grid", "Open File Containing Bounding Boxes") menuOpenImage = filemenu.Append(wx.ID_OPEN, "&Open Image", "Open Image File") menuSaveGrid = filemenu.Append(wx.ID_SAVE, "&Save Grid", "Save Bounding Boxes to CSV File") menuSaveImage = filemenu.Append(wx.ID_SAVEAS, "&Save Image", "Save Image") menuExit = filemenu.Append(wx.ID_EXIT, "&Exit", "Exit Image Labeler") # Setting up the models menu configmenu = wx.Menu() menuConfigModel = configmenu.Append(wx.ID_ABOUT, "&Models", "Configure Custom Models") # Creating the menubar. menuBar = wx.MenuBar() menuBar.Append(filemenu, "&File") # Adding the "filemenu" to the MenuBar menuBar.Append(configmenu, "&Config") self.frame.SetMenuBar( menuBar) # Adding the MenuBar to the Frame content. # Set events for menuBar things self.frame.Bind(wx.EVT_MENU, self.OnFileAbout, menuAbout) self.frame.Bind(wx.EVT_MENU, self.OnFileOpen, menuOpenImage) self.frame.Bind(wx.EVT_MENU, self.OnImportGrid, menuOpenGrid) self.frame.Bind(wx.EVT_MENU, self.OnFileExit, menuExit) self.frame.Bind(wx.EVT_MENU, self.OnSaveGrid, menuSaveGrid) self.frame.Bind(wx.EVT_MENU, self.OnSaveImage, menuSaveImage) self.frame.Bind(wx.EVT_MENU, self.OnConfigModel, menuConfigModel) #Keep track of how many images you have displayed self.imagecounter = 0 #Define where this program should find images if self.image_dir == None: self.image_dir = os.getcwd() # Get list of image files in the image_dir self.images_obj = get_list_files(self.image_dir) if self.images_obj != None: self.labeler_mode = "batch" else: print( "Warning: no images found in image path or current directory.") #What image will we be starting on if self.starting_image != None: #use the one specified on the command line. self.imagepath = self.starting_image elif self.labeler_mode == "single": #Just use the default application image print("Info: Using application default image:", self.labeler_dir + "/image.jpg") self.imagepath = self.labeler_dir + "/image.jpg" elif self.labeler_mode == "batch": print( "Info: Starting labeler in batch mode, multiple images detected." ) #in batch mode start with the first image self.imagepath = self.images_obj[0]['path'] # Create Panel to display Bounding Box Coordinates self.BBPanel = wx.Panel(self.frame, style=wx.BORDER_SUNKEN | wx.CLOSE_BOX | wx.SYSTEM_MENU | wx.CAPTION) self.BBPanel.SetBackgroundColour("dark gray") # Create the Grid to Hold the Coordinates self.BBGrid = gridlib.Grid(self.BBPanel) self.BBGrid.CreateGrid(100, 5) self.BBGrid.SetColLabelValue(0, "X1") self.BBGrid.SetColLabelValue(1, "Y1") self.BBGrid.SetColLabelValue(2, "X2") self.BBGrid.SetColLabelValue(3, "Y2") self.BBGrid.SetColLabelValue(4, "Label") BBsizer = wx.BoxSizer(wx.VERTICAL) BBsizer.Add(self.BBGrid, 1, wx.EXPAND | wx.ALL) # Do Some things when the mouse clicks inside of the Grid self.BBGrid.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.OnGridLeft) # Do some things when delete key is pressed inside of the grid self.BBGrid.Bind(wx.EVT_KEY_DOWN, self.OnGridDelete) # Get rid of row labels self.BBGrid.SetRowLabelSize(0) # Set all columns to read only, except the Label column set_grid_edit(self) #self.BBGrid.EnableEditing(False) self.BBPanel.SetSizer(BBsizer) # Create Panel for Image Controls self.ControlPanel = wx.Panel(self.frame, style=wx.BORDER_SUNKEN | wx.CLOSE_BOX | wx.SYSTEM_MENU | wx.CAPTION) self.ControlBox = wx.BoxSizer(wx.VERTICAL) self.ControlBox.Add(self.ControlPanel) self.ControlPanel.SetBackgroundColour("dark gray") # Create Buttons to help label image self.button_list = [] self.selected_button = "HOME" self.sibut = wx.Button(self.ControlPanel, -1, size=(50, 50), pos=(5, 5), name="zoom") zoom_img = wx.Image(self.labeler_dir + '/icons/zoom.png', wx.BITMAP_TYPE_ANY) zoom_img = zoom_img.Scale(20, 20) self.sibut.SetBitmap(wx.Bitmap(zoom_img)) self.sibut.Bind(wx.EVT_BUTTON, self.zoom) self.button_list.append(self.sibut) self.hmbut = wx.Button(self.ControlPanel, -1, size=(50, 50), pos=(60, 5), name="home") home_img = wx.Image(self.labeler_dir + '/icons/home.png', wx.BITMAP_TYPE_ANY) home_img = home_img.Scale(20, 20) self.hmbut.SetBitmap(wx.Bitmap(home_img)) self.hmbut.Bind(wx.EVT_BUTTON, self.home) self.button_list.append(self.hmbut) self.hibut = wx.Button(self.ControlPanel, -1, size=(50, 50), pos=(115, 5), name="pan") pan_img = wx.Image(self.labeler_dir + '/icons/pan.png', wx.BITMAP_TYPE_ANY) pan_img = pan_img.Scale(20, 20) self.hibut.SetBitmap(wx.Bitmap(pan_img)) self.hibut.Bind(wx.EVT_BUTTON, self.pan) self.button_list.append(self.hibut) self.plotbut = wx.Button(self.ControlPanel, -1, size=(50, 50), pos=(170, 5), name="bbox") box_img = wx.Image(self.labeler_dir + '/icons/bbox.png', wx.BITMAP_TYPE_ANY) box_img = box_img.Scale(20, 20) self.plotbut.SetBitmap(wx.Bitmap(box_img)) self.plotbut.Bind(wx.EVT_BUTTON, self.plot) self.button_list.append(self.plotbut) # Create Panel Controls for Dataset movement self.BatchPanel = wx.Panel(self.frame, style=wx.BORDER_SUNKEN | wx.CLOSE_BOX | wx.SYSTEM_MENU | wx.CAPTION) self.BatchBox = wx.BoxSizer(wx.VERTICAL) self.BatchBox.Add(self.BatchPanel) self.BatchPanel.SetBackgroundColour("dark gray") self.prevbut = wx.Button(self.BatchPanel, -1, size=(50, 50), pos=(7, 5)) box_img = wx.Image(self.labeler_dir + '/icons/left_arrow.png', wx.BITMAP_TYPE_ANY) box_img = box_img.Scale(20, 20) self.prevbut.SetBitmap(wx.Bitmap(box_img)) self.prevbut.Bind(wx.EVT_BUTTON, self.prev) self.nextbut = wx.Button(self.BatchPanel, -1, size=(50, 50), pos=(60, 5)) box_img = wx.Image(self.labeler_dir + '/icons/right_arrow.png', wx.BITMAP_TYPE_ANY) box_img = box_img.Scale(20, 20) self.nextbut.SetBitmap(wx.Bitmap(box_img)) self.nextbut.Bind(wx.EVT_BUTTON, self.next) if self.labeler_mode == "single": #Disable button in single mode self.nextbut.Disable() self.prevbut.Disable() # Create Panel for Grid controls self.GridControlPanel = wx.Panel(self.frame, style=wx.BORDER_SUNKEN | wx.CLOSE_BOX | wx.SYSTEM_MENU | wx.CAPTION) self.GridControlBox = wx.BoxSizer(wx.VERTICAL) self.GridControlBox.Add(self.GridControlPanel) self.GridControlPanel.SetBackgroundColour("dark gray") # Button to import csv file with bounding boxes self.imbut = wx.Button(self.GridControlPanel, -1, size=(50, 50), pos=(5, 5)) imp_img = wx.Image(self.labeler_dir + '/icons/import.png', wx.BITMAP_TYPE_ANY) imp_img = imp_img.Scale(20, 20) self.imbut.SetBitmap(wx.Bitmap(imp_img)) self.imbut.Bind(wx.EVT_BUTTON, self.OnImportGrid) # Button to save grid to csv file self.grsavebut = wx.Button(self.GridControlPanel, -1, size=(50, 50), pos=(60, 5)) save_img = wx.Image(self.labeler_dir + '/icons/filesave.png', wx.BITMAP_TYPE_ANY) save_img = save_img.Scale(20, 20) self.grsavebut.SetBitmap(wx.Bitmap(save_img)) self.grsavebut.Bind(wx.EVT_BUTTON, self.save_grid) # Button to delete grid and bounding boxes self.grdelbut = wx.Button(self.GridControlPanel, -1, size=(50, 50), pos=(115, 5)) del_img = wx.Image(self.labeler_dir + '/icons/delete_all.png', wx.BITMAP_TYPE_ANY) del_img = del_img.Scale(20, 20) self.grdelbut.SetBitmap(wx.Bitmap(del_img)) self.grdelbut.Bind(wx.EVT_BUTTON, self.clear_bb) # Are we moving the rectangle or creating a new one self.is_moving = False # Hold list of rectangle objects self.rect_obj_list = [] self.rect_labels = [] # A Statusbar i the bottom of the window self.frame.CreateStatusBar() self.frame.Show(True) self.FirstImage() # Frame for image transformations self.TransFrame = TransFrame(None, self) # Frame for image segmentation self.SegFrame = SegmentFrame(None, self) # Frame for configuring models self.ModelFrame = None def OnGridLeft(self, event): ''' Action taken when left click happens on grid ''' row = event.GetRow() self.selected_rect = row self.change_rect_color() highlight_row(self, row) def OnGridDelete(self, event): ''' Delete row in grid ''' row = self.BBGrid.GetGridCursorRow() col = self.BBGrid.GetGridCursorCol() if event.GetKeyCode() == wx.WXK_DELETE: self.OnDelete() def toggle_cursor_mode(self, button, name): ''' Change cursor_mode between bb and rest toolbar Hides active button ''' for butt in self.button_list: if button == butt: if name == "home": # This one doesn't need to be hidden next else: butt.Hide() else: butt.Show() def zoom(self, event): ''' Use Matplotlibs zoom tool ''' self.cursor_mode = "nobb" self.toggle_cursor_mode(self.sibut, "zoom") self.toolbar.zoom() # Toggle off other buttons if self.selected_button == 'HOME': self.toolbar.home() elif self.selected_button == 'PAN': self.toolbar.pan() elif self.selected_button == 'PLOT`': self.toolbar.plot() self.selected_button = "ZOOM" def toggle_off_mode(self): ''' Turn off any button that is selected ''' # Toggle off other buttons if self.selected_button == 'ZOOM': self.toolbar.zoom() elif self.selected_button == 'PAN': self.toolbar.pan() elif self.selected_button == 'PLOT`': self.toolbar.plot() elif self.selected_button == 'HOME': self.toolbar.home() def home(self, event): ''' Return view back to original position ''' self.cursor_mode = "nobb" self.toggle_cursor_mode(self.hmbut, "home") self.toolbar.home() # Reset axes so they don't get messed up when zooming self.axes.set_xbound(0, self.image_shape[1]) self.axes.set_ybound(0, self.image_shape[0]) self.toggle_off_mode() self.selected_button = "HOME" def pan(self, event): ''' Uses Matplotlibs pan tool ''' self.cursor_mode = "nobb" self.toggle_cursor_mode(self.hibut, "pan") self.toolbar.pan() self.toggle_off_mode() self.selected_button = "PAN" def plot(self, event): ''' Draw a rectangle on the canvas ''' self.cursor_mode = "bb" self.toggle_cursor_mode(self.plotbut, "plot") # Set Crosshair as mouse cursor. self.toggle_off_mode() self.selected_button = "PLOT" def next(self, event): ''' Move to next image ''' self.cursor_mode = "nobb" self.toggle_cursor_mode(self.nextbut, "next") # Find Out which image you are currently on in self.images_obj i = 0 for obj in self.images_obj: if obj['path'] == self.imagepath: self.cur_obj_num = i break i += 1 if self.cur_obj_num + 1 == len(self.images_obj): self.user_error("There are no more images left to work on.") self.cur_obj_num = 0 return 0 self.cur_obj_num += 1 # Now display the new image self.imagepath = self.images_obj[self.cur_obj_num]['path'] self.NewImage() def prev(self, event): ''' Move to previous image ''' self.cursor_mode = "nobb" self.toggle_cursor_mode(self.prevbut, "prev") # Find Out which image you are currently on in self.images_obj i = 0 for obj in self.images_obj: if obj['path'] == self.imagepath: self.cur_obj_num = i break i += 1 if self.cur_obj_num == 0: self.user_error("You are on the first image.") return 0 self.cur_obj_num -= 1 # Now display the new image self.imagepath = self.images_obj[self.cur_obj_num]['path'] self.NewImage() def OnFileExit(self, event): ''' Close every frame in the app. ''' self.frame.Destroy() self.TransFrame.Close() self.SegFrame.Close() if self.ModelFrame != None: self.ModelFrame.Close() def OnImportGrid(self, event): ''' Choose CSV file with coordinates to import. ''' with wx.FileDialog(self.frame, "Import CSV File", wildcard="*.csv", style=wx.FD_OPEN) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return # the user changed their mind # Get Pathname pathname = fileDialog.GetPath() # Read file into list new_coords = import_grid_csv(self, pathname) # Loop through coordinates and draw rectangle for coord in new_coords: self.draw_rect(coord) self.canvas.draw() def OnConfigModel(self, event): self.ModelFrame = ModelFrame(None, self) def draw_rect(self, rect): if len(rect) > 4: x0, y0, x1, y1, label = rect self.rect_labels.append(label) else: x0, y0, x1, y1 = rect self.rect_labels.append("") x0 = int(x0) y0 = int(y0) x1 = int(x1) y1 = int(y1) if x0 < 0: x0 = 0 if x1 < 0: x1 = 0 if y0 < 0: y0 = 0 if y1 < 0: y1 = 0 max_height = self.image_shape[0] max_width = self.image_shape[1] if x0 > max_width: x0 = max_width if x1 > max_width: x1 = max_width if y0 > max_height: y0 = max_height if y1 > max_height: y1 = max_height width = int(x1) - int(x0) height = int(y1) - int(y0) self.rect = Rectangle((int(x0), int(y0)), width, height, facecolor='None', edgecolor='green', linewidth='2') self.axes.add_patch(self.rect) self.rect_obj_list.append(self.rect) def OnSaveGrid(self, event): ''' Choose filename to save a CSV with grid with the coordinates. ''' with wx.FileDialog(self.frame, "Save CSV file", wildcard="*.csv", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return # the user changed their mind # Get Pathname pathname = fileDialog.GetPath() # Write to that file write_grid_csv(self, pathname) def OnSaveImage(self, event): ''' Choose filename to save the image. ''' with wx.FileDialog(self.frame, "Save an image file", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return # the user changed their mind # Get Pathname imagepathname = fileDialog.GetPath() # Write to that file write_image(self, imagepathname) def OnFileOpen(self, event): ''' Open Dialog so user can select a new image. ''' # Get the file path you wan to open. with wx.FileDialog(self.frame, "Open Image File", wildcard="Image Files *.png|*.jpg", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return # the user changed their mind # Proceed loading the file chosen by the user pathname = fileDialog.GetPath() self.imagepath = pathname self.NewImage() def OnFileAbout(self, event): ''' Display information about the application ''' # A message dialog box with an OK button. wx.OK is a standard ID in wxWidgets. dlg = wx.MessageDialog(self.frame, "A GUI for labeling images", "About Image Labeler", wx.OK) dlg.ShowModal() dlg.Destroy() def OnKeyDown(self, event): ''' Actions to be taken when a key is pressed ''' if event.key == 'delete': self.OnDelete() def OnLeftDown(self, event): ''' Actions taken when mouse button is pressed ''' # Is the click inside a rectangle? found = 0 current_x = event.xdata current_y = event.ydata for i in range(len(self.rect_obj_list)): rect = self.rect_obj_list[i] result = check_inside_rect((current_x, current_y), rect) if result == 1: self.selected_rect = i found += 1 # We want to select this rectangle, and move it if found > 0: self.is_moving = True self.change_rect_color() self.selected_rect_obj = self.rect_obj_list[self.selected_rect] self.x0, self.y0 = self.selected_rect_obj.xy self.x1 = None self.y1 = None self.press = self.x0, self.y0, event.xdata, event.ydata return 0 if self.cursor_mode == "nobb": return 0 # If the above is not satisified, it is time to draw a new rectangle # Initialise the rectangle self.rect = Rectangle((0, 0), 1, 1, facecolor='None', edgecolor='green', linewidth='2') self.x0 = None self.y0 = None self.x1 = None self.y1 = None self.axes.add_patch(self.rect) # Check the mouse press was actually on the canvas if event.xdata is not None and event.ydata is not None: # Upon initial press of the mouse record the origin and record the mouse as pressed self.frame.pressed = True self.rect.set_linestyle('dashed') self.x0 = event.xdata self.y0 = event.ydata def OnMotion(self, event): ''' Action taken when mouse movement happens over the canvas ''' # If a rectangle is selected and needs to be moved if self.is_moving == True: if event.xdata is not None and event.ydata is not None: if self.press is None: return self.x0, self.y0, xpress, ypress = self.press dx = event.xdata - xpress dy = event.ydata - ypress self.x1 = self.x0 + dx self.y1 = self.y0 + dy self.selected_rect_obj.set_x(self.x1) self.selected_rect_obj.set_y(self.y1) self.selected_rect_obj.figure.canvas.draw() return 0 # If the mouse has been pressed draw an updated rectangle when the mouse is # moved so the user can see what the current selection is elif self.frame.pressed: # Check the mouse was released on the canvas, # and if it wasn't then just leave the width and # height as the last values set by the motion event if event.xdata is not None and event.ydata is not None: self.x1 = event.xdata self.y1 = event.ydata # Set the width and height and draw the rectangle self.rect.set_width(self.x1 - self.x0) self.rect.set_height(self.y1 - self.y0) self.rect.set_xy((self.x0, self.y0)) self.canvas.draw() def OnLeftUp(self, event): ''' Actions taken with mouse button is released ''' # Rectangle has finished moving and objects need to be updated. if self.is_moving == True: x0 = self.selected_rect_obj.get_bbox().x0 y0 = self.selected_rect_obj.get_bbox().y0 x1 = self.selected_rect_obj.get_bbox().x1 y1 = self.selected_rect_obj.get_bbox().y1 self.selected_rect_obj.figure.canvas.draw() self.is_moving = False self.press = None # Update Grid with new coordinates fill_grid(self) return 0 # A new rectangle is finished being drawn elif self.frame.pressed: # Upon release draw the rectangle as a solid rectangle self.frame.pressed = False self.rect.set_linestyle('solid') # Check the mouse was released on the canvas, and if it wasn't then # just leave the width and height as the last values set by the motion event if event.xdata is not None and event.ydata is not None: self.x1 = event.xdata self.y1 = event.ydata # Set the width and height and origin of the bounding rectangle self.boundingRectWidth = self.x1 - self.x0 self.boundingRectHeight = self.y1 - self.y0 self.bouningRectOrigin = (self.x0, self.y0) # Draw the bounding rectangle self.rect.set_width(self.boundingRectWidth) self.rect.set_height(self.boundingRectHeight) self.rect.set_xy((self.x0, self.y0)) self.canvas.draw() # Keep list of rect objects self.rect_obj_list.append(self.rect) self.rect_labels.append("") # Fill the grid with the bounding boxes fill_grid(self) def NewImage(self): ''' A new image needs to be read in, and various objects need to be cleaned up. ''' # Delete all rectangles from the canvas self.clear_bb() # Uncheck all transformation boxes self.TransFrame.reset_boxes() # Clear Rectangle List self.rect_obj_list = [] self.rect_labels = [] # Read image into original_image and current_image self.ReadImage() # Refresh the canvas self.RefreshImage() def ReadImage(self): ''' Read image off disk ''' self.original_image = cv2.imread(self.imagepath) #Matplotlib is RGB, opencv is BGR self.original_image = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2RGB) self.current_image = self.original_image.copy() self.image_shape = self.current_image.shape def FirstImage(self): ''' The very first image is handled differently ''' # Read image into the original_image and current_image self.ReadImage() # Set Frame to size of image, plust a little extra self.frame.SetSize( (self.image_shape[1] + 550, self.image_shape[0] + 200)) self.set_panels() # Display the image on the canvas self.img_obj = self.axes.imshow(self.current_image, cmap='gray') self.canvas.draw() def RefreshImage(self): ''' Display new image to Matplotlib canvas and tiddy up ''' # Set Frame to size of image, plust a little extra self.frame.SetSize( (self.image_shape[1] + 550, self.image_shape[0] + 200)) self.set_panels() # Display the image on the canvas self.img_obj.set_extent( (0.0, self.image_shape[1], self.image_shape[0], 0.0)) self.BasicRefresh() def BasicRefresh(self): self.img_obj.set_data(self.current_image) self.canvas.draw() def OnDelete(self): ''' Delete the selected rectangle ''' # Don't try to delete if empty if len(self.rect_obj_list) < 1: self.user_error("There is nothing to delete.") return 1 try: self.selected_rect except: self.user_error("You haven't selected a rectangle yet.") return 1 rectangle = self.rect_obj_list[self.selected_rect] # Remove object from list self.rect_obj_list.remove(rectangle) del self.rect_labels[self.selected_rect] # Remove object from canvas rectangle.remove() # Remove coordinates from grid self.BBGrid.DeleteRows(self.selected_rect) # redraw the canvas self.canvas.draw() # clear del self.selected_rect def change_rect_color(self): ''' change the line color of currently selected rectangle ''' # Set selected rectangle line color black if len(self.rect_obj_list) < 1: return 0 rect = self.rect_obj_list[self.selected_rect] #set everything back to green for i in range(len(self.rect_obj_list)): rect = self.rect_obj_list[i] # Set currently selected rect line as black if i == self.selected_rect: rect.set_edgecolor('red') else: # Set everything else as green rect.set_edgecolor('green') # Also highlight row in grid highlight_row(self, self.selected_rect) self.canvas.draw() def clear_bb(self, event=None): ''' Remove all rectangles and empty grid ''' for rectangle in self.rect_obj_list: # Remove object from canvas rectangle.remove() empty_grid(self) # Set the list back to empty self.rect_obj_list = [] self.rect_labels = [] # redraw the canvas self.canvas.draw() def set_panels(self): ''' Set the size and position of the pannels based on the images size. ''' grid_width = 425 control_height = 70 img_pane_max_width = self.monitor_size[0] - grid_width - 10 img_pane_max_height = self.monitor_size[1] - control_height - 120 #Set some common sense things in relation to image widths if self.image_shape[1] < 525: img_pane_width = 525 elif self.image_shape[1] > img_pane_max_width: img_pane_width = img_pane_max_width else: img_pane_width = self.image_shape[1] #Set some common sense things in relation to image height if self.image_shape[0] < 525: img_pane_height = 525 elif self.image_shape[0] > img_pane_max_height: img_pane_height = img_pane_max_height else: img_pane_height = self.image_shape[0] #Come up with what the frame size should be frame_width = img_pane_width + grid_width + 10 frame_height = img_pane_height + control_height + 90 self.CanvasPanel.SetPosition((0, 0)) self.CanvasPanel.SetSize((img_pane_width, img_pane_height)) self.ControlPanel.SetPosition((0, img_pane_height + 5)) self.ControlPanel.SetSize((235, control_height)) self.BatchPanel.SetPosition((240, img_pane_height + 5)) self.BatchPanel.SetSize((125, control_height)) self.BBPanel.SetPosition((img_pane_width + 5, 0)) self.BBPanel.SetSize((grid_width, img_pane_height)) self.GridControlPanel.SetPosition( (img_pane_width + 5, img_pane_height + 5)) self.GridControlPanel.SetSize((grid_width, control_height)) #Set Overall frame size self.frame.SetSize((frame_width, frame_height)) self.canvas.SetSize((self.image_shape[1], self.image_shape[0])) # Reset axes so they don't get messed up when zooming self.axes.set_ybound(0, self.image_shape[0]) self.axes.set_xbound(0, self.image_shape[1]) def save_grid(self, event): ''' Save the selected bounding boxes to some file, database,etc ''' if len(self.rect_obj_list) < 1: self.user_error("You haven't selected a single bounding box yet.") return 1 else: write_grid_csv(self) return 0 def user_info(self, message): wx.MessageBox(message, 'Info', wx.OK) def user_error(self, message): wx.MessageBox(message, 'Error', wx.ICON_ERROR | wx.OK)
class TVWindow(wx.Frame): #########################Init Funcions############################# def __init__(self, parent=None, dpi=200, geoid=Geodesic.WGS84, resolution="10m"): """Constructor""" #call init of super class default_style = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.NO_FULL_REPAINT_ON_RESIZE | wx.WS_EX_CONTEXTHELP | wx.FRAME_EX_CONTEXTHELP wx.Frame.__init__(self, parent, title="Track Viewer %s" % parent.__version__, style=default_style, size=(400 * 2, 300 * 2)) self.Bind(wx.EVT_CLOSE, self.on_close_main) self.parent = parent try: self.center_lon = self.parent.dsk_row["inter_lon"] except AttributeError: self.center_lon = 180 self.dpi = dpi self.grd_file = None self.geo_tiff_paths = [] self.geoid = geoid self.resolution = resolution self.panel = wx.Panel(self, -1, size=(400 * 2, 300 * 2)) #Populate UI and Menu self.init_UI() self.create_menu() self.configure() self.update() def init_UI(self): spacing = 10 #----------------Build Directory and File Buttons----------------- # grd_sizer = wx.BoxSizer(wx.HORIZONTAL) # self.grd_path = wx.TextCtrl(self.panel, id=-1, size=(100,25), style=wx.TE_READONLY) # self.change_grd_btn = buttons.GenButton(self.panel, id=-1, label="Add Grid",size=(176, 29)) # self.change_grd_btn.InitColours() # self.Bind(wx.EVT_BUTTON, self.on_change_grd_btn, self.change_grd_btn) # grd_sizer.Add(self.change_grd_btn, wx.ALIGN_LEFT) # grd_sizer.AddSpacer(20) # grd_sizer.Add(self.grd_path,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND) #------------------------------------Make DropDown Box-----------------------------------------------------# latlon_sizer = wx.StaticBoxSizer( wx.StaticBox(self.panel, wx.ID_ANY, "Choose Gravity Window"), wx.VERTICAL) proj_sizer = wx.StaticBoxSizer( wx.StaticBox(self.panel, wx.ID_ANY, "Choose Projection"), wx.VERTICAL) down_sample_sizer = wx.StaticBoxSizer( wx.StaticBox(self.panel, wx.ID_ANY, "Choose Downsample Factor"), wx.HORIZONTAL) projs = [ "Mercator", "Mollweide", "North Polar Stereographic", "South Polar Stereographic" ] self.proj_box = wx.ComboBox(self.panel, id=wx.ID_ANY, size=(100, 25), value=projs[0], choices=projs, style=wx.CB_DROPDOWN | wx.TE_READONLY) self.Bind(wx.EVT_COMBOBOX, self.on_select_proj, self.proj_box) self.max_lat_box = wx.TextCtrl(self.panel, id=wx.ID_ANY | wx.TE_CENTRE, size=(25, 25)) self.min_lat_box = wx.TextCtrl(self.panel, id=wx.ID_ANY | wx.TE_CENTRE, size=(25, 25)) self.max_lon_box = wx.TextCtrl(self.panel, id=wx.ID_ANY | wx.TE_CENTRE, size=(25, 25)) self.min_lon_box = wx.TextCtrl(self.panel, id=wx.ID_ANY | wx.TE_CENTRE, size=(25, 25)) self.down_sample_box = wx.TextCtrl(self.panel, id=wx.ID_ANY | wx.TE_CENTRE, size=(50, 25)) self.re_render_button = wx.Button(self.panel, id=wx.ID_ANY, label='Refresh Figure', size=(50, 25)) self.Bind(wx.EVT_BUTTON, self.on_re_render_button, self.re_render_button) #Projection sizer proj_sizer.Add(self.proj_box, 1, wx.ALIGN_LEFT | wx.ALIGN_TOP | wx.EXPAND | wx.ALL, spacing) #Lat-Lon Sizer lat_sizer = wx.BoxSizer(wx.HORIZONTAL) lat_sizer.AddMany([ (self.min_lat_box, 1, wx.ALIGN_LEFT | wx.ALIGN_TOP | wx.EXPAND | wx.ALL, spacing), (self.max_lat_box, 1, wx.ALIGN_RIGHT | wx.ALIGN_TOP | wx.EXPAND | wx.ALL, spacing) ]) lon_sizer = wx.BoxSizer(wx.HORIZONTAL) lon_sizer.AddMany([ (self.min_lon_box, 1, wx.ALIGN_LEFT | wx.ALIGN_BOTTOM | wx.EXPAND | wx.ALL, spacing), (self.max_lon_box, 1, wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM | wx.EXPAND | wx.ALL, spacing) ]) latlon_sizer.AddMany([ (lat_sizer, 1, wx.ALIGN_TOP | wx.EXPAND, spacing), (lon_sizer, 1, wx.ALIGN_BOTTOM | wx.EXPAND, spacing) ]) #Downsample sizer with downsample box and refresh button down_sample_sizer.AddMany([ (self.re_render_button, 1, wx.ALIGN_LEFT | wx.ALIGN_BOTTOM | wx.EXPAND | wx.ALL, spacing), (self.down_sample_box, 1, wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM | wx.EXPAND | wx.ALL, spacing) ]) #Combine projection and downsample sizers proj_ds_sizer = wx.BoxSizer(wx.VERTICAL) proj_ds_sizer.AddMany([ (proj_sizer, 1, wx.ALIGN_TOP | wx.EXPAND, spacing), (down_sample_sizer, 1, wx.ALIGN_BOTTOM | wx.EXPAND, spacing) ]) #Combine all in final sizer all_txt_btn_sizer = wx.BoxSizer(wx.HORIZONTAL) all_txt_btn_sizer.AddMany([ (proj_ds_sizer, 1, wx.ALIGN_LEFT | wx.EXPAND, spacing), (latlon_sizer, 1, wx.ALIGN_RIGHT | wx.EXPAND, spacing) ]) #-------------------------------------Make Figure----------------------------------------------------------# self.fig = Figure((2, 2), dpi=self.dpi) self.canvas = FigCanvas(self.panel, -1, self.fig) self.toolbar = NavigationToolbar(self.canvas) self.ax = self.fig.add_subplot(111) psk.remove_axis_lines_and_ticks(self.ax) self.toolbar.Hide() self.plot_setting = "Zoom" self.toolbar.zoom() self.canvas.Bind(wx.EVT_MIDDLE_DOWN, self.on_middle_click_plot) self.canvas.Bind(wx.EVT_MOTION, self.on_move_mouse_plot) self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.on_select_dleft_click) #----------------------------------Build UI and Fit--------------------------------------------------------# outer_sizer = wx.BoxSizer(wx.VERTICAL) outer_sizer.AddMany( [ #(grd_sizer,1,wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND|wx.LEFT|wx.RIGHT,spacing), (all_txt_btn_sizer, 1, wx.ALIGN_CENTER | wx.ALIGN_TOP | wx.EXPAND), (self.canvas, 10, wx.ALIGN_CENTER | wx.ALIGN_TOP | wx.EXPAND) ]) self.panel.SetSizerAndFit(outer_sizer) def create_menu(self): """ Generates Menu """ self.menubar = wx.MenuBar() #----------------- # File Menu #----------------- menu_file = wx.Menu() m_tiff = menu_file.Append(-1, "&Add GeoTiff\tCtrl-O", "AddTiff") self.Bind(wx.EVT_MENU, self.on_add_tiff, m_tiff) menu_file.AppendSeparator() m_exit = menu_file.Append(-1, "&Exit\tCtrl-Q", "Exit") self.Bind(wx.EVT_MENU, self.on_close_main, m_exit) #----------------- self.menubar.Append(menu_file, "&File") self.SetMenuBar(self.menubar) def configure(self): try: dsk_row = self.parent.dsk_row TRACKSELECTED = isinstance(dsk_row, type(None)) except: TRACKSELECTED = False if TRACKSELECTED: #Set up default values self.min_lat_box.SetValue("%.1f" % dsk_row["inter_lat"] - 10) self.max_lat_box.SetValue("%.1f" % dsk_row["inter_lat"] + 10) self.min_lon_box.SetValue("%.1f" % dsk_row["inter_lon"] - 10) self.max_lon_box.SetValue("%.1f" % dsk_row["inter_lon"] + 10) else: # TODO self.min_lat_box.SetValue("%.1f" % -20.) self.max_lat_box.SetValue("%.1f" % 30.) self.min_lon_box.SetValue("%.1f" % 190.) self.max_lon_box.SetValue("%.1f" % 250.) self.down_sample_box.SetValue("%.0f" % 50.) self.window = [None, None, None, None] self.down_sample_factor = None #########################Update UI Funcions############################# def update(self): #Populates Logger and makes plot self.make_map() self.plot_gravity() self.plot_tracks() self.plot_sites() self.canvas.draw() def on_close_main(self, event): self.parent.tvw_open = False self.Destroy() ###################Button and Dropdown Functions######################### # def on_change_grd_btn(self,event): # dlg = wx.FileDialog( # self, message="Choose Grid File", # defaultDir=self.parent.WD, # defaultFile="", # wildcard="Grid Files (*.grd,*.nc,*.ncf)|*.grd;*.nc;*.ncf|All Files (*.*)|*.*", # style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST # ) # if dlg.ShowModal() == wx.ID_OK: # self.grd_file = dlg.GetPath() # self.grd_path.SetValue(self.grd_file) # self.update() # dlg.Destroy() # else: dlg.Destroy() def on_select_proj(self, event): self.update() def on_re_render_button(self, event): self.plot_gravity() ###########################Figure Funcions############################### def on_middle_click_plot(self, event): if event.LeftIsDown() or event.ButtonDClick(): event.Skip() return elif self.plot_setting == "Zoom": self.plot_setting = "Pan" self.toolbar.pan('off') elif self.plot_setting == "Pan": self.plot_setting = "Zoom" self.toolbar.zoom() event.Skip() def on_move_mouse_plot(self, event): try: dsk_row = self.parent.dsk_row except AttributeError: event.Skip() return pos = event.GetPosition() width, height = self.canvas.get_width_height() pos = [pos[0], height - pos[1]] pos = self.ax.transData.inverted().transform(pos) lonlat = ccrs.PlateCarree().transform_point(*pos, self.proj) self.plot_tracer_on_self_and_parent(dsk_row, lonlat) self.parent.canvas.draw() self.canvas.draw() event.Skip() def on_select_dleft_click(self, event): try: dsk_row = self.parent.dsk_row except AttributeError: event.Skip() return pos = event.GetPosition() width, height = self.canvas.get_width_height() pos = [pos[0], height - pos[1]] pos = self.ax.transData.inverted().transform(pos) lonlat = ccrs.PlateCarree().transform_point(*pos, self.proj) proj_locs = utl.calc_projected_distance(dsk_row['inter_lon'], dsk_row['inter_lat'], [lonlat[0]], [lonlat[1]], dsk_row['strike']) self.parent.set_new_intercept(proj_locs.iloc[0]["dist"]) self.parent.update(event) self.update() ##########################Additional Plotting and Backend Functions################ def on_parent_select_track(self): self.center_lon = self.parent.dsk_row["inter_lon"] self.update() def on_add_tiff(self, event): dlg = wx.FileDialog( self, message="Choose GeoTiff File", defaultDir=self.parent.WD, wildcard="Tiff Files (*.tiff)|*.tiff|All Files (*.*)|*.*", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) if dlg.ShowModal() == wx.ID_OK: self.geo_tiff_paths.append(dlg.GetPath()) self.update() dlg.Destroy() else: dlg.Destroy() def make_map(self): #set basemap try: self.fig.delaxes(self.ax) except AttributeError: pass #TODO: ADD TRANSVERSE MERCATOR AT STRIKE AS OPTION if self.proj_box.GetValue() == 'North Polar Stereographic': self.proj = ccrs.NorthPolarStereo( central_longitude=self.center_lon, true_scale_latitude=None, globe=None) self.ax = self.fig.add_subplot(111, projection=self.proj) pgeo.make_circular_ax(self.ax) elif self.proj_box.GetValue() == 'South Polar Stereographic': self.proj = ccrs.SouthPolarStereo( central_longitude=self.center_lon, true_scale_latitude=None, globe=None) self.ax = self.fig.add_subplot(111, projection=self.proj) pgeo.make_circular_ax(self.ax) elif self.proj_box.GetValue() == 'Mercator': self.proj = ccrs.Mercator(central_longitude=self.center_lon) self.ax = self.fig.add_subplot(111, projection=self.proj) path = mpath.Path( np.array([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]])) self.ax.set_boundary(path, transform=self.ax.transAxes) elif self.proj_box.GetValue() == 'Mollweide': self.proj = ccrs.Mollweide(central_longitude=self.center_lon, globe=None) self.ax = self.fig.add_subplot(111, projection=self.proj) # path = mpath.Path(np.array([[0.05,0.05],[0.95,0.05],[0.95,0.95],[0.05,0.95],[0.05,0.05]])) # self.ax.set_boundary(path, transform=self.fig.transFigure) else: self.parent.user_warning("Projection %s not supported" % str(self.proj_box.GetValue())) return self.ax.set_xticks(np.arange(0, 370, 10.), crs=ccrs.PlateCarree()) self.ax.set_yticks(np.arange(-80, 90, 10.), crs=ccrs.PlateCarree()) self.ax.tick_params(grid_linewidth=.5, grid_linestyle=":", color="k", labelsize=8) lon_formatter = LongitudeFormatter(zero_direction_label=True) lat_formatter = LatitudeFormatter() self.ax.xaxis.set_major_formatter(lon_formatter) self.ax.yaxis.set_major_formatter(lat_formatter) # self.ax.gridlines(color='grey', alpha=0.5, linestyle='--',linewidth=.5) land = cfeature.NaturalEarthFeature('physical', 'land', '110m', edgecolor="black", facecolor="", linewidth=2) self.ax.add_feature(land) # if self.proj_box.GetValue() == 'Mercator': self.ax.gridlines(color='black', alpha=0.4, linestyle='--', draw_labels=True) def plot_gravity(self): # try: TRACKSELECTED = self.parent.dsk_row!=None # except: TRACKSELECTED = False # if not TRACKSELECTED: self.parent.user_warning("No track selected, skipping gravity plotting"); return new_window = [ float(self.min_lon_box.GetValue()), float(self.max_lon_box.GetValue()), float(self.min_lat_box.GetValue()), float(self.max_lat_box.GetValue()) ] new_down_sample_factor = float(self.down_sample_box.GetValue()) if new_window == self.window and new_down_sample_factor == self.down_sample_factor: return else: self.window, self.down_sample_factor = new_window, new_down_sample_factor with wx.BusyInfo("Refreshing Gravity Grid", parent=self): # wx.Yield() all_lons, all_lats, all_grav = pg.get_sandwell( self.window, self.down_sample_factor) # self.ax.imshow(all_grav,cmap="viridis",alpha=.75,transform=ccrs.PlateCarree(),zorder=0,extent=self.window,origin="lower") fcm = self.ax.contourf(all_lons, all_lats, all_grav, 60, cmap="viridis", alpha=.75, transform=ccrs.PlateCarree(), zorder=0) self.ax.set_extent(self.window, crs=ccrs.PlateCarree()) def plot_tracks(self): try: dsk_row = self.parent.dsk_row dsk_data = self.parent.deskew_df[self.parent.deskew_df["sz_name"] == dsk_row["sz_name"]] except AttributeError: return # projected_distances = utl.calc_projected_distance(dsk_row['inter_lon'],dsk_row['inter_lat'],mag_data['lon'].tolist(),mag_data['lat'].tolist(),dsk_row['strike']) # dis = max(abs(projected_distances["dist"]))*1000 # geodict1 = Geodesic.WGS84.Direct(dsk_row['inter_lat'],dsk_row['inter_lon'],dsk_row['strike']-90,dis) # geodict2 = Geodesic.WGS84.Direct(dsk_row['inter_lat'],dsk_row['inter_lon'],dsk_row['strike']+90,dis) self.ax.plot([geodict1["lon2"], geodict2["lon2"]], [geodict1["lat2"], geodict2["lat2"]], transform=ccrs.Geodetic(), color="black", linewidth=1, linestyle='--') for j, (i, row) in enumerate(dsk_data.iterrows()): # Read in deskewed profile # This is hard-coded now. It will be updated to take a list of profiles in the future if not os.path.isfile(infile): self.parent.user_warning("Data file %s could not be found" % infile) dskd = utl.open_mag_file( os.path.join(row['data_dir'], row["comp_name"])) # Define the angle along which to project perp = row["strike"] - 180 lon = dskd["lon"] lat = dskd["lat"] mag = sk.phase_shift_data(dskd["mag"].tolist(), row["phase_shift"]) # Find distance to project if row["track_type"] == 'ship': pcol = '#000000' scle = 0.2 * 1e3 if row["track_type"] == 'aero': if 'Ed' in row["comp_name"]: pcol = 'purple' else: pcol = 'darkorchid' scle = 0.5 * 1e3 # Project amplitude onto map mlats, mlons = [], [] for i in range(len(mag)): gdsc = self.geoid.Direct(lat[i], lon[i], perp, mag[i] * scle) mlons.append(gdsc['lon2']) mlats.append(gdsc['lat2']) # Plot map elements deskew_tracks.append( self.ax.plot(utl.convert_to_0_360(lon), lat, '--', linewidth=1.0, transform=ccrs.PlateCarree(), color=pcol, zorder=990)) deskew_tracks.append( self.ax.plot(utl.convert_to_0_360(mlons), mlats, '-', linewidth=1.0, transform=ccrs.PlateCarree(), color=pcol, zorder=1000)) deskew_fill.append( self.ax.fill_between(utl.convert_to_0_360( np.array(mlons)[mag > 0]), np.array(mlats)[mag > 0], lat[mag > 0], transform=ccrs.PlateCarree(), alpha=0.5, color=pcol)) def plot_sites(self): pass #should plot present site locations of tracks def plot_tracer_on_self_and_parent(self, dsk_row, lonlat): proj_locs = utl.calc_projected_distance(dsk_row['inter_lon'], dsk_row['inter_lat'], [lonlat[0]], [lonlat[1]], dsk_row['strike']) self.plot_tracer_point(dsk_row, proj_locs.iloc[0]["dist"], color="red", marker="o", s=10) self.parent.plot_tracer_point(proj_locs.iloc[0]["dist"], linestyle='--', color='red', alpha=.5) def plot_tracer_point(self, dsk_row, dis, **kwargs): try: self.point_on_track.remove() except (AttributeError, ValueError) as e: pass geodict = Geodesic.WGS84.Direct(dsk_row['inter_lat'], dsk_row['inter_lon'], dsk_row['strike'] - 90, dis * 1000) self.point_on_track = self.ax.scatter(geodict["lon2"], geodict["lat2"], transform=ccrs.Geodetic(), **kwargs)
def pan(self, *args): self.ToggleTool(self.wx_ids['Zoom'], False) self.ToggleTool(self.ON_LABELPEAKS, False) NavigationToolbar2WxAgg.pan(self, *args)
class SkwLatWindow(wx.Frame): #########################Init Funcions############################# def __init__(self, parent=None, dpi=200, fontsize=6): """Constructor""" #call init of super class default_style = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.NO_FULL_REPAINT_ON_RESIZE | wx.WS_EX_CONTEXTHELP | wx.FRAME_EX_CONTEXTHELP wx.Frame.__init__(self, parent, title="Skewness by Latitude %s" % parent.__version__, style=default_style) self.Bind(wx.EVT_CLOSE, self.on_close_main) self.parent = parent self.dpi = dpi self.fontsize = fontsize # self.panel = wx.Panel(self, wx.ID_ANY) self.scrolled_panel = wx.lib.scrolledpanel.ScrolledPanel( self, wx.ID_ANY) # make the Panel #Populate UI and Menu self.init_UI() self.create_menu() self.scrolled_panel.SetAutoLayout(True) self.scrolled_panel.SetupScrolling() # endable scrolling self.update() def init_UI(self): spacing, vpadding, hpadding = 0., .01, .15 #------------------------------------Make DropDown Box-----------------------------------------------------# sz_names_sizer = wx.StaticBoxSizer( wx.StaticBox(self.scrolled_panel, wx.ID_ANY, "Spreading Zone"), wx.HORIZONTAL) try: sz_names = self.parent.deskew_df["sz_name"].drop_duplicates( ).tolist() self.maximum_profiles = max([ len(self.parent.deskew_df[self.parent.deskew_df["sz_name"] == sz_name]) for sz_name in sz_names ]) except AttributeError: sz_names, self.maximum_profiles = [""], 6 self.sz_names_box = wx.ComboBox(self.scrolled_panel, id=wx.ID_ANY, size=(300, 50), value=sz_names[0], choices=sz_names, style=wx.CB_DROPDOWN | wx.TE_READONLY) self.Bind(wx.EVT_COMBOBOX, self.on_select_sz, self.sz_names_box) self.show_synth_button = wx.CheckBox(self.scrolled_panel, id=wx.ID_ANY, label="Plot Synthetic", size=(300, 50)) self.Bind(wx.EVT_CHECKBOX, self.on_show_synth_button, self.show_synth_button) sz_names_sizer.AddMany([ (self.sz_names_box, 0, wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, spacing), (self.show_synth_button, 0, wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL, spacing) ]) # self.panel.SetSizerAndFit(sz_names_sizer) #-------------------------------------Make Figure----------------------------------------------------------# canvas_sizer = wx.BoxSizer(wx.VERTICAL) self.fig = Figure((2., 1. * self.maximum_profiles), dpi=self.dpi) self.fig.subplots_adjust(top=1. - vpadding, right=1. - hpadding, left=hpadding, bottom=vpadding, wspace=.0, hspace=.0) self.canvas = FigCanvas(self.scrolled_panel, wx.ID_ANY, self.fig) self.toolbar = NavigationToolbar(self.canvas) self.toolbar.Hide() self.plot_setting = "Zoom" self.toolbar.zoom() self.canvas.Bind(wx.EVT_MIDDLE_DOWN, self.on_middle_click_plot) self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.on_select_dleft_click) canvas_sizer.AddMany([ (sz_names_sizer, 0, wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND | wx.ALL, spacing), (self.canvas, 1, wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND | wx.ALL, spacing) ]) #----------------------------------Build UI and Fit--------------------------------------------------------# self.scrolled_panel.SetSizerAndFit(canvas_sizer) # outer_sizer = wx.BoxSizer(wx.VERTICAL) # outer_sizer.AddMany([(self.panel,1,wx.ALIGN_CENTER|wx.EXPAND), # (self.scrolled_panel,2,wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND)]) # self.SetSizer(outer_sizer) # outer_sizer.Fit(self) def create_menu(self): """ Generates Menu """ self.menubar = wx.MenuBar() #----------------- # File Menu #----------------- menu_file = wx.Menu() submenu_save_plots = wx.Menu() m_save_plot = submenu_save_plots.Append(-1, "&Save Plot", "") self.Bind(wx.EVT_MENU, self.on_save_plot, m_save_plot, "save-plot") m_new_sub_plots = menu_file.Append(-1, "&Save Result", submenu_save_plots) menu_file.AppendSeparator() m_exit = menu_file.Append(-1, "&Exit\tCtrl-Q", "Exit") self.Bind(wx.EVT_MENU, self.on_close_main, m_exit) #----------------- self.menubar.Append(menu_file, "&File") self.SetMenuBar(self.menubar) #########################Update UI Funcions############################# def update(self): #Populates Logger and makes plot self.fig.clear() self.plot_skewnesses_by_lat() self.canvas.draw() def on_save_plot(self, event): self.toolbar.save_figure() def on_close_main(self, event): self.parent.skw_lat_open = False self.Destroy() ##########################ComboBox Funcions############################## def on_select_sz(self, event): self.update() def on_show_synth_button(self, event): self.update() ###########################Figure Funcions############################### def on_middle_click_plot(self, event): if event.LeftIsDown() or event.ButtonDClick(): event.Skip() return elif self.plot_setting == "Zoom": self.plot_setting = "Pan" self.toolbar.pan('off') elif self.plot_setting == "Pan": self.plot_setting = "Zoom" self.toolbar.zoom() event.Skip() def on_select_dleft_click(self, event): pass # try: dsk_df = self.parent.deskew_df # except AttributeError: event.Skip(); return # pos=event.GetPosition() # width, height = self.canvas.get_width_height() # pos = [pos[0],height-pos[1]] # pos = self.ax.transData.inverted().transform(pos) # min_dis,min_row = np.inf,None # ylim = self.ax.get_ylim() # for i,row in dsk_df.iterrows(): # dis = ((row["inter_lat"]-pos[0])/ylim[0])**2 + ((row["aei"]-pos[1])/ylim[1])**2 # if dis < min_dis: # min_dis = dis # min_row = row # self.parent.track_box.SetValue(min_row["comp_name"]) # self.parent.on_select_track(event) ##########################Additional Plotting and Backend Functions################ def on_parent_select_track(self): pass def plot_skewnesses_by_lat(self, clip_on=True): try: sz_name = self.sz_names_box.GetValue() rows = self.parent.deskew_df[self.parent.deskew_df["sz_name"] == sz_name] rows.sort_values("inter_lat", inplace=True, ascending=False) except (AttributeError, KeyError) as e: print("Spreading Zone %s not found in deskew file" % str(self.sz_names_box.GetValue())) return try: xlims = self.parent.ax.get_xlim() ylims = self.parent.ax.get_ylim() except AttributeError: xlims, ylims = (-300, 300), (-150, 150) axs = self.fig.subplots(self.maximum_profiles, 1, sharex=True, sharey=True) # for ax in axs: # ax.set_facecolor("grey") axs = axs[:len(rows)] for j, (ax, (i, row)) in enumerate(zip(axs, rows.iterrows())): print(j, row["comp_name"], xlims, ylims) ax.set_anchor('W') psk.remove_axis_lines_and_ticks(ax) min_proj_dis, max_proj_dis = psk.plot_skewness_data( row, float(row['phase_shift']), ax, picker=True, clip_on=clip_on, xlims=xlims, flip=True) ax.annotate( r"%s" % row['comp_name'] + "\n" + r"%.1f$^\circ$N,%.1f$^\circ$E" % (float( row['inter_lat']), utl.convert_to_0_360(row['inter_lon'])), xy=(-.215, .5), xycoords="axes fraction", fontsize=self.fontsize, va="center", ha="left") ax.annotate(r"$\theta$=%.1f" % float(row['phase_shift']) + "\n" + r"$e_a$=%.1f" % float(row['aei']), xy=(1.15, .5), xycoords="axes fraction", fontsize=self.fontsize, va="center", ha="right") # ax.set_ylabel(r"$\theta$=%.1f"%float(row['phase_shift'])+"\n"+r"$e_a$=%.1f"%float(row['aei']),rotation=0,fontsize=self.fontsize) ax.yaxis.set_label_coords(1.05, .45) ax.patch.set_alpha(0.0) # ax.format_coord = format_coord if self.show_synth_button.GetValue(): try: ax.plot(self.parent.dis_synth, self.parent.synth, 'r-', alpha=.4, zorder=1) except (AttributeError, IndexError): print( "No synthetic found to render in skewness by latitude window" ) scale = np.sqrt(sum(np.array(xlims)**2)) if not scale < 20 or scale > 3000: ax.set_xlim(xlims) ax.set_ylim(ylims) if self.parent.spreading_rate_path != None: psk.plot_chron_span_on_axes( sz_name, self.fig.get_axes(), rows[['age_min', 'age_max']].iloc[0], spreading_rate_path=self.parent.spreading_rate_path) # self.fig.subplots_adjust(hspace=.0) #remove space between subplot axes self.canvas.draw()
def pan(self, ev): if wx.Platform == '__WXMAC__' : self.ToggleTool(self.wx_ids['Pan'], self.GetToolState(self.wx_ids['Pan'])) NavToolbar.pan(self,ev)
class InterpretationEditorFrame(wx.Frame): #########################Init Funcions############################# def __init__(self,parent): """Constructor""" #set parent and resolution self.parent = parent self.GUI_RESOLUTION=self.parent.GUI_RESOLUTION #call init of super class wx.Frame.__init__(self, self.parent, title="Interpretation Editor",size=(675*self.GUI_RESOLUTION,425*self.GUI_RESOLUTION)) self.Bind(wx.EVT_CLOSE, self.on_close_edit_window) #make the Panel self.panel = wx.Panel(self,-1,size=(700*self.GUI_RESOLUTION,450*self.GUI_RESOLUTION)) #set icon icon = wx.EmptyIcon() icon_path = os.path.join(IMG_DIRECTORY, 'PmagPy.ico') if os.path.exists(icon_path): icon.CopyFromBitmap(wx.Bitmap(icon_path), wx.BITMAP_TYPE_ANY) self.SetIcon(icon) self.specimens_list=self.parent.specimens self.current_fit_index = None self.search_query = "" self.font_type = self.parent.font_type #build UI self.init_UI() #update with stuff self.on_select_level_name(None) def init_UI(self): """ Builds User Interface for the interpretation Editor """ #set fonts FONT_WEIGHT=1 if sys.platform.startswith('win'): FONT_WEIGHT=-1 font1 = wx.Font(9+FONT_WEIGHT, wx.SWISS, wx.NORMAL, wx.NORMAL, False, self.font_type) font2 = wx.Font(12+FONT_WEIGHT, wx.SWISS, wx.NORMAL, wx.NORMAL, False, self.font_type) #if you're on mac do some funny stuff to make it look okay is_mac = False if sys.platform.startswith("darwin"): is_mac = True self.search_bar = wx.SearchCtrl(self.panel, size=(350*self.GUI_RESOLUTION,25) ,style=wx.TE_PROCESS_ENTER | wx.TE_PROCESS_TAB | wx.TE_NOHIDESEL) self.Bind(wx.EVT_TEXT_ENTER, self.on_enter_search_bar,self.search_bar) self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, self.on_enter_search_bar,self.search_bar) # self.Bind(wx.EVT_TEXT, self.on_complete_search_bar,self.search_bar) #build logger self.logger = wx.ListCtrl(self.panel, -1, size=(350*self.GUI_RESOLUTION,475*self.GUI_RESOLUTION),style=wx.LC_REPORT) self.logger.SetFont(font1) self.logger.InsertColumn(0, 'specimen',width=55*self.GUI_RESOLUTION) self.logger.InsertColumn(1, 'fit name',width=45*self.GUI_RESOLUTION) self.logger.InsertColumn(2, 'max',width=35*self.GUI_RESOLUTION) self.logger.InsertColumn(3, 'min',width=35*self.GUI_RESOLUTION) self.logger.InsertColumn(4, 'n',width=25*self.GUI_RESOLUTION) self.logger.InsertColumn(5, 'fit type',width=60*self.GUI_RESOLUTION) self.logger.InsertColumn(6, 'dec',width=35*self.GUI_RESOLUTION) self.logger.InsertColumn(7, 'inc',width=35*self.GUI_RESOLUTION) self.logger.InsertColumn(8, 'mad',width=35*self.GUI_RESOLUTION) self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnClick_listctrl, self.logger) self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK,self.OnRightClickListctrl,self.logger) #set fit attributes box self.display_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "display options"), wx.HORIZONTAL) self.name_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "fit name/color"), wx.VERTICAL) self.bounds_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "fit bounds"), wx.VERTICAL) self.buttons_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY), wx.VERTICAL) #logger display selection box UPPER_LEVEL = self.parent.level_box.GetValue() if UPPER_LEVEL=='sample': name_choices = self.parent.samples if UPPER_LEVEL=='site': name_choices = self.parent.sites if UPPER_LEVEL=='location': name_choices = self.parent.locations if UPPER_LEVEL=='study': name_choices = ['this study'] self.level_box = wx.ComboBox(self.panel, -1, size=(100*self.GUI_RESOLUTION, 25), value=UPPER_LEVEL, choices=['sample','site','location','study'], style=wx.CB_DROPDOWN) self.Bind(wx.EVT_COMBOBOX, self.on_select_higher_level,self.level_box) self.level_names = wx.ComboBox(self.panel, -1, size=(100*self.GUI_RESOLUTION, 25), value=self.parent.level_names.GetValue(), choices=name_choices, style=wx.CB_DROPDOWN) self.Bind(wx.EVT_COMBOBOX, self.on_select_level_name,self.level_names) #mean type and plot display boxes self.mean_type_box = wx.ComboBox(self.panel, -1, size=(100*self.GUI_RESOLUTION, 25), value=self.parent.mean_type_box.GetValue(), choices=['Fisher','Fisher by polarity','None'], style=wx.CB_DROPDOWN,name="high_type") self.Bind(wx.EVT_COMBOBOX, self.on_select_mean_type_box,self.mean_type_box) self.mean_fit_box = wx.ComboBox(self.panel, -1, size=(100*self.GUI_RESOLUTION, 25), value=self.parent.mean_fit, choices=(['None','All'] + self.parent.fit_list), style=wx.CB_DROPDOWN,name="high_type") self.Bind(wx.EVT_COMBOBOX, self.on_select_mean_fit_box,self.mean_fit_box) #show box if UPPER_LEVEL == "study" or UPPER_LEVEL == "location": show_box_choices = ['specimens','samples','sites'] if UPPER_LEVEL == "site": show_box_choices = ['specimens','samples'] if UPPER_LEVEL == "sample": show_box_choices = ['specimens'] self.show_box = wx.ComboBox(self.panel, -1, size=(100*self.GUI_RESOLUTION, 25), value='specimens', choices=show_box_choices, style=wx.CB_DROPDOWN,name="high_elements") self.Bind(wx.EVT_COMBOBOX, self.on_select_show_box,self.show_box) #coordinates box self.coordinates_box = wx.ComboBox(self.panel, -1, size=(100*self.GUI_RESOLUTION, 25), choices=self.parent.coordinate_list, value=self.parent.coordinates_box.GetValue(), style=wx.CB_DROPDOWN, name="coordinates") self.Bind(wx.EVT_COMBOBOX, self.on_select_coordinates,self.coordinates_box) #bounds select boxes self.tmin_box = wx.ComboBox(self.panel, -1, size=(80*self.GUI_RESOLUTION, 25), choices=[''] + self.parent.T_list, style=wx.CB_DROPDOWN, name="lower bound") self.tmax_box = wx.ComboBox(self.panel, -1, size=(80*self.GUI_RESOLUTION, 25), choices=[''] + self.parent.T_list, style=wx.CB_DROPDOWN, name="upper bound") #color box self.color_dict = self.parent.color_dict self.color_box = wx.ComboBox(self.panel, -1, size=(80*self.GUI_RESOLUTION, 25), choices=[''] + self.color_dict.keys(), style=wx.TE_PROCESS_ENTER, name="color") self.Bind(wx.EVT_TEXT_ENTER, self.add_new_color, self.color_box) #name box self.name_box = wx.TextCtrl(self.panel, -1, size=(80*self.GUI_RESOLUTION, 25), name="name") #more mac stuff h_size_buttons,button_spacing = 25,5.5 if is_mac: h_size_buttons,button_spacing = 18,0. #buttons self.add_all_button = wx.Button(self.panel, id=-1, label='add new fit to all specimens',size=(160*self.GUI_RESOLUTION,h_size_buttons)) self.add_all_button.SetFont(font1) self.Bind(wx.EVT_BUTTON, self.add_fit_to_all, self.add_all_button) self.add_fit_button = wx.Button(self.panel, id=-1, label='add fit to highlighted specimens',size=(160*self.GUI_RESOLUTION,h_size_buttons)) self.add_fit_button.SetFont(font1) self.Bind(wx.EVT_BUTTON, self.add_highlighted_fits, self.add_fit_button) self.delete_fit_button = wx.Button(self.panel, id=-1, label='delete highlighted fits',size=(160*self.GUI_RESOLUTION,h_size_buttons)) self.delete_fit_button.SetFont(font1) self.Bind(wx.EVT_BUTTON, self.delete_highlighted_fits, self.delete_fit_button) self.apply_changes_button = wx.Button(self.panel, id=-1, label='apply changes to highlighted fits',size=(160*self.GUI_RESOLUTION,h_size_buttons)) self.apply_changes_button.SetFont(font1) self.Bind(wx.EVT_BUTTON, self.apply_changes, self.apply_changes_button) #windows display_window_0 = wx.GridSizer(2, 1, 10*self.GUI_RESOLUTION, 19*self.GUI_RESOLUTION) display_window_1 = wx.GridSizer(2, 1, 10*self.GUI_RESOLUTION, 19*self.GUI_RESOLUTION) display_window_2 = wx.GridSizer(2, 1, 10*self.GUI_RESOLUTION, 19*self.GUI_RESOLUTION) name_window = wx.GridSizer(2, 1, 10*self.GUI_RESOLUTION, 19*self.GUI_RESOLUTION) bounds_window = wx.GridSizer(2, 1, 10*self.GUI_RESOLUTION, 19*self.GUI_RESOLUTION) buttons1_window = wx.GridSizer(4, 1, 5*self.GUI_RESOLUTION, 19*self.GUI_RESOLUTION) display_window_0.AddMany( [(self.coordinates_box, wx.ALIGN_LEFT), (self.show_box, wx.ALIGN_LEFT)] ) display_window_1.AddMany( [(self.level_box, wx.ALIGN_LEFT), (self.mean_type_box, wx.ALIGN_LEFT)] ) display_window_2.AddMany( [(self.level_names, wx.ALIGN_LEFT), (self.mean_fit_box, wx.ALIGN_LEFT)] ) name_window.AddMany( [(self.name_box, wx.ALIGN_LEFT), (self.color_box, wx.ALIGN_LEFT)] ) bounds_window.AddMany( [(self.tmin_box, wx.ALIGN_LEFT), (self.tmax_box, wx.ALIGN_LEFT)] ) buttons1_window.AddMany( [(self.add_fit_button, wx.ALL|wx.ALIGN_CENTER|wx.SHAPED, 0), (self.add_all_button, wx.ALL|wx.ALIGN_CENTER|wx.SHAPED, 0), (self.delete_fit_button, wx.ALL|wx.ALIGN_CENTER|wx.SHAPED, 0), (self.apply_changes_button, wx.ALL|wx.ALIGN_CENTER|wx.SHAPED, 0)]) self.display_sizer.Add(display_window_0, 1, wx.TOP|wx.EXPAND, 8) self.display_sizer.Add(display_window_1, 1, wx.TOP | wx.LEFT|wx.EXPAND, 8) self.display_sizer.Add(display_window_2, 1, wx.TOP | wx.LEFT|wx.EXPAND, 8) self.name_sizer.Add(name_window, 1, wx.TOP, 5.5) self.bounds_sizer.Add(bounds_window, 1, wx.TOP, 5.5) self.buttons_sizer.Add(buttons1_window, 1, wx.TOP, 0) #duplicate higher levels plot self.fig = Figure((2.5*self.GUI_RESOLUTION, 2.5*self.GUI_RESOLUTION), dpi=100) self.canvas = FigCanvas(self.panel, -1, self.fig, ) self.toolbar = NavigationToolbar(self.canvas) self.toolbar.Hide() self.toolbar.zoom() self.higher_EA_setting = "Zoom" self.canvas.Bind(wx.EVT_LEFT_DCLICK,self.on_equalarea_higher_select) self.canvas.Bind(wx.EVT_MOTION,self.on_change_higher_mouse_cursor) self.canvas.Bind(wx.EVT_MIDDLE_DOWN,self.home_higher_equalarea) self.canvas.Bind(wx.EVT_RIGHT_DOWN,self.pan_zoom_higher_equalarea) self.eqarea = self.fig.add_subplot(111) draw_net(self.eqarea) #Higher Level Statistics Box self.stats_sizer = wx.StaticBoxSizer( wx.StaticBox( self.panel, wx.ID_ANY,"mean statistics" ), wx.VERTICAL) for parameter in ['mean_type','dec','inc','alpha95','K','R','n_lines','n_planes']: COMMAND="self.%s_window=wx.TextCtrl(self.panel,style=wx.TE_CENTER|wx.TE_READONLY,size=(75*self.GUI_RESOLUTION,25))"%parameter exec COMMAND COMMAND="self.%s_window.SetBackgroundColour(wx.WHITE)"%parameter exec COMMAND COMMAND="self.%s_window.SetFont(font2)"%parameter exec COMMAND COMMAND="self.%s_outer_window = wx.GridSizer(1,2,5*self.GUI_RESOLUTION,15*self.GUI_RESOLUTION)"%parameter exec COMMAND COMMAND="""self.%s_outer_window.AddMany([ (wx.StaticText(self.panel,label='%s',style=wx.TE_CENTER),wx.EXPAND), (self.%s_window, wx.EXPAND)])"""%(parameter,parameter,parameter) exec COMMAND COMMAND="self.stats_sizer.Add(self.%s_outer_window, 1, wx.ALIGN_LEFT|wx.EXPAND, 0)"%parameter exec COMMAND self.switch_stats_button = wx.SpinButton(self.panel, id=wx.ID_ANY, style=wx.SP_HORIZONTAL|wx.SP_ARROW_KEYS|wx.SP_WRAP, name="change stats") self.Bind(wx.EVT_SPIN, self.on_select_stats_button,self.switch_stats_button) #construct panel hbox0 = wx.BoxSizer(wx.HORIZONTAL) hbox0.Add(self.name_sizer,flag=wx.ALIGN_TOP|wx.EXPAND,border=8) hbox0.Add(self.bounds_sizer,flag=wx.ALIGN_TOP|wx.EXPAND,border=8) vbox0 = wx.BoxSizer(wx.VERTICAL) vbox0.Add(hbox0,flag=wx.ALIGN_TOP,border=8) vbox0.Add(self.buttons_sizer,flag=wx.ALIGN_TOP,border=8) hbox1 = wx.BoxSizer(wx.HORIZONTAL) hbox1.Add(vbox0,flag=wx.ALIGN_TOP,border=8) hbox1.Add(self.stats_sizer,flag=wx.ALIGN_TOP,border=8) hbox1.Add(self.switch_stats_button,flag=wx.ALIGN_TOP|wx.EXPAND,border=8) vbox1 = wx.BoxSizer(wx.VERTICAL) vbox1.Add(self.display_sizer,flag=wx.ALIGN_TOP,border=8) vbox1.Add(hbox1,flag=wx.ALIGN_TOP,border=8) vbox1.Add(self.canvas,proportion=1,flag=wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,border=8) vbox2 = wx.BoxSizer(wx.VERTICAL) vbox2.Add(self.search_bar,proportion=.5,flag=wx.ALIGN_LEFT | wx.ALIGN_BOTTOM | wx.EXPAND, border=8) vbox2.Add(self.logger,proportion=1,flag=wx.ALIGN_LEFT|wx.EXPAND,border=8) hbox2 = wx.BoxSizer(wx.HORIZONTAL) hbox2.Add(vbox2,proportion=1,flag=wx.ALIGN_LEFT|wx.EXPAND) hbox2.Add(vbox1,flag=wx.ALIGN_TOP|wx.EXPAND) self.panel.SetSizerAndFit(hbox2) hbox2.Fit(self) ################################Logger Functions################################## def update_editor(self): """ updates the logger and plot on the interpretation editor window @param: changed_interpretation_parameters -> if the logger should be whipped and completely recalculated from scratch or not (default = True) """ self.fit_list = [] self.search_choices = [] for specimen in self.specimens_list: if specimen not in self.parent.pmag_results_data['specimens']: continue self.fit_list += [(fit,specimen) for fit in self.parent.pmag_results_data['specimens'][specimen]] self.logger.DeleteAllItems() offset = 0 for i in range(len(self.fit_list)): i -= offset v = self.update_logger_entry(i) if v == "s": offset += 1 def update_logger_entry(self,i): """ helper function that given a index in this objects fit_list parameter inserts a entry at that index @param: i -> index in fit_list to find the (specimen_name,fit object) tup that determines all the data for this logger entry. """ if i < len(self.fit_list): tup = self.fit_list[i] elif i < self.logger.GetItemCount(): self.logger.DeleteItem(i) return else: return coordinate_system = self.parent.COORDINATE_SYSTEM fit = tup[0] pars = fit.get(coordinate_system) fmin,fmax,n,ftype,dec,inc,mad = "","","","","","","" specimen = tup[1] if coordinate_system=='geographic': block_key = 'zijdblock_geo' elif coordinate_system=='tilt-corrected': block_key = 'zijdblock_tilt' else: block_key = 'zijdblock' name = fit.name if pars == {} and self.parent.Data[specimen][block_key] != []: fit.put(specimen, coordinate_system, self.parent.get_PCA_parameters(specimen,fit,fit.tmin,fit.tmax,coordinate_system,fit.PCA_type)) pars = fit.get(coordinate_system) elif self.parent.Data[specimen][block_key]==[]: spars = fit.get('specimen') fmin = fit.tmin fmax = fit.tmax if 'specimen_n' in spars.keys(): n = str(spars['specimen_n']) else: n = 'No Data' if 'calculation_type' in spars.keys(): ftype = spars['calculation_type'] else: ftype = 'No Data' dec = 'No Data' inc = 'No Data' mad = 'No Data' else: if 'measurement_step_min' in pars.keys(): fmin = str(fit.tmin) if 'measurement_step_max' in pars.keys(): fmax = str(fit.tmax) if 'specimen_n' in pars.keys(): n = str(pars['specimen_n']) if 'calculation_type' in pars.keys(): ftype = pars['calculation_type'] if 'specimen_dec' in pars.keys(): dec = "%.1f"%pars['specimen_dec'] if 'specimen_inc' in pars.keys(): inc = "%.1f"%pars['specimen_inc'] if 'specimen_mad' in pars.keys(): mad = "%.1f"%pars['specimen_mad'] if 'specimen_alpha95' in pars.keys(): a95 = "%.1f"%pars['specimen_alpha95'] if 'specimen_k' in pars.keys(): sk = "%.1f"%pars['specimen_k'] if 'specimen_r' in pars.keys(): sr2 = "%.1f"%pars['specimen_r_sq'] if self.search_query != "": entry = (specimen+name+fmin+fmax+n+ftype+dec+inc+mad).replace(" ","").lower() if self.search_query not in entry: self.fit_list.pop(i) if i < self.logger.GetItemCount(): self.logger.DeleteItem(i) return "s" for e in (specimen,name,fmin,fmax,n,ftype,dec,inc,mad): if e not in self.search_choices: self.search_choices.append(e) if i < self.logger.GetItemCount(): self.logger.DeleteItem(i) self.logger.InsertStringItem(i, str(specimen)) self.logger.SetStringItem(i, 1, name) self.logger.SetStringItem(i, 2, fmin) self.logger.SetStringItem(i, 3, fmax) self.logger.SetStringItem(i, 4, n) self.logger.SetStringItem(i, 5, ftype) self.logger.SetStringItem(i, 6, dec) self.logger.SetStringItem(i, 7, inc) self.logger.SetStringItem(i, 8, mad) self.logger.SetItemBackgroundColour(i,"WHITE") a,b = False,False if fit in self.parent.bad_fits: self.logger.SetItemBackgroundColour(i,"red") b = True if self.parent.current_fit == fit: self.logger.SetItemBackgroundColour(i,"LIGHT BLUE") self.logger_focus(i) self.current_fit_index = i a = True if a and b: self.logger.SetItemBackgroundColour(i,"red") def update_current_fit_data(self): """ updates the current_fit of the parent Zeq_GUI entry in the case of it's data being changed """ if self.current_fit_index: self.update_logger_entry(self.current_fit_index) def change_selected(self,new_fit): """ updates passed in fit or index as current fit for the editor (does not affect parent), if no parameters are passed in it sets first fit as current @param: new_fit -> fit object to highlight as selected """ if self.search_query and self.parent.current_fit not in map(lambda x: x[0], self.fit_list): return if self.current_fit_index == None: if not self.parent.current_fit: return for i,(fit,specimen) in enumerate(self.fit_list): if fit == self.parent.current_fit: self.current_fit_index = i break i = 0 if isinstance(new_fit, Fit): for i, (fit,speci) in enumerate(self.fit_list): if fit == new_fit: break elif type(new_fit) is int: i = new_fit elif new_fit != None: print('cannot select fit of type: ' + str(type(new_fit))) if self.current_fit_index != None and \ len(self.fit_list) > 0 and \ self.fit_list[self.current_fit_index][0] in self.parent.bad_fits: self.logger.SetItemBackgroundColour(self.current_fit_index,"") else: self.logger.SetItemBackgroundColour(self.current_fit_index,"WHITE") self.current_fit_index = i if self.fit_list[self.current_fit_index][0] in self.parent.bad_fits: self.logger.SetItemBackgroundColour(self.current_fit_index,"red") else: self.logger.SetItemBackgroundColour(self.current_fit_index,"LIGHT BLUE") def logger_focus(self,i,focus_shift=16): """ focuses the logger on an index 12 entries below i @param: i -> index to focus on """ if self.logger.GetItemCount()-1 > i+focus_shift: i += focus_shift else: i = self.logger.GetItemCount()-1 self.logger.Focus(i) def OnClick_listctrl(self, event): """ Edits the logger and the Zeq_GUI parent object to select the fit that was newly selected by a double click @param: event -> wx.ListCtrlEvent that triggered this function """ i = event.GetIndex() if self.parent.current_fit == self.fit_list[i][0]: return self.parent.initialize_CART_rot(self.fit_list[i][1]) si = self.parent.specimens.index(self.fit_list[i][1]) self.parent.specimens_box.SetSelection(si) self.parent.select_specimen(self.fit_list[i][1]) self.change_selected(i) fi = 0 while (self.parent.s == self.fit_list[i][1] and i >= 0): i,fi = (i-1,fi+1) self.parent.update_fit_box() self.parent.fit_box.SetSelection(fi-1) self.parent.update_selection() def OnRightClickListctrl(self, event): """ Edits the logger and the Zeq_GUI parent object so that the selected interpretation is now marked as bad @param: event -> wx.ListCtrlEvent that triggered this function """ i = event.GetIndex() fit = self.fit_list[i][0] if fit in self.parent.bad_fits: self.parent.bad_fits.remove(fit) if i == self.current_fit_index: self.logger.SetItemBackgroundColour(i,"LIGHT BLUE") else: self.logger.SetItemBackgroundColour(i,"WHITE") else: self.parent.bad_fits.append(fit) if i == self.current_fit_index: self.logger.SetItemBackgroundColour(i,"red") else: self.logger.SetItemBackgroundColour(i,"red") self.parent.calculate_higher_levels_data() self.parent.plot_higher_levels_data() self.logger_focus(i) ##################################Search Bar Functions############################### def on_enter_search_bar(self,event): self.search_query = self.search_bar.GetValue().replace(" ","").lower() self.update_editor() # def on_complete_search_bar(self,event): # self.search_bar.AutoComplete(self.search_choices) ###################################ComboBox Functions################################ def add_new_color(self,event): new_color = self.color_box.GetValue() if ':' in new_color: color_list = new_color.split(':') color_name = color_list[0] color_val = map(eval, tuple(color_list[1].strip('( )').split(','))) for val in color_val: if val > 1 or val < 0: print("invalid RGB sequence"); return else: return self.color_dict[color_name] = color_val #clear old box self.color_box.Clear() #update fit box self.color_box.SetItems([''] + self.color_dict.keys()) def on_select_coordinates(self,event): self.parent.coordinates_box.SetStringSelection(self.coordinates_box.GetStringSelection()) self.parent.onSelect_coordinates(event) def on_select_show_box(self,event): """ """ self.parent.UPPER_LEVEL_SHOW=self.show_box.GetValue() self.parent.calculate_higher_levels_data() self.parent.update_selection() def on_select_higher_level(self,event,called_by_parent=False): """ alters the possible entries in level_names combobox to give the user selections for which specimen interpretations to display in the logger @param: event -> the wx.COMBOBOXEVENT that triggered this function """ UPPER_LEVEL=self.level_box.GetValue() if UPPER_LEVEL=='sample': self.level_names.SetItems(self.parent.samples) self.level_names.SetStringSelection(self.parent.Data_hierarchy['sample_of_specimen'][self.parent.s]) if UPPER_LEVEL=='site': self.level_names.SetItems(self.parent.sites) self.level_names.SetStringSelection(self.parent.Data_hierarchy['site_of_specimen'][self.parent.s]) if UPPER_LEVEL=='location': self.level_names.SetItems(self.parent.locations) self.level_names.SetStringSelection(self.parent.Data_hierarchy['location_of_specimen'][self.parent.s]) if UPPER_LEVEL=='study': self.level_names.SetItems(['this study']) self.level_names.SetStringSelection('this study') if not called_by_parent: self.parent.level_box.SetStringSelection(UPPER_LEVEL) self.parent.onSelect_higher_level(event,True) self.on_select_level_name(event) def on_select_level_name(self,event,called_by_parent=False): """ change this objects specimens_list to control which specimen interpretatoins are displayed in this objects logger @param: event -> the wx.ComboBoxEvent that triggered this function """ high_level_name=str(self.level_names.GetValue()) if self.level_box.GetValue()=='sample': self.specimens_list=self.parent.Data_hierarchy['samples'][high_level_name]['specimens'] elif self.level_box.GetValue()=='site': self.specimens_list=self.parent.Data_hierarchy['sites'][high_level_name]['specimens'] elif self.level_box.GetValue()=='location': self.specimens_list=self.parent.Data_hierarchy['locations'][high_level_name]['specimens'] elif self.level_box.GetValue()=='study': self.specimens_list=self.parent.Data_hierarchy['study']['this study']['specimens'] if not called_by_parent: self.parent.level_names.SetStringSelection(high_level_name) self.parent.onSelect_level_name(event,True) self.specimens_list.sort(cmp=specimens_comparator) self.update_editor() def on_select_mean_type_box(self, event): """ set parent Zeq_GUI to reflect change in this box and change the @param: event -> the wx.ComboBoxEvent that triggered this function """ new_mean_type = self.mean_type_box.GetValue() if new_mean_type == "None": self.parent.clear_higher_level_pars() self.parent.mean_type_box.SetStringSelection(new_mean_type) self.parent.onSelect_mean_type_box(event) def on_select_mean_fit_box(self, event): """ set parent Zeq_GUI to reflect the change in this box then replot the high level means plot @param: event -> the wx.COMBOBOXEVENT that triggered this function """ new_mean_fit = self.mean_fit_box.GetValue() self.parent.mean_fit_box.SetStringSelection(new_mean_fit) self.parent.onSelect_mean_fit_box(event) ###################################Button Functions################################## def on_select_stats_button(self,event): """ """ i = self.switch_stats_button.GetValue() self.parent.switch_stats_button.SetValue(i) self.parent.update_higher_level_stats() def add_highlighted_fits(self, evnet): """ adds a new interpretation to each specimen highlighted in logger if multiple interpretations are highlighted of the same specimen only one new interpretation is added @param: event -> the wx.ButtonEvent that triggered this function """ specimens = [] next_i = self.logger.GetNextSelected(-1) if next_i == -1: return else: while next_i != -1: fit,specimen = self.fit_list[next_i] if specimen in specimens: next_i = self.logger.GetNextSelected(next_i) continue else: specimens.append(specimen) next_i = self.logger.GetNextSelected(next_i) for specimen in specimens: self.add_fit_to_specimen(specimen) self.update_editor() self.parent.update_selection() def add_fit_to_all(self,event): for specimen in self.parent.specimens: self.add_fit_to_specimen(specimen) self.update_editor() self.parent.update_selection() def add_fit_to_specimen(self,specimen): if specimen not in self.parent.pmag_results_data['specimens']: self.parent.pmag_results_data['specimens'][specimen] = [] new_name = self.name_box.GetLineText(0) new_color = self.color_box.GetValue() new_tmin = self.tmin_box.GetValue() new_tmax = self.tmax_box.GetValue() if not new_name: next_fit = str(len(self.parent.pmag_results_data['specimens'][specimen]) + 1) while ("Fit " + next_fit) in map(lambda x: x.name, self.parent.pmag_results_data['specimens'][specimen]): next_fit = str(int(next_fit) + 1) new_name = ("Fit " + next_fit) if not new_color: next_fit = str(len(self.parent.pmag_results_data['specimens'][specimen]) + 1) new_color = self.parent.colors[(int(next_fit)-1) % len(self.parent.colors)] else: new_color = self.color_dict[new_color] if not new_tmin: new_tmin = None if not new_tmax: new_tmax = None if new_name in map(lambda x: x.name, self.parent.pmag_results_data['specimens'][specimen]): print('-E- interpretation called ' + new_name + ' already exsists for specimen ' + specimen) return new_fit = Fit(new_name, new_tmax, new_tmin, new_color, self.parent) new_fit.put(specimen,self.parent.COORDINATE_SYSTEM,self.parent.get_PCA_parameters(specimen,new_fit,new_tmin,new_tmax,self.parent.COORDINATE_SYSTEM,"DE-BFL")) self.parent.pmag_results_data['specimens'][specimen].append(new_fit) def delete_highlighted_fits(self, event): """ iterates through all highlighted fits in the logger of this object and removes them from the logger and the Zeq_GUI parent object @param: event -> the wx.ButtonEvent that triggered this function """ next_i = -1 deleted_items = [] while True: next_i = self.logger.GetNextSelected(next_i) if next_i == -1: break deleted_items.append(next_i) deleted_items.sort(cmp=lambda x,y: y - x) for item in deleted_items: self.delete_entry(index=item) self.parent.update_selection() def delete_entry(self, fit = None, index = None): """ deletes the single item from the logger of this object that corrisponds to either the passed in fit or index. Note this function mutaits the logger of this object if deleting more than one entry be sure to pass items to delete in from highest index to lowest or else odd things can happen. @param: fit -> Fit object to delete from this objects logger @param: index -> integer index of the entry to delete from this objects logger """ if type(index) == int and not fit: fit,specimen = self.fit_list[index] if fit and type(index) == int: for i, (f,s) in enumerate(self.fit_list): if fit == f: index,specimen = i,s break if index == self.current_fit_index: self.current_fit_index = None if fit not in self.parent.pmag_results_data['specimens'][specimen]: print("cannot remove item (entry #: " + str(index) + ") as it doesn't exist, this is a dumb bug contact devs") self.logger.DeleteItem(index) return self.parent.pmag_results_data['specimens'][specimen].remove(fit) del self.fit_list[index] self.logger.DeleteItem(index) def apply_changes(self, event): """ applies the changes in the various attribute boxes of this object to all highlighted fit objects in the logger, these changes are reflected both in this object and in the Zeq_GUI parent object. @param: event -> the wx.ButtonEvent that triggered this function """ new_name = self.name_box.GetLineText(0) new_color = self.color_box.GetValue() new_tmin = self.tmin_box.GetValue() new_tmax = self.tmax_box.GetValue() next_i = -1 changed_i = [] while True: next_i = self.logger.GetNextSelected(next_i) if next_i == -1: break specimen = self.fit_list[next_i][1] fit = self.fit_list[next_i][0] if new_name: if new_name not in map(lambda x: x.name, self.parent.pmag_results_data['specimens'][specimen]): fit.name = new_name if new_color: fit.color = self.color_dict[new_color] #testing not_both = True if new_tmin and new_tmax: if fit == self.parent.current_fit: self.parent.tmin_box.SetStringSelection(new_tmin) self.parent.tmax_box.SetStringSelection(new_tmax) fit.put(specimen,self.parent.COORDINATE_SYSTEM, self.parent.get_PCA_parameters(specimen,fit,new_tmin,new_tmax,self.parent.COORDINATE_SYSTEM,fit.PCA_type)) not_both = False if new_tmin and not_both: if fit == self.parent.current_fit: self.parent.tmin_box.SetStringSelection(new_tmin) fit.put(specimen,self.parent.COORDINATE_SYSTEM, self.parent.get_PCA_parameters(specimen,fit,new_tmin,fit.tmax,self.parent.COORDINATE_SYSTEM,fit.PCA_type)) if new_tmax and not_both: if fit == self.parent.current_fit: self.parent.tmax_box.SetStringSelection(new_tmax) fit.put(specimen,self.parent.COORDINATE_SYSTEM, self.parent.get_PCA_parameters(specimen,fit,fit.tmin,new_tmax,self.parent.COORDINATE_SYSTEM,fit.PCA_type)) changed_i.append(next_i) offset = 0 for i in changed_i: i -= offset v = self.update_logger_entry(i) if v == "s": offset += 1 self.parent.update_selection() ###################################Canvas Functions################################## def scatter(self,*args,**kwargs): # args_corrected = self.eqarea.transAxes.transform(vstack(args).T) # x,y = args_corrected.T self.eqarea.scatter(*args,**kwargs) def plot(self,*args,**kwargs): # args_corrected = self.eqarea.transAxes.transform(vstack(args).T) # x,y = args_corrected.T self.eqarea.plot(*args,**kwargs) def write(self,text): self.eqarea.text(-1.2,1.15,text,{'family':self.font_type, 'fontsize':10*self.GUI_RESOLUTION, 'style':'normal','va':'center', 'ha':'left' }) def draw_net(self): draw_net(self.eqarea) def draw(self): self.toolbar.home() self.eqarea.set_xlim(-1., 1.) self.eqarea.set_ylim(-1., 1.) self.eqarea.axes.set_aspect('equal') self.eqarea.axis('off') self.canvas.draw() def pan_zoom_higher_equalarea(self,event): """ Uses the toolbar for the canvas to change the function from zoom to pan or pan to zoom @param: event -> the wx.MouseEvent that triggered this funciton """ if event.LeftIsDown() or event.ButtonDClick(): return elif self.higher_EA_setting == "Zoom": self.higher_EA_setting = "Pan" try: self.toolbar.pan('off') except TypeError: pass elif self.higher_EA_setting == "Pan": self.higher_EA_setting = "Zoom" try: self.toolbar.zoom() except TypeError: pass else: self.higher_EA_setting = "Zoom" try: self.toolbar.zoom() except TypeError: pass def home_higher_equalarea(self,event): """ returns higher equal area to it's original position @param: event -> the wx.MouseEvent that triggered the call of this function @alters: toolbar setting """ self.toolbar.home() def on_change_higher_mouse_cursor(self,event): """ If mouse is over data point making it selectable change the shape of the cursor @param: event -> the wx Mouseevent for that click """ if self.show_box.GetValue() != "specimens": return if not self.parent.higher_EA_xdata or not self.parent.higher_EA_ydata: return pos=event.GetPosition() width, height = self.canvas.get_width_height() pos[1] = height - pos[1] xpick_data,ypick_data = pos xdata_org = self.parent.higher_EA_xdata ydata_org = self.parent.higher_EA_ydata data_corrected = self.eqarea.transData.transform(vstack([xdata_org,ydata_org]).T) xdata,ydata = data_corrected.T xdata = map(float,xdata) ydata = map(float,ydata) e = 4e0 if self.higher_EA_setting == "Zoom": self.canvas.SetCursor(wx.StockCursor(wx.CURSOR_CROSS)) elif self.higher_EA_setting == "Pan": self.canvas.SetCursor(wx.StockCursor(wx.CURSOR_WATCH)) else: self.canvas.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) for i,(x,y) in enumerate(zip(xdata,ydata)): if 0 < sqrt((x-xpick_data)**2. + (y-ypick_data)**2.) < e: self.canvas.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) break def on_equalarea_higher_select(self,event): self.parent.on_equalarea_higher_select(event,fig = self.eqarea, canvas = self.canvas) ###############################Window Functions###################################### def on_close_edit_window(self, event): """ the function that is triggered on the close of the interpretation editor window @param: event -> wx.WindowEvent that triggered this function """ self.parent.ie_open = False self.Destroy()
class PVWindow(wx.Frame): #########################Init Funcions############################# def __init__(self,parent=None,dpi=100,geoid=Geodesic.WGS84,fontsize=8): """Constructor""" #call init of super class default_style = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.NO_FULL_REPAINT_ON_RESIZE | wx.WS_EX_CONTEXTHELP | wx.FRAME_EX_CONTEXTHELP wx.Frame.__init__(self, parent, title="Phase Viewer %s"%parent.__version__,style=default_style, size=(400*2,300*2)) self.Bind(wx.EVT_CLOSE, self.on_close_main) self.parent=parent self.dpi = dpi self.geoid = geoid self.fontsize = fontsize self.mouse_left_down = False self.panel = wx.Panel(self,-1,size=(400*2,300*2)) #Populate UI and Menu self.init_UI() self.create_menu() self.configure() self.update() def init_UI(self): spacing = 10 #-----------------------------------------Make SideBar-----------------------------------------------------# side_bar_sizer = wx.BoxSizer(wx.VERTICAL) #####-------------------------------------Make FilterBox---------------------------------------------------# filter_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "Filter Options"), wx.VERTICAL) highlow_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "Highcut/Lowcut"), wx.HORIZONTAL) order_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "Order"), wx.HORIZONTAL) text_input_sizer = wx.BoxSizer(wx.HORIZONTAL) null_filter = lambda x,y,z,fs,order: x self.filters = {"None":null_filter,"Butterworth Bandpass":sk.butter_bandpass_filter,"Butterworth Lowpass":sk.butter_lowpass_filter} list_filters = ["None","Butterworth Bandpass","Butterworth Lowpass"] self.filter_type_box = wx.ComboBox(self.panel, id=wx.ID_ANY,size=(200, 25), value=list_filters[0], choices=list_filters, style=wx.CB_DROPDOWN|wx.TE_READONLY) self.Bind(wx.EVT_COMBOBOX, self.on_select_filter,self.filter_type_box) self.lowcut_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(50,25)) self.highcut_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(50,25)) self.order_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(50,25)) order_sizer.Add(self.order_box, 1, wx.ALIGN_RIGHT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing) highlow_sizer.AddMany([(self.lowcut_box, 1, wx.ALIGN_LEFT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing), (self.highcut_box, 1, wx.ALIGN_RIGHT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing)]) text_input_sizer.AddMany([(highlow_sizer, 2, wx.ALIGN_LEFT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing), (order_sizer, 1, wx.ALIGN_RIGHT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing)]) filter_sizer.AddMany([(self.filter_type_box, 1, wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing), (text_input_sizer, 1, wx.ALIGN_CENTER|wx.ALIGN_BOTTOM|wx.EXPAND|wx.ALL, spacing)]) #####-------------------------------------Make BoundsBox---------------------------------------------------# bounds_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "Bounds"), wx.HORIZONTAL) bounds_diff_sizer = wx.BoxSizer(wx.HORIZONTAL) self.low_bound_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(50,25)) self.high_bound_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(50,25)) self.aero_diff_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(50,25)) bounds_sizer.AddMany([(self.low_bound_box, 1, wx.ALIGN_LEFT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing), (self.high_bound_box, 1, wx.ALIGN_RIGHT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing)]) bounds_diff_sizer.AddMany([(bounds_sizer, 2, wx.ALIGN_LEFT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing), (self.aero_diff_box, 1, wx.ALIGN_RIGHT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing)]) #####-------------------------------------Make UpdateBtn---------------------------------------------------# self.update_button = wx.Button(self.panel, id=wx.ID_ANY, label='Update',size=(200,50)) self.Bind(wx.EVT_BUTTON, self.on_update_button, self.update_button) #-------------------------------------Make Figures---------------------------------------------------------# self.power_fig = Figure((3, 3), dpi=self.dpi) self.power_canvas = FigCanvas(self.panel, -1, self.power_fig) self.power_toolbar = NavigationToolbar(self.power_canvas) # psk.remove_axis_lines_and_ticks(self.ax) self.power_toolbar.Hide() self.power_plot_setting = "Zoom" self.power_toolbar.zoom() self.power_canvas.Bind(wx.EVT_MIDDLE_DOWN,self.on_middle_click_power_plot) self.fig = Figure((4, 3), dpi=self.dpi) self.canvas = FigCanvas(self.panel, -1, self.fig) self.toolbar = NavigationToolbar(self.canvas) # psk.remove_axis_lines_and_ticks(self.ax) self.toolbar.Hide() self.plot_setting = "Zoom" self.toolbar.zoom() self.canvas.Bind(wx.EVT_MIDDLE_DOWN,self.on_middle_click_plot) self.canvas.Bind(wx.EVT_MOTION,self.on_move_mouse_plot) self.canvas.Bind(wx.EVT_LEFT_DOWN, self.on_left_click_down) self.canvas.Bind(wx.EVT_LEFT_UP, self.on_left_click_up) # self.canvas.Bind(wx.EVT_RIGHT_DCLICK, self.on_select_dright_click) #----------------------------------Build UI and Fit--------------------------------------------------------# side_bar_sizer.AddMany([(filter_sizer, 1, wx.ALIGN_LEFT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing), (bounds_diff_sizer, 1, wx.ALIGN_LEFT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing), (self.update_button, 1, wx.ALIGN_LEFT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing), (self.power_canvas, 1, wx.ALIGN_LEFT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing)]) outer_sizer = wx.BoxSizer(wx.HORIZONTAL) outer_sizer.AddMany([#(grd_sizer,1,wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND|wx.LEFT|wx.RIGHT,spacing), (side_bar_sizer,1,wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND), (self.canvas,10,wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND)]) self.panel.SetSizerAndFit(outer_sizer) def create_menu(self): """ Generates Menu """ self.menubar = wx.MenuBar() #----------------- # File Menu #----------------- menu_file = wx.Menu() menu_file.AppendSeparator() m_exit = menu_file.Append(-1, "&Exit\tCtrl-Q", "Exit") self.Bind(wx.EVT_MENU, self.on_close_main, m_exit) #----------------- self.menubar.Append(menu_file, "&File") self.SetMenuBar(self.menubar) def configure(self): self.lowcut_box.SetValue("%.2f"%0.01) self.highcut_box.SetValue("%.2f"%0.1) self.order_box.SetValue("%d"%3) self.low_bound_box.SetValue("%.2f"%(-50.0)) self.high_bound_box.SetValue("%.2f"%50.0) self.aero_diff_box.SetValue("%.1f"%0.0) #########################Update UI Funcions############################# def update(self): self.fig.clear() #clear self.power_fig.clear() self.draw_figures() #draw self.canvas.draw() #rerender self.power_canvas.draw() def on_close_main(self,event): self.parent.pv_open=False self.Destroy() ###################Button and Dropdown Functions######################### # def on_change_grd_btn(self,event): # dlg = wx.FileDialog( # self, message="Choose Grid File", # defaultDir=self.parent.WD, # defaultFile="", # wildcard="Grid Files (*.grd,*.nc,*.ncf)|*.grd;*.nc;*.ncf|All Files (*.*)|*.*", # style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST # ) # if dlg.ShowModal() == wx.ID_OK: # self.grd_file = dlg.GetPath() # self.grd_path.SetValue(self.grd_file) # self.update() # dlg.Destroy() # else: dlg.Destroy() def on_select_filter(self,event): self.update() def on_update_button(self,event): self.update() ###########################Figure Funcions############################### def on_middle_click_plot(self,event): if event.LeftIsDown() or event.ButtonDClick(): event.Skip(); return elif self.plot_setting == "Zoom": self.plot_setting = "Pan" self.toolbar.pan('off') elif self.plot_setting == "Pan": self.plot_setting = "Pick" self.toolbar.pan('off') myCursor= wx.StockCursor(wx.CURSOR_IBEAM) self.canvas.SetCursor(myCursor) elif self.plot_setting == "Pick": self.plot_setting = "Zoom" self.toolbar.zoom() event.Skip() def on_middle_click_power_plot(self,event): if event.LeftIsDown() or event.ButtonDClick(): event.Skip(); return elif self.power_plot_setting == "Zoom": self.power_plot_setting = "Pan" self.power_toolbar.pan('off') elif self.power_plot_setting == "Pan": self.power_plot_setting = "Zoom" self.power_toolbar.zoom() event.Skip() def on_move_mouse_plot(self,event): pos=event.GetPosition() width, height = self.canvas.get_width_height() pos = [pos[0],height-pos[1]] pos = self.ax0.transData.inverted().transform(pos) # self.annotate_point(pos,xy=(1-0.02,1-0.02),xycoords="axes fraction",bbox=dict(boxstyle="round", fc="w",alpha=.5),fontsize=self.fontsize,va='top',ha='right') self.plot_tracer_point(pos[0],linestyle='--',color='red',alpha=.5) self.canvas.draw() event.Skip() def on_left_click_down(self,event): if self.plot_setting!="Pick": event.Skip(); return pos=event.GetPosition() width, height = self.canvas.get_width_height() pos = [pos[0],height-pos[1]] pos = self.ax0.transData.inverted().transform(pos) self.tmp_new_bounds = [pos[0],None] self.mouse_left_down = True event.Skip() def on_left_click_up(self,event): if self.plot_setting!="Pick": event.Skip(); return pos=event.GetPosition() width, height = self.canvas.get_width_height() pos = [pos[0],height-pos[1]] pos = self.ax0.transData.inverted().transform(pos) self.tmp_new_bounds[1] = pos[0] self.mouse_left_down = False if abs(self.tmp_new_bounds[1]-self.tmp_new_bounds[0])>0: self.low_bound_box.SetValue("%.1f"%float(min(self.tmp_new_bounds))) self.high_bound_box.SetValue("%.1f"%float(max(self.tmp_new_bounds))) self.update() event.Skip() ##########################Additional Plotting and Backend Functions################ def on_parent_select_track(self): self.update() def plot_tracer_point(self,x,**kwargs): try: for tracer in self.tracers: tracer.remove() except (AttributeError,ValueError) as e: pass self.tracers = [] self.tracers.append(self.ax0.axvline(x,**kwargs)) self.tracers.append(self.ax1.axvline(x,**kwargs)) self.tracers.append(self.ax2.axvline(x,**kwargs)) if self.mouse_left_down: myCursor= wx.StockCursor(wx.CURSOR_IBEAM) self.canvas.SetCursor(myCursor) self.tracers.append(self.ax0.axvspan(min([self.tmp_new_bounds[0],x]),max([self.tmp_new_bounds[0],x]),color="yellow",alpha=.4)) self.tracers.append(self.ax1.axvspan(min([self.tmp_new_bounds[0],x]),max([self.tmp_new_bounds[0],x]),color="yellow",alpha=.4)) self.tracers.append(self.ax2.axvspan(min([self.tmp_new_bounds[0],x]),max([self.tmp_new_bounds[0],x]),color="yellow",alpha=.4)) def draw_figures(self): ####################################################Get Values dsk_row = self.parent.dsk_row track = self.parent.track ddis = float(self.parent.samp_dis_box.GetValue()) if ddis==0: self.parent.user_warning("Synthetic is required for comparision of phase, start by initalilzing a synthetic"); return synth_dis = self.parent.dis_synth synth_mag = self.parent.synth filter_type = self.filter_type_box.GetValue() lowcut = float(self.lowcut_box.GetValue()) highcut = float(self.highcut_box.GetValue()) order = int(self.order_box.GetValue()) left_bound = float(self.low_bound_box.GetValue()) right_bound = float(self.high_bound_box.GetValue()) aero_diff = float(self.aero_diff_box.GetValue()) left_idx = np.argmin(np.abs(synth_dis-left_bound)) right_idx = np.argmin(np.abs(synth_dis-right_bound)) left_idx,right_idx = min([left_idx,right_idx]),max([left_idx,right_idx]) bin_range,bin_num = (-180,180),120 ###################################################Filter Data data_path = os.path.join(dsk_row["data_dir"],dsk_row["comp_name"]) data_df = utl.open_mag_file(data_path) projected_distances = utl.calc_projected_distance(dsk_row['inter_lon'],dsk_row['inter_lat'],data_df['lon'].tolist(),data_df['lat'].tolist(),(180+dsk_row['strike'])%360) shifted_mag = sk.phase_shift_data(data_df["mag"],dsk_row["phase_shift"]) if np.any(np.diff(projected_distances["dist"])<0): itshifted_mag = np.interp(-synth_dis,-projected_distances["dist"],shifted_mag) else: itshifted_mag = np.interp(synth_dis,projected_distances["dist"],shifted_mag) fitshifted_mag = self.filters[filter_type](itshifted_mag,lowcut,highcut,fs=1/ddis,order=order) ###################################################Actual Plotting outer = gridspec.GridSpec(4, 1) ###################################################Axis 0: Magnetic profiles self.ax0 = self.fig.add_subplot(outer[0]) if self.parent.show_other_comp: #Handle Other Aeromag Component if dsk_row["track_type"]=="aero": if "Ed.lp" in track: other_track = track.replace("Ed.lp","Vd.lp") total_track = track.replace("Ed.lp","Td.lp") other_phase = dsk_row["phase_shift"]-90 elif "Hd.lp" in track: other_track = track.replace("Hd.lp","Vd.lp") total_track = track.replace("Hd.lp","Td.lp") other_phase = dsk_row["phase_shift"]-90 elif "Vd.lp" in track: other_track = track.replace("Vd.lp","Ed.lp") total_track = track.replace("Vd.lp","Td.lp") if other_track not in self.parent.deskew_df["comp_name"].tolist(): other_track = track.replace("Vd.lp","Hd.lp") other_phase = dsk_row["phase_shift"]+90 else: self.parent.user_warning("Improperly named component files should have either Ed.lp, Hd.lp, or Vd.lp got: %s"%track); return oth_row = self.parent.deskew_df[self.parent.deskew_df["comp_name"]==other_track].iloc[0] oth_data_path = os.path.join(oth_row["data_dir"],oth_row["comp_name"]) tot_data_path = os.path.join(oth_row["data_dir"],total_track) #Should be in same place oth_data_df = utl.open_mag_file(oth_data_path) oth_shifted_mag = sk.phase_shift_data(oth_data_df["mag"],other_phase) if np.any(np.diff(projected_distances["dist"])<0): oth_itshifted_mag = np.interp(-synth_dis,-projected_distances["dist"],oth_shifted_mag) else: oth_itshifted_mag = np.interp(synth_dis,projected_distances["dist"],oth_data_df) oth_fitshifted_mag = self.filters[filter_type](oth_itshifted_mag,lowcut,highcut,fs=1/ddis,order=order) if filter_type=="None": psk.plot_skewness_data(oth_row,other_phase,self.ax0,xlims=[None,None],color='darkgreen',zorder=2,picker=True,alpha=.7,return_objects=True,flip=True) else: self.ax0.plot(synth_dis,oth_fitshifted_mag,color="#299C29",zorder=3,alpha=.6) tot_data_df = utl.open_mag_file(tot_data_path) if np.any(np.diff(projected_distances["dist"])<0): tot_imag = np.interp(-synth_dis,-projected_distances["dist"],tot_data_df["mag"]) else: tot_imag = np.interp(synth_dis,projected_distances["dist"],tot_data_df["mag"]) tot_fimag = self.filters[filter_type](tot_imag,lowcut,highcut,fs=1/ddis,order=order) if filter_type=="None": psk.plot_skewness_data(dsk_row,dsk_row["phase_shift"],self.ax0,xlims=[None,None],zorder=3,picker=True,return_objects=True,flip=True) else: self.ax0.plot(synth_dis,fitshifted_mag,color="#7F7D7D",zorder=3,alpha=.6) self.ax0.plot(self.parent.dis_synth,self.parent.synth,'r-',alpha=.4,zorder=1) self.ax0.set_ylabel("Magnetic Profiles") # self.ax0.get_xaxis().set_ticklabels([]) ###################################################Axis 1/2: Phase Angles and Differences self.ax1 = self.fig.add_subplot(outer[1], sharex=self.ax0) self.ax2 = self.fig.add_subplot(outer[2], sharex=self.ax0) ###################################################Calculate: Phase Differences trimmed_dis = synth_dis[left_idx:right_idx] trimmed_synth = synth_mag[left_idx:right_idx] trimmed_fitshifted_mag = fitshifted_mag[left_idx:right_idx] al_data = np.angle(hilbert(fitshifted_mag),deg=False)[left_idx:right_idx] al_synth = np.angle(hilbert(np.real(synth_mag)),deg=False)[left_idx:right_idx] data_synth_diff = phase_diff_func(al_synth,al_data) if self.parent.show_other_comp and dsk_row["track_type"]=="aero": trimmed_oth_fitshifted_mag = oth_fitshifted_mag[left_idx:right_idx] al_oth = np.angle(hilbert(oth_fitshifted_mag),deg=False)[left_idx:right_idx] oth_synth_diff = phase_diff_func(al_synth,al_oth) oth_data_diff = phase_diff_func(al_oth,al_data) if abs(aero_diff) > 0: idx = ma.array(np.abs(oth_data_diff)<aero_diff) self.ax1.plot((trimmed_dis[~idx]),(np.rad2deg(al_oth[~idx])),color="darkgreen",linestyle=":") self.ax2.plot((trimmed_dis[~idx]),(oth_synth_diff[~idx]),color="tab:pink",alpha=.8,linestyle=":") self.ax2.plot((trimmed_dis[~idx]),(oth_data_diff[~idx]),color="tab:grey",alpha=.8,linestyle=":") self.ax1.plot((trimmed_dis[~idx]),(np.rad2deg(al_data[~idx])),color="k",linestyle=":") self.ax1.plot((trimmed_dis[~idx]),(np.rad2deg(al_synth[~idx])),color="r",linestyle=":") self.ax2.plot((trimmed_dis[~idx]),(data_synth_diff[~idx]),color="tab:red",alpha=.8,linestyle=":") # import pdb; pdb.set_trace() # not_trimmed_dis = (trimmed_dis[~idx]) # not_trimmed_dis[np.diff(~idx,prepend=[0])] = ma.masked # not_al_data = (al_data[~idx]) # not_al_data[np.diff(~idx)] = ma.masked # not_al_synth = (al_synth[~idx]) # not_al_synth[np.diff(~idx)] = ma.masked # not_al_oth = (al_oth[~idx]) # not_al_oth[np.diff(~idx)] = ma.masked # not_data_synth_diff = (data_synth_diff[~idx]) # not_data_synth_diff[np.diff(~idx)] = ma.masked # not_oth_synth_diff = (oth_synth_diff[~idx]) # not_oth_synth_diff[np.diff(~idx)] = ma.masked # not_oth_data_diff = (oth_data_diff[~idx]) # not_oth_data_diff[np.diff(~idx)] = ma.masked trimmed_dis = (trimmed_dis[idx]) al_data = (al_data[idx]) al_synth = (al_synth[idx]) al_oth = (al_oth[idx]) data_synth_diff = (data_synth_diff[idx]) oth_synth_diff = (oth_synth_diff[idx]) oth_data_diff = (oth_data_diff[idx]) self.ax1.plot(trimmed_dis,np.rad2deg(al_oth),color="darkgreen") self.ax2.plot(trimmed_dis,oth_synth_diff,color="tab:pink",alpha=.8) self.ax2.plot(trimmed_dis,oth_data_diff,color="tab:grey",alpha=.8) self.ax1.plot(trimmed_dis,np.rad2deg(al_data),color="k") self.ax1.plot(trimmed_dis,np.rad2deg(al_synth),color="r") self.ax2.plot(trimmed_dis,data_synth_diff,color="tab:red",alpha=.8) # self.ax2.get_xaxis.set_ticklabels self.ax0.set_xlim(*self.parent.ax.get_xlim()) self.ax0.set_ylim(*self.parent.ax.get_ylim()) self.ax1.set_ylabel("Phase Angles") self.ax2.set_ylabel("Phase Differences") ###################################################Axis 2.1: Power Spectrum # inner = gridspec.GridSpecFromSubplotSpec(1, 2, subplot_spec=outer[2])#, hspace=0.) # self.ax2 = self.fig.add_subplot(inner[0]) ###################################################Axis 2.2: Phase Statistics self.ax3 = self.fig.add_subplot(outer[3]) if self.parent.show_other_comp and dsk_row["track_type"]=="aero": self.ax3.hist(oth_synth_diff,range=bin_range,bins=bin_num,color="tab:pink",alpha=.5,zorder=2) self.ax3.hist(oth_data_diff,range=bin_range,bins=bin_num,color="tab:grey",alpha=.5,zorder=1) self.ax3.hist(data_synth_diff,range=bin_range,bins=bin_num,color="tab:red",alpha=.5,zorder=3) self.ax3.axvline(np.median(data_synth_diff),color="k",alpha=.5,zorder=5,linestyle=":") self.ax3.axvline(np.mean(data_synth_diff),color="k",alpha=.5,zorder=5) self.ax3.axvspan(np.mean(data_synth_diff)-np.std(data_synth_diff),np.mean(data_synth_diff)+np.std(data_synth_diff),color="tab:grey",alpha=.3,zorder=0) self.ax3.annotate(r"$\theta_{mean}$ = $%.1f^\circ \pm %.1f^\circ$"%(np.mean(data_synth_diff),np.std(data_synth_diff)) + "\n" + r"$\theta_{median}$ = %.1f$^\circ$"%np.median(data_synth_diff),xy=(0.02,1-0.02),xycoords="axes fraction",bbox=dict(boxstyle="round", fc="w",alpha=.5),fontsize=self.fontsize,va='top',ha='left') self.ax3.set_ylabel(r"$\Delta \theta$ Count") self.fig.suptitle("%s\n%s\n"%(dsk_row["sz_name"],track)) ###################################################Power Figure N = (right_idx-left_idx) #Length of signal in distance domain NW = 3 #following Parker and O'brien '97 and HJ-Gordon '03 we use a time-bandwith product of 6 (Nw is half) Ns = 5 #Number of points to use in running average smoothing #Handle Distance Domain # import pdb; pdb.set_trace() Sk_complex, weights, eigenvalues=pmtm(itshifted_mag[left_idx:right_idx], NW=NW, NFFT=N, show=False) Sk = np.abs(Sk_complex)**2 smoothed_tshifted_freq = (np.mean(Sk * np.transpose(weights), axis=0) * ddis)[N//2:][::-1] # smoothed_tshifted_freq = np.convolve(smoothed_tshifted_freq, np.ones((Ns,))/Ns, mode='same') #10 point running average smoothing tdata_freqs = np.linspace(0.0, 1.0/(2.0*ddis), N-N//2) #0 to Nyquest self.power_ax = self.power_fig.add_subplot(111) if self.parent.show_other_comp and dsk_row["track_type"]=="aero": Sk_complex, weights, eigenvalues=pmtm(oth_itshifted_mag[left_idx:right_idx], NW=NW, NFFT=N, show=False) Sk = np.abs(Sk_complex)**2 oth_smoothed_tshifted_freq = (np.mean(Sk * np.transpose(weights), axis=0) * ddis)[N//2:][::-1] # oth_smoothed_tshifted_freq = np.convolve(oth_smoothed_tshifted_freq, np.ones((Ns,))/Ns, mode='same') #10 point running average smoothing self.power_ax.semilogy(tdata_freqs, oth_smoothed_tshifted_freq, color="darkgreen") # self.power_ax.semilogy(tdata_freqs, oth_smoothed_tshifted_freq+smoothed_tshifted_freq, color="grey") Sk_complex, weights, eigenvalues=pmtm(tot_imag[left_idx:right_idx], NW=NW, NFFT=N, show=False) Sk = np.abs(Sk_complex)**2 tot_smoothed_tshifted_freq = (np.mean(Sk * np.transpose(weights), axis=0) * ddis)[N//2:][::-1] # tot_smoothed_tshifted_freq = np.convolve(tot_smoothed_tshifted_freq, np.ones((Ns,))/Ns, mode='same') #10 point running average smoothing self.power_ax.semilogy(tdata_freqs, tot_smoothed_tshifted_freq, color="tab:orange") #Old Numpy Method # synth_freqs = np.fft.fftfreq(len(synth_dis[left_idx:right_idx]),ddis) # tdata_freqs = np.fft.fftfreq(len(shifted_mag[left_idx:right_idx]),ddis) # tshifted_freq = np.fft.fft(shifted_mag[left_idx:right_idx]) # fitshifted_freq = np.fft.fft(fitshifted_mag[left_idx:right_idx]) # tsynth_freq = np.fft.fft(synth_mag[left_idx:right_idx]) self.power_ax.semilogy(tdata_freqs, smoothed_tshifted_freq, color="k",zorder=100) # self.power_ax.semilogy(tdata_freqs, np.abs(tshifted_freq), color="k") # self.power_ax.plot(synth_freqs, np.abs(fitshifted_freq), color="#7F7D7D") # self.power_ax.plot(synth_freqs, np.abs(tsynth_freq), color="r") self.power_ax.set_xlim(0.0,0.4) self.power_ax.set_ylim(1e-1,1e6)
class FigurePanel(wx.Panel): """ Display panel for a matplotlib figure. """ def __init__(self, parent, view, **kwargs): """ Initialize panel and connect the view. Extends wx.App.__init__. """ self.hook = HookCollection( init='madgui.widget.figure.init', capture_mouse=None) super(FigurePanel, self).__init__(parent, **kwargs) self.capturing = False self.view = view # couple figure to canvas self.canvas = Canvas(self, -1, view.figure.figure) view.canvas = self.canvas # create a toolbar self.toolbar = Toolbar(self.canvas) self.hook.init(self) self.toolbar.Realize() # put elements into sizer sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas, 1, wx.EXPAND) sizer.Add(self.toolbar, 0 , wx.LEFT | wx.EXPAND) self.SetSizer(sizer) # setup mouse capturing self.hook.capture_mouse.connect(self.on_capture_mouse) self.toolbar.Bind(wx.EVT_TOOL, self.on_zoom_or_pan, id=self.get_zoom_id()) self.toolbar.Bind(wx.EVT_TOOL, self.on_zoom_or_pan, id=self.get_pan_id()) # get notified when frame is destroyed self.Bind(wx.EVT_WINDOW_DESTROY, self.on_destroy) def on_destroy(self, event): """Invoked when C++ window is destroyed.""" self.view.destroy() def on_zoom_or_pan(self, event): """Capture mouse, after Zoom/Pan tools were clicked.""" if event.IsChecked(): self.capturing = True self.hook.capture_mouse() self.capturing = False event.Skip() def on_capture_mouse(self): """Disable Zoom/Pan tools when someone captures the mouse.""" if self.capturing: return zoom_id = self.get_zoom_id() if self.toolbar.GetToolState(zoom_id): self.toolbar.zoom() self.toolbar.ToggleTool(zoom_id, False) pan_id = self.get_pan_id() if self.toolbar.GetToolState(pan_id): self.toolbar.pan() self.toolbar.ToggleTool(pan_id, False) def get_pan_id(self): try: return self.toolbar.wx_ids['Pan'] except AttributeError: return self.toolbar._NTB2_PAN def get_zoom_id(self): try: return self.toolbar.wx_ids['Zoom'] except AttributeError: return self.toolbar._NTB2_ZOOM
def pan(self, ev): if wx.Platform == "__WXMAC__": self.ToggleTool(self.wx_ids["Pan"], self.GetToolState(self.wx_ids["Pan"])) NavToolbar.pan(self, ev)
class MatplotlibVisualizer(StageVisualizer): """"Subclass of StageVisualizer that utilizes matplotlib""" def __init__(self, parent): """Create a StageVisualizer with matplotlib essentials""" super(StageVisualizer, self).__init__(parent) self.figure = matplotlib.figure.Figure() self.axes = self.figure.add_subplot(111) self.figure.subplots_adjust(top=1, bottom=0, right=1, left=0) self.axes.get_xaxis().set_visible(False) self.axes.get_yaxis().set_visible(False) self.canvas = FigureCanvas(self, -1, self.figure) self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) self.SetSizer(self.sizer) self.toolbar = NavigationToolbar(self.canvas) self.toolbar.pan() self.toolbar.Hide() self.Fit() self.zoomer = self.zoom_factory(self.axes, base_scale=1.5) self.canvas.Bind(wx.EVT_PAINT, self.on_paint) def on_paint(self, evt): """ Redraws the canvas so we can see real time zooming :param evt: For binding wx.EVT_PAINT to this function :return: wx.PaintDC object so subclasses can call super.on_paint(evt) instead of copy/pasting the contents of this function """ dc = wx.PaintDC(self.canvas) # First, draw the graph with matplotlib self.canvas.draw(dc) return dc def zoom_factory(self, ax, base_scale=2.): """ Handle zooming using the scroll wheel. Original source: https://gist.github.com/tacaswell/3144287 """ def zoom_fun(event): # Get the current x and y limits cur_xlim = ax.get_xlim() cur_ylim = ax.get_ylim() cur_xrange = (cur_xlim[1] - cur_xlim[0])*.5 cur_yrange = (cur_ylim[1] - cur_ylim[0])*.5 # Get event location xdata = event.xdata ydata = event.ydata if event.button == 'up': # Deal with zoom in scale_factor = 1/base_scale elif event.button == 'down': # Deal with zoom out scale_factor = base_scale else: # Deal with something that should never happen scale_factor = 1 print event.button # Set new limits - improved from original ax.set_xlim([xdata - (xdata - cur_xlim[0])*scale_factor, xdata + (cur_xlim[1] - xdata)*scale_factor]) ax.set_ylim([ydata - (ydata - cur_ylim[0])*scale_factor, ydata + (cur_ylim[1] - ydata)*scale_factor]) # Force redraw self.canvas.Refresh() # Get the figure of interest fig = ax.get_figure() # Attach the call back fig.canvas.mpl_connect('scroll_event', zoom_fun) # Return the function return zoom_fun