Skip to content

Commit 8aa9dfc

Browse files
authored
Merge pull request #1751 from HEXRD/polar-style-changes
changes some plot styles for the polar view
2 parents 7a03164 + 555682c commit 8aa9dfc

File tree

1 file changed

+169
-11
lines changed

1 file changed

+169
-11
lines changed

hexrdgui/image_canvas.py

Lines changed: 169 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import copy
22
import math
3+
import logging
34

45
from PySide6.QtCore import QThreadPool, QTimer, Signal, Qt
56
from PySide6.QtWidgets import QFileDialog, QMessageBox
@@ -8,8 +9,9 @@
89
from matplotlib.figure import Figure
910
from matplotlib.lines import Line2D
1011
from matplotlib.patches import Circle
11-
from matplotlib.ticker import AutoLocator, FuncFormatter
12+
from matplotlib.ticker import AutoLocator, AutoMinorLocator, FuncFormatter
1213

14+
import matplotlib as mpl
1315
import matplotlib.pyplot as plt
1416
import matplotlib.transforms as tx
1517

@@ -35,6 +37,11 @@
3537
)
3638
from hexrdgui.utils.tth_distortion import apply_tth_distortion_if_needed
3739

40+
# Increase these font sizes (compared to the global font) by the specified
41+
# amounts.
42+
FONTSIZE_LABEL_INCREASE = 4
43+
FONTSIZE_TICKS_INCREASE = 4
44+
3845

3946
class ImageCanvas(FigureCanvas):
4047

@@ -114,6 +121,42 @@ def setup_connections(self):
114121
def thread_pool(self):
115122
return QThreadPool.globalInstance()
116123

124+
@property
125+
def fontsize_label(self):
126+
return HexrdConfig().font_size + FONTSIZE_LABEL_INCREASE
127+
128+
@property
129+
def fontsize_ticks(self):
130+
return HexrdConfig().font_size + FONTSIZE_TICKS_INCREASE
131+
132+
@property
133+
def label_kwargs(self):
134+
return {
135+
'fontsize': self.fontsize_label,
136+
'family': 'serif',
137+
}
138+
139+
@property
140+
def major_tick_kwargs(self):
141+
return {
142+
'left': True,
143+
'right': True,
144+
'bottom': True,
145+
'top': True,
146+
'which': 'major',
147+
'length': 10,
148+
'labelfontfamily': 'serif',
149+
'labelsize': self.fontsize_ticks,
150+
}
151+
152+
@property
153+
def minor_tick_kwargs(self):
154+
return {
155+
**self.major_tick_kwargs,
156+
'which': 'minor',
157+
'length': 2,
158+
}
159+
117160
def __del__(self):
118161
# This is so that the figure can be cleaned up
119162
plt.close(self.figure)
@@ -1004,6 +1047,7 @@ def show_polar(self):
10041047
worker.signals.error.connect(self.async_worker_error)
10051048

10061049
def finish_show_polar(self, iviewer):
1050+
10071051
if self.mode != ViewType.polar:
10081052
# Image mode was switched during generation. Ignore this.
10091053
return
@@ -1034,10 +1078,21 @@ def finish_show_polar(self, iviewer):
10341078
}
10351079
self.axes_images.append(self.axis.imshow(**kwargs))
10361080
self.axis.axis('auto')
1081+
1082+
self.axis.yaxis.set_major_locator(AutoLocator())
1083+
self.axis.yaxis.set_minor_locator(AutoMinorLocator())
1084+
1085+
self.axis.xaxis.set_major_locator(PolarXAxisTickLocator(self))
1086+
self.axis.xaxis.set_minor_locator(
1087+
PolarXAxisMinorTickLocator(self)
1088+
)
1089+
self.axis.tick_params(**self.major_tick_kwargs)
1090+
self.axis.tick_params(**self.minor_tick_kwargs)
1091+
10371092
# Do not allow the axis to autoscale, which could happen if
10381093
# overlays are drawn out-of-bounds
10391094
self.axis.autoscale(False)
1040-
self.axis.set_ylabel(r'$\eta$ [deg]')
1095+
self.axis.set_ylabel(r'$\eta$ [deg]', **self.label_kwargs)
10411096
self.axis.label_outer()
10421097
else:
10431098
rescale_image = False
@@ -1053,11 +1108,11 @@ def finish_show_polar(self, iviewer):
10531108
axis = self.figure.add_subplot(grid[3, 0], sharex=self.axis)
10541109
data = (tth, self.compute_azimuthal_integral_sum())
10551110
unscaled = (tth, self.compute_azimuthal_integral_sum(False))
1056-
self.azimuthal_line_artist, = axis.plot(*data)
1111+
self.azimuthal_line_artist, = axis.plot(*data, '-k', lw=2.5)
10571112
HexrdConfig().last_unscaled_azimuthal_integral_data = unscaled
10581113

