Skip to content

Commit

Permalink
Merge pull request #19 from chiahaoliu/REF_plot_update
Browse files Browse the repository at this point in the history
REF: waterfall plot update
  • Loading branch information
chiahaoliu authored Sep 11, 2017
2 parents 338c1bd + d0da225 commit a622d5f
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 29 deletions.
26 changes: 26 additions & 0 deletions xpdview/tests/test_viwers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""module to start pure mpl-backend viewer"""
import matplotlib.pyplot as plt
import numpy as np
from xpdview.cross_2d import StackViewer, CrossSection
from xpdview.waterfall import Waterfall

# def data list to test
img_data_list = []
key_list = []
int_data_list = []
x = np.linspace(0, 2*np.pi, 200)
for i in range(5):
key_list.append(str(i))
img_data_list.append(np.random.rand(50, 50))
int_data_list.append((x, np.sin(x)))

# init
viewer_fig = plt.figure('test_viewer') # let mpl decide backend
cross_section = CrossSection(viewer_fig)
stack_viewer = StackViewer(cross_section, img_data_list=img_data_list,
key_list=key_list)
waterfall_fig = plt.figure('test_waterfall') # let mpl decide backend
waterfall = Waterfall(fig=waterfall_fig, key_list=key_list,
int_data_list=int_data_list,
unit=('x_unit', 'y_unit'))
plt.show()
19 changes: 19 additions & 0 deletions xpdview/tests/test_waterfall.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import numpy as np
import matplotlib.pyplot as plt
from xpdview.waterfall import Waterfall

fig = plt.figure('test_title')
unit = ('x unit', 'y unit')
int_data_list = []
key_list = []

x = np.linspace(0, 4*np.pi, 200)
for i in range(5):
key_list.append(str(i))
int_data_list.append((x, np.sin(x)))

p1 = Waterfall()
p2 = Waterfall(fig, key_list=key_list, int_data_list=int_data_list,
unit=unit)

plt.show()
13 changes: 13 additions & 0 deletions xpdview/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@
import os
import numpy as np


def conf_label_size(ax, label_size):
ax.xaxis.label.set_size(label_size)
ax.yaxis.label.set_size(label_size)


def conf_tick_size(ax, tick_size):
for tick in ax.xaxis.get_major_ticks():
tick.label.set_fontsize(tick_size)
for tick in ax.yaxis.get_major_ticks():
tick.label.set_fontsize(tick_size)


def chi_read(fn, skiprows=4):
"""wrapper for reading .chi files
Expand Down
71 changes: 42 additions & 29 deletions xpdview/waterfall.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider


def _normalize(self, array, max_val, min_val):
"""core function to normalize a ndarray"""
return np.subtract(array, min_val) / np.subtract(max_val, min_val)

from .utils import conf_label_size, conf_tick_size
from cycler import cycler
simonCycle2 = ["#0B3C5D", "#B82601", "#1c6b0a",
"#328CC1", "#062F4F", "#D9B310",
"#984B43", "#76323F", "#626E60",
"#AB987A", "#C09F80", "#b0b0b0ff"]
mpl.rcParams['axes.prop_cycle'] = cycler(color=simonCycle2)

class Waterfall:
"""class holds data and generate watefall plot
Expand All @@ -24,11 +26,18 @@ class Waterfall:
format. default to None
unit : tuple, optional
a tuple containing strings of x and y labels
label_size : int, optional
size of x-, y-label. default is 16
tick_size : int, optional
size of x-, y-tick. default is 14
kwargs :
keyword arguments for plotting
"""

