Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions GSASII/GSASIIplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,27 @@ def _update_view(self):
wx.CallAfter(*self.updateActions)
Toolbar._update_view(self)

def home(self, *args):
'''Override home button to clear saved GROUP plot limits and trigger replot.
This ensures that pressing home resets to full data range while retaining x-units.
For GROUP plots, we need to replot rather than use matplotlib's home because
matplotlib's home would restore the original shared limits, not per-histogram limits.
'''
G2frame = wx.GetApp().GetMainTopWindow()
# Clear saved GROUP plot x-limits so PlotPatterns will use full data range
if hasattr(G2frame, 'groupXlim'):
del G2frame.groupXlim
if hasattr(G2frame, 'groupPlotMode'):
del G2frame.groupPlotMode
# Check if we're in GROUP plot mode - if so, trigger a replot
if self.arrows.get('_groupMode'):
# Trigger a full replot for GROUP plots
if self.updateActions:
wx.CallAfter(*self.updateActions)
return
# For non-GROUP plots, call the parent's home method
Toolbar.home(self, *args)

def AnyActive(self):
for Itool in range(self.GetToolsCount()):
if self.GetToolState(self.GetToolByPos(Itool).GetId()):
Expand Down
236 changes: 219 additions & 17 deletions GSASII/GSASIIpwdplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,19 @@ def OnPlotKeyPress(event):
Page.plotStyle['flTicks'] = (Page.plotStyle.get('flTicks',0)+1)%3
elif event.key == 'x' and groupName is not None: # share X axis scale for Pattern Groups
plotOpt['sharedX'] = not plotOpt['sharedX']
if not plotOpt['sharedX']: # reset scale
newPlot = True
# Clear saved x-limits when toggling sharedX mode
if hasattr(G2frame, 'groupXlim'):
del G2frame.groupXlim
if hasattr(G2frame, 'groupPlotMode'):
del G2frame.groupPlotMode
newPlot = True
elif event.key == 'r' and groupName is not None: # reset to full range for Pattern Groups
# Clear saved GROUP plot x-limits so plot uses full data range
if hasattr(G2frame, 'groupXlim'):
del G2frame.groupXlim
if hasattr(G2frame, 'groupPlotMode'):
del G2frame.groupPlotMode
newPlot = True
elif event.key == 'x' and 'PWDR' in plottype:
Page.plotStyle['exclude'] = not Page.plotStyle['exclude']
elif event.key == '.':
Expand Down Expand Up @@ -1490,7 +1501,12 @@ def refPlotUpdate(Histograms,cycle=None,restore=False):
'''
if restore:
(G2frame.SinglePlot,G2frame.Contour,G2frame.Weight,
G2frame.plusPlot,G2frame.SubBack,Page.plotStyle['logPlot']) = savedSettings
G2frame.plusPlot,G2frame.SubBack,Page.plotStyle['logPlot'],
Page.plotStyle['qPlot'],Page.plotStyle['dPlot']) = savedSettings
# Also save to G2frame so settings survive Page recreation during ResetPlots
G2frame.savedPlotStyle = {'qPlot': Page.plotStyle['qPlot'],
'dPlot': Page.plotStyle['dPlot'],
'logPlot': Page.plotStyle['logPlot']}
return

if plottingItem not in Histograms:
Expand Down Expand Up @@ -1756,6 +1772,15 @@ def drawTicks(Phases,phaseList,group=False):
if not new and hasattr(Page,'prevPlotType'):
if Page.prevPlotType != plottype: new = True
Page.prevPlotType = plottype

# Restore saved plot style settings (qPlot, dPlot, logPlot) if they were preserved
# across a refinement cycle. These get saved in refPlotUpdate(restore=True) and
# need to be applied here because Page may have been recreated by ResetPlots.
if hasattr(G2frame, 'savedPlotStyle') and G2frame.savedPlotStyle:
for key in ('qPlot', 'dPlot', 'logPlot'):
if key in G2frame.savedPlotStyle:
Page.plotStyle[key] = G2frame.savedPlotStyle[key]
G2frame.savedPlotStyle = None # Clear after applying

if G2frame.ifSetLimitsMode and G2frame.GPXtree.GetItemText(G2frame.GPXtree.GetSelection()) == 'Limits':
# note mode
Expand Down Expand Up @@ -1823,7 +1848,8 @@ def drawTicks(Phases,phaseList,group=False):
plottingItem = G2frame.GPXtree.GetItemText(G2frame.PatternId)
# save settings to be restored after refinement with repPlotUpdate({},restore=True)
savedSettings = (G2frame.SinglePlot,G2frame.Contour,G2frame.Weight,
G2frame.plusPlot,G2frame.SubBack,Page.plotStyle['logPlot'])
G2frame.plusPlot,G2frame.SubBack,Page.plotStyle['logPlot'],
Page.plotStyle['qPlot'],Page.plotStyle['dPlot'])
G2frame.SinglePlot = True
G2frame.Contour = False
G2frame.Weight = True
Expand Down Expand Up @@ -2143,7 +2169,7 @@ def drawTicks(Phases,phaseList,group=False):
Title += ' - background'
if Page.plotStyle['qPlot'] or plottype in ['SASD','REFD'] and not G2frame.Contour:
xLabel = r'$Q, \AA^{-1}$'
elif Page.plotStyle['dPlot'] and 'PWDR' in plottype:
elif Page.plotStyle['dPlot'] and ('PWDR' in plottype or plottype == 'GROUP'):
xLabel = r'$d, \AA$'
elif Page.plotStyle['chanPlot'] and G2frame.Contour:
xLabel = 'Channel no.'
Expand All @@ -2167,7 +2193,8 @@ def drawTicks(Phases,phaseList,group=False):
's: toggle sqrt plot',
'q: toggle Q plot',
't: toggle d-spacing plot',
'x: share x-axes (Q/d only)']
'x: share x-axes (Q/d only)',
'r: reset to full range']
Plot.set_visible(False) # removes "big" plot
gXmin = {}
gXmax = {}
Expand Down Expand Up @@ -2215,8 +2242,6 @@ def drawTicks(Phases,phaseList,group=False):
gdat[i][j] = np.where(y>=0.,np.sqrt(y),-np.sqrt(-y))
else:
gdat[i][j] = y
gYmax[i] = max(max(gdat[i][1]),max(gdat[i][3]))
gYmin[i] = min(min(gdat[i][1]),min(gdat[i][3]))
if Page.plotStyle['qPlot']:
gX[i] = 2.*np.pi/G2lat.Pos2dsp(gParms,gdat[i][0])
elif Page.plotStyle['dPlot']:
Expand All @@ -2225,6 +2250,9 @@ def drawTicks(Phases,phaseList,group=False):
gX[i] = gdat[i][0]
gXmin[i] = min(gX[i])
gXmax[i] = max(gX[i])
# Calculate Y range from full data initially (may be updated later for zoom)
gYmax[i] = max(max(gdat[i][1]),max(gdat[i][3]))
gYmin[i] = min(min(gdat[i][1]),min(gdat[i][3]))
# obs-calc/sigma
DZ = (gdat[i][1]-gdat[i][3])*np.sqrt(gdat[i][2])
DZmin = min(DZmin,DZ.min())
Expand All @@ -2237,22 +2265,111 @@ def drawTicks(Phases,phaseList,group=False):
Page.plotStyle['qPlot'] or Page.plotStyle['dPlot']):
Page.figure.text(0.001,0.94,'X shared',fontsize=11,
color='g')
Plots = Page.figure.subplots(2,Page.groupN,sharey='row',sharex=True,
# Don't use sharey='row' when sharedX - we'll manage y-limits manually
# This avoids conflicts between sharey and our dynamic y-limit updates
Plots = Page.figure.subplots(2,Page.groupN,sharex=True,
gridspec_kw=GS_kw)
else:
Plots = Page.figure.subplots(2,Page.groupN,sharey='row',sharex='col',
gridspec_kw=GS_kw)
Page.figure.subplots_adjust(left=5/100.,bottom=16/150.,
right=.99,top=1.-3/200.,hspace=0,wspace=0)
for i in range(Page.groupN):
up,down = adjustDim(i,Page.groupN)
Plots[up].set_xlim(gXmin[i],gXmax[i])
Plots[down].set_xlim(gXmin[i],gXmax[i])
Plots[down].set_ylim(DZmin,DZmax)
if not Page.plotStyle.get('flTicks',False):
Plots[up].set_ylim(-len(RefTbl[i])*5,102)
# Check if we have a saved shared x-range to restore (for Q/d-space with sharedX)
useSharedLim = (plotOpt['sharedX'] and
(Page.plotStyle['qPlot'] or Page.plotStyle['dPlot']))
savedGroupXlim = getattr(G2frame, 'groupXlim', None)
# Check if saved limits need conversion to current plot mode
savedPlotMode = getattr(G2frame, 'groupPlotMode', None)
currentPlotMode = ('q' if Page.plotStyle['qPlot'] else
'd' if Page.plotStyle['dPlot'] else 'tof')
if savedPlotMode != currentPlotMode and savedGroupXlim is not None:
# Convert saved x-limits to current units
# d = 2π/Q, so Q = 2π/d
if savedPlotMode == 'q' and currentPlotMode == 'd':
# Converting Q limits to d limits: d = 2π/Q (inverts order)
newMin = 2.0 * np.pi / savedGroupXlim[1] # Q_max -> d_min
newMax = 2.0 * np.pi / savedGroupXlim[0] # Q_min -> d_max
savedGroupXlim = (newMin, newMax)
G2frame.groupXlim = savedGroupXlim
G2frame.groupPlotMode = currentPlotMode
elif savedPlotMode == 'd' and currentPlotMode == 'q':
# Converting d limits to Q limits: Q = 2π/d (inverts order)
newMin = 2.0 * np.pi / savedGroupXlim[1] # d_max -> Q_min
newMax = 2.0 * np.pi / savedGroupXlim[0] # d_min -> Q_max
savedGroupXlim = (newMin, newMax)
G2frame.groupXlim = savedGroupXlim
G2frame.groupPlotMode = currentPlotMode
else:
Plots[up].set_ylim(-1,102)
# TOF mode or other transitions - reset limits
savedGroupXlim = None

# Add callback to update y-limits when user zooms interactively
def onGroupXlimChanged(ax):
'''Callback to update y-limits for all panels when x-range changes.
We calculate the global y-range across all panels for the visible x-range,
then explicitly set y-limits on ALL panels.
'''
xlim = ax.get_xlim()
# Save x-limits for persistence across refinements
if useSharedLim:
G2frame.groupXlim = xlim
G2frame.groupPlotMode = currentPlotMode

# Calculate global y-range across ALL panels for visible x-range
global_ymin = float('inf')
global_ymax = float('-inf')
global_dzmin = float('inf')
global_dzmax = float('-inf')
max_tick_space = 0

for i in range(Page.groupN):
xarr = np.array(gX[i])
xye = gdat[i]
mask = (xarr >= xlim[0]) & (xarr <= xlim[1])
if np.any(mask):
# Calculate scaled y-values for visible data
scaleY = lambda Y, idx=i: (Y - gYmin[idx]) / (gYmax[idx] - gYmin[idx]) * 100
visible_obs = scaleY(xye[1][mask])
visible_calc = scaleY(xye[3][mask])
visible_bkg = scaleY(xye[4][mask])
ymin_visible = min(visible_obs.min(), visible_calc.min(), visible_bkg.min())
ymax_visible = max(visible_obs.max(), visible_calc.max(), visible_bkg.max())
global_ymin = min(global_ymin, ymin_visible)
global_ymax = max(global_ymax, ymax_visible)
# Track tick space needed
if not Page.plotStyle.get('flTicks', False):
max_tick_space = max(max_tick_space, len(RefTbl[i]) * 5)
else:
max_tick_space = max(max_tick_space, 1)
# Calculate diff y-limits
DZ_visible = (xye[1][mask] - xye[3][mask]) * np.sqrt(xye[2][mask])
global_dzmin = min(global_dzmin, DZ_visible.min())
global_dzmax = max(global_dzmax, DZ_visible.max())

# Apply global y-limits to ALL panels explicitly
if global_ymax > global_ymin:
yrange = global_ymax - global_ymin
ypad = max(yrange * 0.05, 1.0)
ylim_upper = (global_ymin - ypad - max_tick_space, global_ymax + ypad)
for i in range(Page.groupN):
up, down = adjustDim(i, Page.groupN)
Plots[up].set_ylim(ylim_upper)
Plots[up].autoscale(enable=False, axis='y')
if global_dzmax > global_dzmin:
dzrange = global_dzmax - global_dzmin
dzpad = max(dzrange * 0.05, 0.5)
ylim_lower = (global_dzmin - dzpad, global_dzmax + dzpad)
for i in range(Page.groupN):
up, down = adjustDim(i, Page.groupN)
Plots[down].set_ylim(ylim_lower)
Plots[down].autoscale(enable=False, axis='y')
# Force canvas redraw to apply new limits
Page.canvas.draw_idle()

# Connect callback for all panels when sharedX is enabled
if useSharedLim:
up, down = adjustDim(0, Page.groupN)
Plots[up].callbacks.connect('xlim_changed', onGroupXlimChanged)

# pretty up the tick labels
up,down = adjustDim(0,Page.groupN)
Expand All @@ -2270,6 +2387,7 @@ def drawTicks(Phases,phaseList,group=False):
Plots[up].set_ylabel('Normalized Intensity',fontsize=12)
Page.figure.text(0.001,0.03,commonltrs,fontsize=13)
Page.figure.supxlabel(xLabel)

for i,h in enumerate(groupPlotList):
up,down = adjustDim(i,Page.groupN)
Plot = Plots[up]
Expand All @@ -2296,13 +2414,97 @@ def drawTicks(Phases,phaseList,group=False):
Plot.plot(gX[i],scaleY(xye[3]),pwdrCol['Calc_color'],picker=0.,label=incCptn('calc'),linewidth=1.5)
Plot.plot(gX[i],scaleY(xye[4]),pwdrCol['Bkg_color'],picker=0.,label=incCptn('bkg'),linewidth=1.5) #background
drawTicks(RefTbl[i],list(RefTbl[i].keys()),True)

# Set axis limits AFTER plotting data to prevent autoscaling from overriding them
# When sharedX is enabled, calculate common x-range encompassing all histograms
if useSharedLim:
if savedGroupXlim is not None:
commonXlim = savedGroupXlim
else:
# Calculate union of all histogram x-ranges
commonXmin = min(gXmin.values())
commonXmax = max(gXmax.values())
commonXlim = (commonXmin, commonXmax)

# First pass: set x-limits and calculate global y-range for visible data
# Since sharey='row', all upper panels share y-limits, all lower panels share y-limits
global_ymin = float('inf')
global_ymax = float('-inf')
global_dzmin = float('inf')
global_dzmax = float('-inf')
max_tick_space = 0

for i in range(Page.groupN):
up, down = adjustDim(i, Page.groupN)
if useSharedLim:
xlim = commonXlim
Plots[up].set_xlim(xlim)
Plots[down].set_xlim(xlim)
else:
xlim = (gXmin[i], gXmax[i])
Plots[up].set_xlim(xlim)
Plots[down].set_xlim(xlim)

# Calculate y-range for this panel's visible data
xarr = np.array(gX[i])
xye = gdat[i]
mask = (xarr >= xlim[0]) & (xarr <= xlim[1])
if np.any(mask):
scaleY = lambda Y, idx=i: (Y - gYmin[idx]) / (gYmax[idx] - gYmin[idx]) * 100
visible_obs = scaleY(xye[1][mask])
visible_calc = scaleY(xye[3][mask])
visible_bkg = scaleY(xye[4][mask])
ymin_visible = min(visible_obs.min(), visible_calc.min(), visible_bkg.min())
ymax_visible = max(visible_obs.max(), visible_calc.max(), visible_bkg.max())
global_ymin = min(global_ymin, ymin_visible)
global_ymax = max(global_ymax, ymax_visible)
# Track tick space needed
if not Page.plotStyle.get('flTicks', False):
max_tick_space = max(max_tick_space, len(RefTbl[i]) * 5)
else:
max_tick_space = max(max_tick_space, 1)
# Calculate diff y-limits
DZ_visible = (xye[1][mask] - xye[3][mask]) * np.sqrt(xye[2][mask])
global_dzmin = min(global_dzmin, DZ_visible.min())
global_dzmax = max(global_dzmax, DZ_visible.max())

# Apply global y-limits to ALL panels explicitly (sharey may not propagate properly)
if global_ymax > global_ymin:
yrange = global_ymax - global_ymin
ypad = max(yrange * 0.05, 1.0)
ylim_upper = (global_ymin - ypad - max_tick_space, global_ymax + ypad)
else:
# Fallback to full range
if not Page.plotStyle.get('flTicks', False):
ylim_upper = (-max_tick_space, 102)
else:
ylim_upper = (-1, 102)
if global_dzmax > global_dzmin:
dzrange = global_dzmax - global_dzmin
dzpad = max(dzrange * 0.05, 0.5)
ylim_lower = (global_dzmin - dzpad, global_dzmax + dzpad)
else:
ylim_lower = (DZmin, DZmax)

# Set y-limits on ALL panels
for i in range(Page.groupN):
up, down = adjustDim(i, Page.groupN)
Plots[up].set_ylim(ylim_upper)
Plots[down].set_ylim(ylim_lower)

try: # try used as in PWDR menu not Groups
# Not sure if this does anything
G2frame.dataWindow.moveTickLoc.Enable(False)
G2frame.dataWindow.moveTickSpc.Enable(False)
# G2frame.dataWindow.moveDiffCurve.Enable(True)
except:
pass
# Save the current x-limits for GROUP plots so they can be restored after refinement
# When sharedX is enabled in Q/d-space, all panels share the same x-range
if useSharedLim:
up,down = adjustDim(0,Page.groupN)
G2frame.groupXlim = Plots[up].get_xlim()
G2frame.groupPlotMode = currentPlotMode
Page.canvas.draw()
return
elif G2frame.Weight and not G2frame.Contour:
Expand Down