10591114
self.azimuthal_integral_axis = axis
1060-
axis.set_ylabel(r'Azimuthal Average')
1115+
axis.set_ylabel(r'Azimuthal Average', **self.label_kwargs)
10611116
self.update_azimuthal_plot_overlays()
10621117
self.update_wppf_plot()
10631118

@@ -1067,15 +1122,51 @@ def finish_show_polar(self, iviewer):
10671122
formatter = PolarXAxisFormatter(default_formatter, f)
10681123
axis.xaxis.set_major_formatter(formatter)
10691124

1070-
# Set our custom tick locators as well
1071-
self.axis.xaxis.set_major_locator(PolarXAxisTickLocator(self))
1125+
axis.yaxis.set_major_locator(AutoLocator())
1126+
axis.yaxis.set_minor_locator(AutoMinorLocator())
1127+
10721128
axis.xaxis.set_major_locator(PolarXAxisTickLocator(self))
1129+
self.axis.xaxis.set_minor_locator(
1130+
PolarXAxisMinorTickLocator(self)
1131+
)
1132+
1133+
# change property of ticks
1134+
axis.tick_params(**self.major_tick_kwargs)
1135+
axis.tick_params(**self.minor_tick_kwargs)
1136+
1137+
# add grid lines parallel to x-axis in azimuthal average
1138+
kwargs = {
1139+
'visible': True,
1140+
'which': 'major',
1141+
'axis': 'y',
1142+
'linewidth': 0.25,
1143+
'linestyle': '-',
1144+
'color': 'k',
1145+
'alpha': 0.75,
1146+
}
1147+
axis.grid(**kwargs)
1148+
1149+
kwargs = {
1150+
'visible': True,
1151+
'which': 'minor',
1152+
'axis': 'y',
1153+
'linewidth': 0.075,
1154+
'linestyle': '--',
1155+
'color': 'k',
1156+
'alpha': 0.9,
1157+
}
1158+
axis.grid(**kwargs)
1159+
1160+
# add grid lines parallel to y-axis
1161+
kwargs['which'] = 'both'
1162+
kwargs['axis'] = 'x'
1163+
axis.grid(**kwargs)
10731164
else:
10741165
self.update_azimuthal_integral_plot()
10751166
axis = self.azimuthal_integral_axis
10761167

10771168
# Update the xlabel in case it was modified (via tth distortion)
1078-
axis.set_xlabel(self.polar_xlabel)
1169+
axis.set_xlabel(self.polar_xlabel, **self.label_kwargs)
10791170
else:
10801171
if len(self.axes_images) == 0:
10811172
self.axis = self.figure.add_subplot(111)
@@ -1087,13 +1178,13 @@ def finish_show_polar(self, iviewer):
10871178
'interpolation': 'none',
10881179
}
10891180
self.axes_images.append(self.axis.imshow(**kwargs))
1090-
self.axis.set_ylabel(r'$\eta$ [deg]')
1181+
self.axis.set_ylabel(r'$\eta$ [deg]', **self.label_kwargs)
10911182
else:
10921183
rescale_image = False
10931184
self.axes_images[0].set_data(img)
10941185

10951186
# Update the xlabel in case it was modified (via tth distortion)
1096-
self.axis.set_xlabel(self.polar_xlabel)
1187+
self.axis.set_xlabel(self.polar_xlabel, **self.label_kwargs)
10971188