def __init__(self, fig=None, canvas=None,
key_list=None, int_data_list=None,
*, unit=None):
*, unit=None, label_size=16, tick_size=14,
**kwargs):
if int_data_list is None:
int_data_list = []
if key_list is None:
Expand All @@ -39,14 +48,16 @@ def __init__(self, fig=None, canvas=None,
if not canvas:
canvas = self.fig.canvas
self.canvas = canvas
self.kwargs = kwargs

# callback for showing legend
self.canvas.mpl_connect('pick_event', self.on_plot_hover)
self.key_list = key_list
self.int_data_list = int_data_list
self.ax = self.fig.add_subplot(111)
self.unit = unit

self.label_size = label_size
self.tick_size = tick_size
# flag to prevent update
self.halt = False
# add sliders, which store information
Expand All @@ -64,6 +75,7 @@ def __init__(self, fig=None, canvas=None,
# init
self.update(self.key_list, self.int_data_list, refresh=True)


def update(self, key_list=None, int_data_list=None, refresh=False):
"""top method to update information carried by class and plot
Expand All @@ -88,6 +100,7 @@ def update(self, key_list=None, int_data_list=None, refresh=False):
self.int_data_list = []
self.key_list.extend(key_list)
self.int_data_list.extend(int_data_list)
self._adapt_data_list(int_data_list)
# generate plot
self.halt = False
self._update_plot() # use current value of x,y offset
Expand All @@ -96,17 +109,15 @@ def _adapt_data_list(self, int_data_list):
"""method to return statefull information of 1D data list"""
x_array_list = []
y_array_list = []
# parse
for x, y in int_data_list:
x_array_list.append(x)
y_array_list.append(y)
y_max = np.max(y_array_list)
y_min = np.min(y_array_list)
y_dist = y_max - y_min
x_max = np.max(x_array_list)
x_min = np.min(x_array_list)
x_dist = x_max - x_min
return (x_array_list, y_array_list, y_min, y_max,
y_dist, x_min, x_max, x_dist)
self.x_array_list = x_array_list
self.y_array_list = y_array_list
# stateful information
self.y_dist = np.max(y_array_list) - np.min(y_array_list)
self.x_dist = np.max(x_array_list) - np.min(x_array_list)

def on_plot_hover(self, event):
"""callback to show legend when click on one of curves"""
Expand All @@ -120,24 +131,26 @@ def _update_plot(self, x_offset_val=None, y_offset_val=None):
"""core method to update x-, y-offset sliders"""
self.ax.set_facecolor('w')
self.ax.cla()
# remain current offset
if not x_offset_val:
x_offset_val = self.x_offset_slider.val
if not y_offset_val:
y_offset_val = self.y_offset_slider.val
# get stateful info
state = self._adapt_data_list(self.int_data_list)
x_array_list, y_array_list, \
y_min, y_max, y_dist, x_min, x_max, x_dist = state
for ind, el in enumerate(zip(x_array_list, y_array_list)):
for ind, el in enumerate(zip(self.x_array_list,
self.y_array_list)):
x, y = el
self.ax.plot(x + x_dist * ind * x_offset_val,
y + y_dist * ind * y_offset_val,
label=self.key_list[ind], picker=5)
self.ax.plot(x + self.x_dist * ind * x_offset_val,
y + self.y_dist * ind * y_offset_val,
label=self.key_list[ind], picker=5,
**self.kwargs)
self.ax.autoscale()
if self.unit:
xlabel, ylabel = self.unit
self.ax.set_xlabel = xlabel
self.ax.set_ylabel = ylabel
self.ax.set_xlabel(xlabel)
self.ax.set_ylabel(ylabel)
conf_tick_size(self.ax, self.tick_size)
conf_label_size(self.ax, self.label_size)
self.canvas.draw_idle()

def update_y_offset(self, val):
Expand All @@ -155,10 +168,10 @@ def no_int_data_plot(self, ax, canvas):
"""
ax.cla()
ax.text(.5, .5,
'{}'.format("We couldn't find reduced data in directory "
"currently set.\nReducded data are generated "
"by proper calibration and integration.\n"
"Please go to our documentation for more details:\n"
'{}'.format("No reduced data was generated.\n"
"Proper calibration is required for "
"data reduction.\n"
"Please refer to our documentation for more details:\n"
"http://xpdacq.github.io/quickstart.html"),
ha='center', va='center', color='w',
transform=ax.transAxes, size=11)
Expand Down

0 comments on commit a622d5f

Please sign in to comment.