@@ -229,8 +229,10 @@ def OnPlotKeyPress(event):
229229 Page .plotStyle ['flTicks' ] = (Page .plotStyle .get ('flTicks' ,0 )+ 1 )% 3
230230 elif event .key == 'x' and groupName is not None : # share X axis scale for Pattern Groups
231231 plotOpt ['sharedX' ] = not plotOpt ['sharedX' ]
232- if not plotOpt ['sharedX' ]: # reset scale
233- newPlot = True
232+ # Clear saved x-limits when toggling sharedX mode (MG/Cl Sonnet)
233+ if hasattr (G2frame , 'groupXlim' ):
234+ del G2frame .groupXlim
235+ newPlot = True
234236 elif event .key == 'x' and 'PWDR' in plottype :
235237 Page .plotStyle ['exclude' ] = not Page .plotStyle ['exclude' ]
236238 elif event .key == '.' :
@@ -421,6 +423,13 @@ def OnPlotKeyPress(event):
421423 newPlot = True
422424 if 'PWDR' in plottype or plottype .startswith ('GROUP' ):
423425 Page .plotStyle ['qPlot' ] = not Page .plotStyle ['qPlot' ]
426+ # switching from d to Q
427+ if (Page .plotStyle ['qPlot' ] and
428+ Page .plotStyle ['dPlot' ] and
429+ getattr (G2frame , 'groupXlim' , None ) is not None ):
430+ G2frame .groupXlim = (
431+ 2.0 * np .pi / G2frame .groupXlim [1 ], # Q_max -> d_min
432+ 2.0 * np .pi / G2frame .groupXlim [0 ]) # Q_min -> d_max
424433 Page .plotStyle ['dPlot' ] = False
425434 Page .plotStyle ['chanPlot' ] = False
426435 elif plottype in ['SASD' ,'REFD' ]:
@@ -436,6 +445,13 @@ def OnPlotKeyPress(event):
436445 elif event .key == 't' and ('PWDR' in plottype or plottype .startswith ('GROUP' )):
437446 newPlot = True
438447 Page .plotStyle ['dPlot' ] = not Page .plotStyle ['dPlot' ]
448+ # switching from Q to d
449+ if (Page .plotStyle ['qPlot' ] and
450+ Page .plotStyle ['dPlot' ] and
451+ getattr (G2frame , 'groupXlim' , None ) is not None ):
452+ G2frame .groupXlim = (
453+ 2.0 * np .pi / G2frame .groupXlim [1 ], # Q_min <- d_max
454+ 2.0 * np .pi / G2frame .groupXlim [0 ]) # Q_max <- d_min
439455 Page .plotStyle ['qPlot' ] = False
440456 Page .plotStyle ['chanPlot' ] = False
441457 elif event .key == 'm' :
@@ -1490,7 +1506,10 @@ def refPlotUpdate(Histograms,cycle=None,restore=False):
14901506 '''
14911507 if restore :
14921508 (G2frame .SinglePlot ,G2frame .Contour ,G2frame .Weight ,
1493- G2frame .plusPlot ,G2frame .SubBack ,Page .plotStyle ['logPlot' ]) = savedSettings
1509+ G2frame .plusPlot ,G2frame .SubBack ,Page .plotStyle ['logPlot' ],
1510+ Page .plotStyle ['qPlot' ],Page .plotStyle ['dPlot' ]) = savedSettings
1511+ # Also save to G2frame so settings survive Page recreation during ResetPlots (MG/Cl Sonnet)
1512+ G2frame .savedPlotStyle = copy .copy (Page .plotStyle )
14941513 return
14951514
14961515 if plottingItem not in Histograms :
@@ -1704,7 +1723,70 @@ def drawTicks(Phases,phaseList,group=False):
17041723 Plot .axvline (xt ,color = plcolor ,
17051724 picker = 3. ,
17061725 label = '_FLT_' + phase ,lw = 0.5 )
1707-
1726+
1727+ # Callback used to update y-limits when user zooms interactively (MG/Cl Sonnet)
1728+ def onGroupXlimChanged (ax ):
1729+ '''Callback to update y-limits for all panels when x-range changes.
1730+ We calculate the global y-range across all panels for the visible x-range,
1731+ then explicitly set y-limits on ALL panels.
1732+ '''
1733+ xlim = ax .get_xlim ()
1734+ # Save x-limits for persistence across refinements
1735+ if (plotOpt ['sharedX' ] and
1736+ (Page .plotStyle ['qPlot' ] or Page .plotStyle ['dPlot' ])):
1737+ G2frame .groupXlim = xlim
1738+
1739+ # Calculate global y-range across ALL panels for visible x-range
1740+ global_ymin = float ('inf' )
1741+ global_ymax = float ('-inf' )
1742+ global_dzmin = float ('inf' )
1743+ global_dzmax = float ('-inf' )
1744+ max_tick_space = 0
1745+
1746+ for i in range (Page .groupN ):
1747+ xarr = np .array (gX [i ])
1748+ xye = gdat [i ]
1749+ mask = (xarr >= xlim [0 ]) & (xarr <= xlim [1 ])
1750+ if np .any (mask ):
1751+ # Calculate scaled y-values for visible data
1752+ scaleY = lambda Y , idx = i : (Y - gYmin [idx ]) / (gYmax [idx ] - gYmin [idx ]) * 100
1753+ visible_obs = scaleY (xye [1 ][mask ])
1754+ visible_calc = scaleY (xye [3 ][mask ])
1755+ visible_bkg = scaleY (xye [4 ][mask ])
1756+ ymin_visible = min (visible_obs .min (), visible_calc .min (), visible_bkg .min ())
1757+ ymax_visible = max (visible_obs .max (), visible_calc .max (), visible_bkg .max ())
1758+ global_ymin = min (global_ymin , ymin_visible )
1759+ global_ymax = max (global_ymax , ymax_visible )
1760+ # Track tick space needed
1761+ if not Page .plotStyle .get ('flTicks' , False ):
1762+ max_tick_space = max (max_tick_space , len (RefTbl [i ]) * 5 )
1763+ else :
1764+ max_tick_space = max (max_tick_space , 1 )
1765+ # Calculate diff y-limits
1766+ DZ_visible = (xye [1 ][mask ] - xye [3 ][mask ]) * np .sqrt (xye [2 ][mask ])
1767+ global_dzmin = min (global_dzmin , DZ_visible .min ())
1768+ global_dzmax = max (global_dzmax , DZ_visible .max ())
1769+
1770+ # Apply global y-limits to ALL panels explicitly
1771+ if global_ymax > global_ymin :
1772+ yrange = global_ymax - global_ymin
1773+ ypad = max (yrange * 0.05 , 1.0 )
1774+ ylim_upper = (global_ymin - ypad - max_tick_space , global_ymax + ypad )
1775+ for i in range (Page .groupN ):
1776+ up , down = adjustDim (i , Page .groupN )
1777+ Plots [up ].set_ylim (ylim_upper )
1778+ Plots [up ].autoscale (enable = False , axis = 'y' )
1779+ if global_dzmax > global_dzmin :
1780+ dzrange = global_dzmax - global_dzmin
1781+ dzpad = max (dzrange * 0.05 , 0.5 )
1782+ ylim_lower = (global_dzmin - dzpad , global_dzmax + dzpad )
1783+ for i in range (Page .groupN ):
1784+ up , down = adjustDim (i , Page .groupN )
1785+ Plots [down ].set_ylim (ylim_lower )
1786+ Plots [down ].autoscale (enable = False , axis = 'y' )
1787+ # Force canvas redraw to apply new limits
1788+ Page .canvas .draw_idle ()
1789+
17081790 #### beginning PlotPatterns execution #####################################
17091791 global exclLines ,Page
17101792 global DifLine
@@ -1756,6 +1838,13 @@ def drawTicks(Phases,phaseList,group=False):
17561838 if not new and hasattr (Page ,'prevPlotType' ):
17571839 if Page .prevPlotType != plottype : new = True
17581840 Page .prevPlotType = plottype
1841+
1842+ # Restore saved plot style settings (qPlot, dPlot, logPlot) if they were preserved
1843+ # across a refinement cycle. These get saved in refPlotUpdate(restore=True) and
1844+ # need to be applied here because Page may have been recreated by ResetPlots. (based on MG/Cl Sonnet)
1845+ if hasattr (G2frame , 'savedPlotStyle' ):
1846+ Page .plotStyle .update (G2frame .savedPlotStyle )
1847+ del G2frame .savedPlotStyle # Clear after applying
17591848
17601849 if G2frame .ifSetLimitsMode and G2frame .GPXtree .GetItemText (G2frame .GPXtree .GetSelection ()) == 'Limits' :
17611850 # note mode
@@ -1823,7 +1912,8 @@ def drawTicks(Phases,phaseList,group=False):
18231912 plottingItem = G2frame .GPXtree .GetItemText (G2frame .PatternId )
18241913 # save settings to be restored after refinement with repPlotUpdate({},restore=True)
18251914 savedSettings = (G2frame .SinglePlot ,G2frame .Contour ,G2frame .Weight ,
1826- G2frame .plusPlot ,G2frame .SubBack ,Page .plotStyle ['logPlot' ])
1915+ G2frame .plusPlot ,G2frame .SubBack ,Page .plotStyle ['logPlot' ],
1916+ Page .plotStyle ['qPlot' ],Page .plotStyle ['dPlot' ])
18271917 G2frame .SinglePlot = True
18281918 G2frame .Contour = False
18291919 G2frame .Weight = True
@@ -2143,7 +2233,7 @@ def drawTicks(Phases,phaseList,group=False):
21432233 Title += ' - background'
21442234 if Page .plotStyle ['qPlot' ] or plottype in ['SASD' ,'REFD' ] and not G2frame .Contour :
21452235 xLabel = r'$Q, \AA^{-1}$'
2146- elif Page .plotStyle ['dPlot' ] and 'PWDR' in plottype :
2236+ elif Page .plotStyle ['dPlot' ] and ( 'PWDR' in plottype or plottype == 'GROUP' ) :
21472237 xLabel = r'$d, \AA$'
21482238 elif Page .plotStyle ['chanPlot' ] and G2frame .Contour :
21492239 xLabel = 'Channel no.'
@@ -2215,8 +2305,6 @@ def drawTicks(Phases,phaseList,group=False):
22152305 gdat [i ][j ] = np .where (y >= 0. ,np .sqrt (y ),- np .sqrt (- y ))
22162306 else :
22172307 gdat [i ][j ] = y
2218- gYmax [i ] = max (max (gdat [i ][1 ]),max (gdat [i ][3 ]))
2219- gYmin [i ] = min (min (gdat [i ][1 ]),min (gdat [i ][3 ]))
22202308 if Page .plotStyle ['qPlot' ]:
22212309 gX [i ] = 2. * np .pi / G2lat .Pos2dsp (gParms ,gdat [i ][0 ])
22222310 elif Page .plotStyle ['dPlot' ]:
@@ -2225,6 +2313,9 @@ def drawTicks(Phases,phaseList,group=False):
22252313 gX [i ] = gdat [i ][0 ]
22262314 gXmin [i ] = min (gX [i ])
22272315 gXmax [i ] = max (gX [i ])
2316+ # Calculate Y range from full data initially (may be updated later for zoom)
2317+ gYmax [i ] = max (max (gdat [i ][1 ]),max (gdat [i ][3 ]))
2318+ gYmin [i ] = min (min (gdat [i ][1 ]),min (gdat [i ][3 ]))
22282319 # obs-calc/sigma
22292320 DZ = (gdat [i ][1 ]- gdat [i ][3 ])* np .sqrt (gdat [i ][2 ])
22302321 DZmin = min (DZmin ,DZ .min ())
@@ -2237,22 +2328,23 @@ def drawTicks(Phases,phaseList,group=False):
22372328 Page .plotStyle ['qPlot' ] or Page .plotStyle ['dPlot' ]):
22382329 Page .figure .text (0.001 ,0.94 ,'X shared' ,fontsize = 11 ,
22392330 color = 'g' )
2240- Plots = Page .figure .subplots (2 ,Page .groupN ,sharey = 'row' ,sharex = True ,
2331+ #Plots = Page.figure.subplots(2,Page.groupN,sharey='row',sharex=True,
2332+ # gridspec_kw=GS_kw)
2333+ # Don't use sharey='row' when sharedX - we'll manage y-limits manually
2334+ # This avoids conflicts between sharey and our dynamic y-limit updates
2335+ Plots = Page .figure .subplots (2 ,Page .groupN ,sharex = True ,
22412336 gridspec_kw = GS_kw )
22422337 else :
22432338 Plots = Page .figure .subplots (2 ,Page .groupN ,sharey = 'row' ,sharex = 'col' ,
22442339 gridspec_kw = GS_kw )
22452340 Page .figure .subplots_adjust (left = 5 / 100. ,bottom = 16 / 150. ,
22462341 right = .99 ,top = 1. - 3 / 200. ,hspace = 0 ,wspace = 0 )
2247- for i in range (Page .groupN ):
2248- up ,down = adjustDim (i ,Page .groupN )
2249- Plots [up ].set_xlim (gXmin [i ],gXmax [i ])
2250- Plots [down ].set_xlim (gXmin [i ],gXmax [i ])
2251- Plots [down ].set_ylim (DZmin ,DZmax )
2252- if not Page .plotStyle .get ('flTicks' ,False ):
2253- Plots [up ].set_ylim (- len (RefTbl [i ])* 5 ,102 )
2254- else :
2255- Plots [up ].set_ylim (- 1 ,102 )
2342+
2343+ # Connect callback for all panels when sharedX is enabled
2344+ if (plotOpt ['sharedX' ] and
2345+ (Page .plotStyle ['qPlot' ] or Page .plotStyle ['dPlot' ])):
2346+ up , down = adjustDim (0 , Page .groupN )
2347+ Plots [up ].callbacks .connect ('xlim_changed' , onGroupXlimChanged )
22562348
22572349 # pretty up the tick labels
22582350 up ,down = adjustDim (0 ,Page .groupN )
@@ -2268,7 +2360,7 @@ def drawTicks(Phases,phaseList,group=False):
22682360 Plots [up ].set_ylabel (r'$\rm\sqrt{Normalized\ intensity}$' ,fontsize = 12 )
22692361 else :
22702362 Plots [up ].set_ylabel ('Normalized Intensity' ,fontsize = 12 )
2271- Page .figure .text (0.001 ,0.03 ,commonltrs ,fontsize = 13 )
2363+ Page .figure .text (0.001 ,0.03 ,commonltrs ,fontsize = 13 , color = 'g' )
22722364 Page .figure .supxlabel (xLabel )
22732365 for i ,h in enumerate (groupPlotList ):
22742366 up ,down = adjustDim (i ,Page .groupN )
@@ -2284,7 +2376,7 @@ def drawTicks(Phases,phaseList,group=False):
22842376 transform = Plot .transAxes ,
22852377 verticalalignment = 'top' ,
22862378 horizontalalignment = ha ,
2287- fontsize = 14 )
2379+ fontsize = 14 , color = 'g' , fontweight = 'bold' )
22882380 xye = gdat [i ]
22892381 DZ = (xye [1 ]- xye [3 ])* np .sqrt (xye [2 ])
22902382 DifLine = Plot1 .plot (gX [i ],DZ ,pwdrCol ['Diff_color' ]) #,picker=1.,label=incCptn('diff')) #(Io-Ic)/sig(Io)
@@ -2296,13 +2388,99 @@ def drawTicks(Phases,phaseList,group=False):
22962388 Plot .plot (gX [i ],scaleY (xye [3 ]),pwdrCol ['Calc_color' ],picker = 0. ,label = incCptn ('calc' ),linewidth = 1.5 )
22972389 Plot .plot (gX [i ],scaleY (xye [4 ]),pwdrCol ['Bkg_color' ],picker = 0. ,label = incCptn ('bkg' ),linewidth = 1.5 ) #background
22982390 drawTicks (RefTbl [i ],list (RefTbl [i ].keys ()),True )
2391+
2392+ # Set axis limits AFTER plotting data to prevent autoscaling from overriding them (MG/Cl Sonnet)
2393+ # When sharedX is enabled, calculate common x-range encompassing all histograms
2394+ if (plotOpt ['sharedX' ] and
2395+ (Page .plotStyle ['qPlot' ] or Page .plotStyle ['dPlot' ])):
2396+ if getattr (G2frame , 'groupXlim' , None ) is not None :
2397+ commonXlim = getattr (G2frame , 'groupXlim' , None )
2398+ else :
2399+ # Calculate union of all histogram x-ranges
2400+ commonXmin = min (gXmin .values ())
2401+ commonXmax = max (gXmax .values ())
2402+ commonXlim = (commonXmin , commonXmax )
2403+
2404+ # First pass: set x-limits and calculate global y-range for visible data
2405+ # Since sharey='row', all upper panels share y-limits, all lower panels share y-limits
2406+ global_ymin = float ('inf' )
2407+ global_ymax = float ('-inf' )
2408+ global_dzmin = float ('inf' )
2409+ global_dzmax = float ('-inf' )
2410+ max_tick_space = 0
2411+
2412+ for i in range (Page .groupN ):
2413+ up , down = adjustDim (i , Page .groupN )
2414+ if (plotOpt ['sharedX' ] and
2415+ (Page .plotStyle ['qPlot' ] or Page .plotStyle ['dPlot' ])):
2416+ xlim = commonXlim
2417+ Plots [up ].set_xlim (xlim )
2418+ Plots [down ].set_xlim (xlim )
2419+ else :
2420+ xlim = (gXmin [i ], gXmax [i ])
2421+ Plots [up ].set_xlim (xlim )
2422+ Plots [down ].set_xlim (xlim )
2423+
2424+ # Calculate y-range for this panel's visible data
2425+ xarr = np .array (gX [i ])
2426+ xye = gdat [i ]
2427+ mask = (xarr >= xlim [0 ]) & (xarr <= xlim [1 ])
2428+ if np .any (mask ):
2429+ scaleY = lambda Y , idx = i : (Y - gYmin [idx ]) / (gYmax [idx ] - gYmin [idx ]) * 100
2430+ visible_obs = scaleY (xye [1 ][mask ])
2431+ visible_calc = scaleY (xye [3 ][mask ])
2432+ visible_bkg = scaleY (xye [4 ][mask ])
2433+ ymin_visible = min (visible_obs .min (), visible_calc .min (), visible_bkg .min ())
2434+ ymax_visible = max (visible_obs .max (), visible_calc .max (), visible_bkg .max ())
2435+ global_ymin = min (global_ymin , ymin_visible )
2436+ global_ymax = max (global_ymax , ymax_visible )
2437+ # Track tick space needed
2438+ if not Page .plotStyle .get ('flTicks' , False ):
2439+ max_tick_space = max (max_tick_space , len (RefTbl [i ]) * 5 )
2440+ else :
2441+ max_tick_space = max (max_tick_space , 1 )
2442+ # Calculate diff y-limits
2443+ DZ_visible = (xye [1 ][mask ] - xye [3 ][mask ]) * np .sqrt (xye [2 ][mask ])
2444+ global_dzmin = min (global_dzmin , DZ_visible .min ())
2445+ global_dzmax = max (global_dzmax , DZ_visible .max ())
2446+
2447+ # Apply global y-limits to ALL panels explicitly (sharey may not propagate properly)
2448+ if global_ymax > global_ymin :
2449+ yrange = global_ymax - global_ymin
2450+ ypad = max (yrange * 0.05 , 1.0 )
2451+ ylim_upper = (global_ymin - ypad - max_tick_space , global_ymax + ypad )
2452+ else :
2453+ # Fallback to full range
2454+ if not Page .plotStyle .get ('flTicks' , False ):
2455+ ylim_upper = (- max_tick_space , 102 )
2456+ else :
2457+ ylim_upper = (- 1 , 102 )
2458+ if global_dzmax > global_dzmin :
2459+ dzrange = global_dzmax - global_dzmin
2460+ dzpad = max (dzrange * 0.05 , 0.5 )
2461+ ylim_lower = (global_dzmin - dzpad , global_dzmax + dzpad )
2462+ else :
2463+ ylim_lower = (DZmin , DZmax )
2464+
2465+ # Set y-limits on ALL panels
2466+ for i in range (Page .groupN ):
2467+ up , down = adjustDim (i , Page .groupN )
2468+ Plots [up ].set_ylim (ylim_upper )
2469+ Plots [down ].set_ylim (ylim_lower )
2470+
22992471 try : # try used as in PWDR menu not Groups
23002472 # Not sure if this does anything
23012473 G2frame .dataWindow .moveTickLoc .Enable (False )
23022474 G2frame .dataWindow .moveTickSpc .Enable (False )
23032475 # G2frame.dataWindow.moveDiffCurve.Enable(True)
23042476 except :
23052477 pass
2478+ # Save the current x-limits for GROUP plots so they can be restored after refinement (MG/Cl Sonnet)
2479+ # When sharedX is enabled in Q/d-space, all panels share the same x-range
2480+ if (plotOpt ['sharedX' ] and
2481+ (Page .plotStyle ['qPlot' ] or Page .plotStyle ['dPlot' ])):
2482+ up ,down = adjustDim (0 ,Page .groupN )
2483+ G2frame .groupXlim = Plots [up ].get_xlim ()
23062484 Page .canvas .draw ()
23072485 return
23082486 elif G2frame .Weight and not G2frame .Contour :
0 commit comments