10981189
if rescale_image:
10991190
self.axis.relim()
@@ -1193,7 +1284,8 @@ def polar_x_axis_type(self):
11931284

11941285
def on_polar_x_axis_type_changed(self):
11951286
# Update the x-label
1196-
self.azimuthal_integral_axis.set_xlabel(self.polar_xlabel)
1287+
self.azimuthal_integral_axis.set_xlabel(
1288+
self.polar_xlabel, **self.label_kwargs)
11971289

11981290
# Still need to draw if the x-label was modified
11991291
self.draw_idle()
@@ -1701,7 +1793,12 @@ def draw_mask_boundaries(self, axis, det=None):
17011793
delta_eta_est = np.nanmedian(eta_diff)
17021794
tolerance = delta_eta_est * 10
17031795
big_gaps, = np.nonzero(eta_diff > tolerance)
1704-
verts[i] = np.insert(vert, big_gaps + 1, np.nan, axis=0)
1796+
verts[i] = np.insert(
1797+
vert,
1798+
big_gaps + 1,
1799+
np.nan,
1800+
axis=0,
1801+
)
17051802

17061803
if self.mode == ViewType.stereo:
17071804
# Now convert from polar to stereo
@@ -1756,6 +1853,67 @@ def tick_values(self, vmin, vmax):
17561853
return canvas.polar_x_type_to_tth(values)
17571854

17581855

1856+
class PolarXAxisMinorTickLocator(AutoMinorLocator):
1857+
"""Subclass the tick locator so we can modify its behavior
1858+
1859+
We will modify any value ranges provided so that the current x-axis type
1860+
provides nice looking ticks.
1861+
1862+
For instance, for Q, we want to space minor ticks non-linearly between
1863+
major ticks
1864+
"""
1865+
def __init__(self, canvas, *args, **kwargs):
1866+
super().__init__(*args, **kwargs)
1867+
self._hexrdgui_canvas = canvas
1868+
1869+
def __call__(self):
1870+
canvas = self._hexrdgui_canvas
1871+
if self.axis.get_scale() == 'log':
1872+
logging.warning(
1873+
'PolarXAxisMinorTickLocator does not work on logarithmic '
1874+
'scales'
1875+
)
1876+
return []
1877+
1878+
majorlocs = np.unique(self.axis.get_majorticklocs())
1879+
if len(majorlocs) < 2:
1880+
# Need at least two major ticks to find minor tick locations.
1881+
return []
1882+
1883+
# Convert to our current x type
1884+
majorlocs = canvas.polar_tth_to_x_type(majorlocs)
1885+
majorstep = majorlocs[1] - majorlocs[0]
1886+
1887+
if self.ndivs is None:
1888+
self.ndivs = mpl.rcParams[
1889+
'ytick.minor.ndivs' if self.axis.axis_name == 'y'
1890+
else 'xtick.minor.ndivs'] # for x and z axis
1891+
1892+
if self.ndivs == 'auto':
1893+
majorstep_mantissa = 10 ** (np.log10(majorstep) % 1)
1894+
if np.isclose(majorstep_mantissa, [1, 2.5, 5, 10]).any():
1895+
ndivs = 5
1896+
else:
1897+
ndivs = 4
1898+
else:
1899+
ndivs = self.ndivs
1900+
1901+
minorstep = majorstep / ndivs
1902+
1903+
vmin, vmax = sorted(self.axis.get_view_interval())
1904+
# Convert to our current x type
1905+
vmin, vmax = canvas.polar_tth_to_x_type([vmin, vmax])
1906+
t0 = majorlocs[0]
1907+
tmin = round((vmin - t0) / minorstep)
1908+
tmax = round((vmax - t0) / minorstep) + 1
1909+
locs = (np.arange(tmin, tmax) * minorstep) + t0
1910+
1911+
# Convert back to tth
1912+
locs = canvas.polar_x_type_to_tth(locs)
1913+
1914+
return self.raise_if_exceeds(locs)
1915+
1916+
17591917
class PolarXAxisFormatter(FuncFormatter):
17601918
"""Subclass the func formatter so we can keep the default formatter in sync
17611919

0 commit comments

Comments
 (0)