Skip to content

Commit

Permalink
Merge pull request NSLS-II-XPD#15 from CJ-Wright/waterfall_callback
Browse files Browse the repository at this point in the history
Waterfall callback
  • Loading branch information
chiahaoliu authored Aug 23, 2017
2 parents 0b08c14 + 5353f42 commit 869a78e
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 26 deletions.
33 changes: 33 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# codecov can find this file anywhere in the repo, so we don't need to clutter
# the root folder.
#comment: false

codecov:
notify:
require_ci_to_pass: no

coverage:
status:
patch:
default:
target: '80'
if_no_uploads: error
if_not_found: success
if_ci_failed: failure
project:
default: false
library:
target: auto
if_no_uploads: error
if_not_found: success
if_ci_failed: failure
paths: '!*/tests/.*'

tests:
target: 97.9%
paths: '*/tests/.*'

flags:
tests:
paths:
- tests/
13 changes: 13 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[run]
source =
xpdview
[report]
omit =
*/python?.?/*
*/site-packages/nose/*
# ignore _version.py and versioneer.py
.*version.*
*_version.py

exclude_lines =
if __name__ == '__main__':
9 changes: 9 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[flake8]
exclude =
.git,
__pycache__,
doc/conf.py,
old,
build,
dist,
max-complexity = 10
3 changes: 3 additions & 0 deletions .landscape.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
python-targets:
- 3
doc-warnings: true
70 changes: 70 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
language: python
sudo: false
env:
global:
BUILD_DOCS_BRANCH: master
DOCS_SUBDIR: doc/
TARGET_DEV_SUBDIR: bluesky/
HTML_SUBDIR: build/html
DOCS_CONDA_DEPS: "bluesky"
DOCS_PIP_DEPS: "tqdm"

cache:
directories:
- $HOME/.cache/pip
- $HOME/.cache/matplotlib

services:
- mongodb
addons:
apt:
sources:
- mongodb-3.2-precise
packages:
- mongodb-org-server

python:
- 3.5
before_install:
- git clone https://github.com/NSLS-II/nsls2-ci --branch master --single-branch ~/ci_scripts
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16"
- "export DISPLAY=:99.0"
- wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh
- chmod +x miniconda.sh
- ./miniconda.sh -b -p ~/mc
- export PATH=~/mc/bin:$PATH
- conda update conda --yes
- export CONDARC=ci/condarc
- export MDS_HOST=localhost
- export MDS_DATABASE=test
- export MDS_TIMEZONE=US/Eastern
- mkdir -p /home/travis/.config/metadatastore
- 'echo ''port: 27017'' > /home/travis/.config/metadatastore/connection.yml'
- export FS_HOST=localhost
- export FS_DATABASE=test
- mkdir -p /home/travis/.config/filestore
- 'echo ''port: 27017'' > /home/travis/.config/filestore/connection.yml'


install:
- export GIT_FULL_HASH=`git rev-parse HEAD`
- conda create --yes -n testenv numpy scipy flake8 matplotlib python=$TRAVIS_PYTHON_VERSION pytest coverage pip databroker ophyd historydict boltons doct pyepics super_state_machine mock xlrd scikit-beam bluesky pyFAI pyxdameraulevenshtein pyqt=4 -c lightsource2 -c conda-forge -c soft-matter
- source activate testenv
- python setup.py install
# Need to clean the python build directory (and other cruft) or pytest is
# going to find the build directory and get confused why there are two sets
# of every test file
- python -c "from filestore import utils, conf; utils.install_sentinels(conf.connection_config, 1)"
# make sure the sqlite file exists to avoid race conditions
- python -c "from bluesky.utils import get_history; get_history()"
- pip install codecov pytest-env python-coveralls
- git clean -xfd

script:
# - coverage run run_tests.py
# - coverage report -m
- flake8 xpdview

#after_success:
# - codecov
# - coveralls
6 changes: 5 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@
description='Visualization Code for Beam line',
zip_safe=False,
url='https://github.com/chiahaoliu/xpdView.git',
)
install_requires=['numpy', 'six', 'matplotlib',
# 'PyQt5', 'PyQt4',
'scipy',
'tifffile']
)
47 changes: 47 additions & 0 deletions xpdview/callbacks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from bluesky.callbacks.broker import BrokerCallbackBase
from .waterfall import Waterfall


class LiveWaterfall(BrokerCallbackBase):
"""
Stream 1D images in a waterfall viewer.
Parameters
----------
x_name : str
field name for x dimension
y_name: str
field name for y dimension
db: databroker.Broker instance
The Broker to fill the events if events are not filled
units: tuple of str
The units for the x and y axes
"""

def __init__(self, x_name, y_name, db=None, units=None):
super().__init__((x_name, y_name,), db=db)
self.db = db
self.x_name = x_name
self.y_name = y_name
self.units = units

from matplotlib.figure import Figure
self.fig = Figure()

self.wf = Waterfall(fig=self.fig, unit=self.units)
self.i = 0

def start(self, doc):
self.i = 0
self.wf.key_list.clear()
self.wf.int_data_list.clear()

def event(self, doc):
super().event(doc)
y = doc['data'][self.y_name]
x = doc['data'][self.x_name]
self.update((x, y))

def update(self, data):
self.wf.update(key_list=[self.i], int_data_list=[data])
self.i += 1
11 changes: 6 additions & 5 deletions xpdview/one_dimensional_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
##############################################################################

"""
This file will handle all of the one dimensional plotting of the lower left tile in the
Display Window
This file will handle all of the one dimensional plotting of the lower left
tile in the Display Window
"""
import warnings
import matplotlib.pyplot as plt
Expand All @@ -27,7 +27,8 @@ class IntegrationPlot(object):
Attributes
----------
int_data_dict : dict
this dictionary holds the data to be plotted by the user; data must be stored as lists inside a list with the
this dictionary holds the data to be plotted by the user; data must be
stored as lists inside a list with the
format [x_data_list, y_data_list]
fig : object
this needs to be a matplotlib figure
Expand Down Expand Up @@ -87,9 +88,9 @@ def give_plot(self, key):
try:
# grab corresponding data based on key
data = self.int_data_dict[use_key]
x,y = data
x, y = data
self.ax.plot(x, y)
#self.ax.hold(False)
# self.ax.hold(False)
self.ax.legend([use_key[:10]])
self.ax.autoscale()
self.canvas.draw()
Expand Down
44 changes: 24 additions & 20 deletions xpdview/waterfall.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
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)


class Waterfall:
"""class holds data and generate watefall plot
Expand All @@ -23,19 +25,31 @@ class Waterfall:
unit : tuple, optional
a tuple containing strings of x and y labels
"""
def __init__(self, fig, canvas, key_list=None, int_data_list=None,

def __init__(self, fig=None, canvas=None,
key_list=None, int_data_list=None,
*, unit=None):
if int_data_list is None:
int_data_list = []
if key_list is None:
key_list = []
if not fig:
fig = plt.figure()
self.fig = fig
if not canvas:
canvas = self.fig.canvas
self.canvas = canvas

# 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

# flag to prevent update
self.halt = False
# add sliders, which store informations
# add sliders, which store information
y_offset_slider_ax = self.fig.add_axes([0.15, 0.95, 0.3, 0.035])
self.y_offset_slider = Slider(y_offset_slider_ax,
'y-offset', 0.0, 1.0,
Expand Down Expand Up @@ -63,21 +77,11 @@ def update(self, key_list=None, int_data_list=None, refresh=False):
option to set refresh or not. default to False.
"""
if not int_data_list:
print("INFO: no reduced data is feeded in, "
print("INFO: no reduced data was fed in, "
"waterfall plot can't be updated")
self.halt = True
self.no_int_data_plot(self.ax, self.canvas)
return
# validate shape
array_shape_list = list(map(lambda x: np.shape(x[0]),
int_data_list))
array_shape_num = np.unique(array_shape_list)
if len(array_shape_num)> 1:
print("INFO: there are reduced data with different length, "
"they might come from different calibrations or "
"experiment setups. Please check if your data files\n"
"INFO: waterfall plot won't be updated")
return
# refresh list
if refresh:
self.key_list = []
Expand All @@ -86,13 +90,13 @@ def update(self, key_list=None, int_data_list=None, refresh=False):
self.int_data_list.extend(int_data_list)
# generate plot
self.halt = False
self._update_plot()# use current value of x,y offset
self._update_plot() # use current value of x,y offset

def _adapt_data_list(self, int_data_list):
"""method to return statefull information of 1D data list"""
x_array_list = []
y_array_list = []
for x,y in int_data_list:
for x, y in int_data_list:
x_array_list.append(x)
y_array_list.append(y)
y_max = np.max(y_array_list)
Expand All @@ -108,7 +112,7 @@ def on_plot_hover(self, event):
"""callback to show legend when click on one of curves"""
line = event.artist
name = line.get_label()
line.axes.legend([name],handlelength=0,
line.axes.legend([name], handlelength=0,
handletextpad=0, fancybox=True)
line.figure.canvas.draw_idle()

Expand All @@ -122,8 +126,8 @@ def _update_plot(self, x_offset_val=None, y_offset_val=None):
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
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)):
x, y = el
self.ax.plot(x + x_dist * ind * x_offset_val,
Expand Down Expand Up @@ -156,7 +160,7 @@ def no_int_data_plot(self, ax, canvas):
"by proper calibration and integration.\n"
"Please go to our documentation for more details:\n"
"http://xpdacq.github.io/quickstart.html"),
ha='center', va='center', color='w',
transform=ax.transAxes, size=11)
ha='center', va='center', color='w',
transform=ax.transAxes, size=11)
ax.set_facecolor('k')
canvas.draw_idle()

0 comments on commit 869a78e

Please sign in to comment.