From 8ab0698fdfe646e668ea7d4e284fba63be04bac5 Mon Sep 17 00:00:00 2001 From: Eric Dill Date: Wed, 12 Nov 2014 15:15:53 -0500 Subject: [PATCH 1/3] MNT: Changes based on skxray refactor --- bubblegum/backend/mpl/__init__.py | 2 +- bubblegum/backend/mpl/cross_section_2d.py | 34 +++++++++++++++-------- bubblegum/backend/mpl/stack_1d.py | 4 +-- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/bubblegum/backend/mpl/__init__.py b/bubblegum/backend/mpl/__init__.py index ddb7a65..b98fdd2 100644 --- a/bubblegum/backend/mpl/__init__.py +++ b/bubblegum/backend/mpl/__init__.py @@ -48,7 +48,7 @@ class AbstractMPLDataView(object): """ Class docstring """ - _default_cmap = 'jet' + _default_cmap = 'gray' _default_norm = cm.colors.Normalize(vmin=0, vmax=1) def __init__(self, fig, cmap=None, norm=None, *args, **kwargs): diff --git a/bubblegum/backend/mpl/cross_section_2d.py b/bubblegum/backend/mpl/cross_section_2d.py index f74a065..d92842c 100644 --- a/bubblegum/backend/mpl/cross_section_2d.py +++ b/bubblegum/backend/mpl/cross_section_2d.py @@ -39,7 +39,7 @@ from six.moves import zip from matplotlib.widgets import Cursor from mpl_toolkits.axes_grid1 import make_axes_locatable -from matplotlib.ticker import NullLocator +from matplotlib.ticker import NullLocator, LinearLocator import numpy as np from . import AbstractMPLDataView @@ -138,9 +138,9 @@ def _percentile_limit(im): _INTERPOLATION = ['none', 'nearest', 'bilinear', 'bicubic', 'spline16', - 'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', - 'quadric', 'catrom', 'gaussian', 'bessel', 'mitchell', - 'sinc', 'lanczos'] + 'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', + 'quadric', 'catrom', 'gaussian', 'bessel', 'mitchell', + 'sinc', 'lanczos'] class CrossSection2DView(AbstractDataView2D, AbstractMPLDataView): @@ -177,14 +177,12 @@ def __init__(self, fig, data_list, key_list, cmap=None, norm=None, """ if 'limit_args' in kwargs: raise Exception("changed API, don't use limit_args anymore, use closures") - # call up the inheritance chain super(CrossSection2DView, self).__init__(fig=fig, data_list=data_list, key_list=key_list, norm=norm, cmap=cmap) self._xsection = CrossSection(fig, - self._data_dict[self._key_list[0]], - cmap=cmap, norm=norm, + cmap=self._cmap, norm=self._norm, limit_func=limit_func, interpolation=interpolation) @@ -248,7 +246,7 @@ def inner(self, *args, **kwargs): return ret inner.__name__ = func.__name__ - inner.__doct__ = func.__doc__ + inner.__doc__ = func.__doc__ return inner @@ -287,7 +285,13 @@ class CrossSection(object): Properties ---------- - interpolation + interpolation : str + The stringly-typed pixel interpolation. See _INTERPOLATION attribute + of this cross_section_2d module + cmap : str + The colormap to use for rendering the image + + """ def __init__(self, fig, cmap=None, norm=None, @@ -356,10 +360,10 @@ def __init__(self, fig, cmap=None, norm=None, # (set up the horizontal and vertical cuts) self._ax_h = divider.append_axes('top', .5, pad=0.1, sharex=self._im_ax) - self._ax_h.yaxis.set_major_locator(NullLocator()) + self._ax_h.yaxis.set_major_locator(LinearLocator(numticks=2)) self._ax_v = divider.append_axes('left', .5, pad=0.1, sharey=self._im_ax) - self._ax_v.xaxis.set_major_locator(NullLocator()) + self._ax_v.xaxis.set_major_locator(LinearLocator(numticks=2)) self._ax_cb = divider.append_axes('right', .2, pad=.5) # add the color bar self._cb = fig.colorbar(self._im, cax=self._ax_cb) @@ -644,3 +648,11 @@ def _update_artists(self): def _draw(self): self._fig.canvas.draw() + + @auto_redraw + def autoscale_horizontal(self, enable): + self._ax_h.autoscale(enable=enable) + + @auto_redraw + def autoscale_vertical(self, enable): + self._ax_v.autoscale(enable=False) diff --git a/bubblegum/backend/mpl/stack_1d.py b/bubblegum/backend/mpl/stack_1d.py index abb6582..ceff408 100644 --- a/bubblegum/backend/mpl/stack_1d.py +++ b/bubblegum/backend/mpl/stack_1d.py @@ -222,9 +222,9 @@ def clear_data(self): Override abstract base class to also clear the ordered dict mpl lines """ # clear all data from the data_dict - self._data_dict.clear() + self._data_dict._clear() # clear all lines from the lines_dict - self._lines_dict.clear() + self._lines_dict._clear() # clear the artists self._ax.cla() # clear the list of keys From 742111b39f84fcfd30769e578b3b4db1afa957ca Mon Sep 17 00:00:00 2001 From: Eric Dill Date: Wed, 12 Nov 2014 15:25:39 -0500 Subject: [PATCH 2/3] MNT: Fixed some demos and removed obsolete ones --- qt_apps/DEMO_Live.py | 161 ------------------------- qt_apps/DEMO_live.py | 139 ---------------------- qt_apps/DEMO_plot1D.py | 2 +- qt_apps/DEMO_plot1D_with_datainput.py | 164 -------------------------- 4 files changed, 1 insertion(+), 465 deletions(-) delete mode 100644 qt_apps/DEMO_Live.py delete mode 100644 qt_apps/DEMO_live.py delete mode 100644 qt_apps/DEMO_plot1D_with_datainput.py diff --git a/qt_apps/DEMO_Live.py b/qt_apps/DEMO_Live.py deleted file mode 100644 index 5379548..0000000 --- a/qt_apps/DEMO_Live.py +++ /dev/null @@ -1,161 +0,0 @@ -# ###################################################################### -# Copyright (c) 2014, Brookhaven Science Associates, Brookhaven # -# National Laboratory. All rights reserved. # -# # -# Redistribution and use in source and binary forms, with or without # -# modification, are permitted provided that the following conditions # -# are met: # -# # -# * Redistributions of source code must retain the above copyright # -# notice, this list of conditions and the following disclaimer. # -# # -# * Redistributions in binary form must reproduce the above copyright # -# notice this list of conditions and the following disclaimer in # -# the documentation and/or other materials provided with the # -# distribution. # -# # -# * Neither the name of the Brookhaven Science Associates, Brookhaven # -# National Laboratory nor the names of its contributors may be used # -# to endorse or promote products derived from this software without # -# specific prior written permission. # -# # -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # -# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # -# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OTHERWISE) ARISING # -# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # -# POSSIBILITY OF SUCH DAMAGE. # -######################################################################## -from __future__ import (absolute_import, division, print_function, - unicode_literals) -import six -import os -import sys -import numpy as np -from bubblegum import QtCore, QtGui -from bubblegum.qt_widgets.real_time import LiveWindow - -from hashlib import sha1 -import datetime - -data_keys = ['x_motor', 'intensity'] - -def gen_id(): - """Generate a sha1 hash of the datetime - """ - return sha1(str(datetime.datetime.utcnow())) - - -def gen_ev_description(): - """Return one of a few pre-defined descriptions - """ - options = ['hkl_scan', 'ascan', 'dscan'] - return options[np.random.randint(len(options))] - - -def gen_hdr(): - hdr = { - 'id': gen_id(), - 'owner': 'edill', - } - return hdr - - -def gen_ev_desc(): - ev_desc = { - 'id': gen_id(), - 'description': gen_ev_description, - 'data_keys': data_keys, - } - return ev_desc - - -def gen_data(): - num_data = 15 - data = {} - for idx, key in enumerate(data_keys): - if 'motor' in key: - # treat it as a motor - data[key] = range(num_data) - elif 'det' in key: - # treat it as a detector - if '0' in key: - # assume it is a point detector - data[key] = np.random.rand(num_data) - elif '1' in key: - # assume it is a strip detector - raise NotImplementedError('strip detector is not supported ' - 'in gen_data()') - elif '2' in key: - # assume it is an area detector - raise NotImplementedError('area detector is not supported ' - 'in gen_data()') - return data - -def gen_event(): - ev = { - 'id': gen_id(), - 'time': datetime.datetime.utcnow(), - 'data': gen_data(), - } - return ev - - -class LiveApp(QtGui.QMainWindow): - def __init__(self, parent=None): - QtGui.QMainWindow.__init__(self, parent=parent) - self.setWindowTitle('LiveApp') - self._main_window = LiveWindow.init_demo() - - self._main_window.setFocus() - self.setCentralWidget(self._main_window) - - # init the dock widget for the execute button - btn_panel = QtGui.QDockWidget() - btn_panel.setFloating(True) - # create buttons - btn_hdr = QtGui.QPushButton('new header') - btn_ev_desc = QtGui.QPushButton('new event descriptor') - btn_event = QtGui.QPushButton('new event') - # connect the button click to the relevant slots - btn_hdr.clicked.connect(self.fire_hdr) - btn_ev_desc.clicked.connect(self.fire_ev_desc) - btn_event.clicked.connect(self.fire_event) - - # add the buttons to the layout - btn_panel_layout = QtGui.QVBoxLayout() - btn_panel_layout.addWidget(btn_hdr) - btn_panel_layout.addWidget(btn_ev_desc) - btn_panel_layout.addWidget(btn_event) - - btn_widget = QtGui.QWidget() - btn_widget.setLayout(btn_panel_layout) - btn_panel.setWidget(btn_widget) - # add the button panel to the window - self._main_window.addDockWidget(QtCore.Qt.LeftDockWidgetArea, btn_panel) - - @QtCore.Slot() - def fire_hdr(self): - # fire a new run header - self._main_window.update("header", gen_hdr()) - @QtCore.Slot() - def fire_ev_desc(self): - # fire a new run header - self._main_window.update("event_descriptor", gen_ev_desc()) - @QtCore.Slot() - def fire_event(self): - # fire a new run header - self._main_window.update("event", gen_event()) - - -if __name__ == "__main__": - app = QtGui.QApplication(sys.argv) - tt = LiveApp() - tt.show() - sys.exit(app.exec_()) diff --git a/qt_apps/DEMO_live.py b/qt_apps/DEMO_live.py deleted file mode 100644 index d0466e8..0000000 --- a/qt_apps/DEMO_live.py +++ /dev/null @@ -1,139 +0,0 @@ -# ###################################################################### -# Copyright (c) 2014, Brookhaven Science Associates, Brookhaven # -# National Laboratory. All rights reserved. # -# # -# Redistribution and use in source and binary forms, with or without # -# modification, are permitted provided that the following conditions # -# are met: # -# # -# * Redistributions of source code must retain the above copyright # -# notice, this list of conditions and the following disclaimer. # -# # -# * Redistributions in binary form must reproduce the above copyright # -# notice this list of conditions and the following disclaimer in # -# the documentation and/or other materials provided with the # -# distribution. # -# # -# * Neither the name of the Brookhaven Science Associates, Brookhaven # -# National Laboratory nor the names of its contributors may be used # -# to endorse or promote products derived from this software without # -# specific prior written permission. # -# # -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # -# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # -# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OTHERWISE) ARISING # -# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # -# POSSIBILITY OF SUCH DAMAGE. # -######################################################################## -""" -Example usage of StackScanner -' -""" -from __future__ import (absolute_import, division, print_function, - unicode_literals) - -import sys - -from bubblegum import QtGui, QtCore -import numpy as np - -from bubblegum.messenger.mpl.cross_section_2d import CrossSection2DMessenger - -from bubblegum.qt_widgets import CrossSectionMainWindow -from nsls2 import core - -import logging -logger = logging.getLogger(__name__) - - -class FrameSourcerBrownian(QtCore.QObject): - new_frame = QtCore.Signal(np.ndarray) - - def __init__(self, im_shape, step_scale=1, decay=30, - delay=500, parent=None): - QtCore.QObject.__init__(self, parent) - self._im_shape = np.asarray(im_shape) - self._scale = step_scale - self._decay = decay - self._delay = delay - if self._im_shape.ndim != 1 and len(self._im_shape) != 2: - raise ValueError("image shape must be 2 dimensional " - "you passed in {}".format(im_shape)) - self._cur_position = np.array(np.asarray(im_shape) / 2) - - self.timer = QtCore.QTimer(parent=self) - self.timer.timeout.connect(self.get_next_frame) - self._count = 0 - - @QtCore.Slot() - def get_next_frame(self): - print('fired {}'.format(self._count)) - self._count += 1 - im = self.gen_next_frame() - self.new_frame.emit(im) - return True - - def gen_next_frame(self): - # add a random step - self._cur_position += np.random.randn(2) * self._scale - # clip it - self._cur_position = np.array([np.clip(v, 0, mx) for - v, mx in zip(self._cur_position, - self._im_shape)]) - - R = core.pixel_to_radius(self._im_shape, - self._cur_position).reshape(self._im_shape) - im = np.exp((-R**2 / self._decay)) - return im - - @QtCore.Slot() - def start(self): - self.timer.start(self._delay) - - @QtCore.Slot() - def stop(self): - self.timer.stop() - - -class StackExplorer(QtGui.QMainWindow): - def __init__(self, parent=None): - QtGui.QMainWindow.__init__(self, parent=parent) - self.setWindowTitle('Brownian Motion') - self.thread = QtCore.QThread(parent=self) - self.worker = FrameSourcerBrownian((1024, 1000), step_scale=5, - decay=100, delay=1000, parent=None) - self.worker.moveToThread(self.thread) - self.worker.timer.moveToThread(self.thread) - init_data = self.worker.gen_next_frame() - print(init_data.shape) - self._main_window = CrossSectionMainWindow(data_list=[init_data], - key_list=['foo']) - - self._main_window.setFocus() - layout = self._main_window._ctrl_widget._widget.layout() - self.start_btn = QtGui.QPushButton('start', parent=self) - self.stop_btn = QtGui.QPushButton('stop', parent=self) - layout.addWidget(self.start_btn) - layout.addWidget(self.stop_btn) - - self.start_btn.clicked.connect(self.worker.start) - self.stop_btn.clicked.connect(self.worker.stop) - self.setCentralWidget(self._main_window) - - self.worker.new_frame.connect( - self._main_window._messenger._view._xsection.update_image) - self.thread.start() - - -app = QtGui.QApplication(sys.argv) -tt = StackExplorer() -tt.show() - -sys.exit(app.exec_()) diff --git a/qt_apps/DEMO_plot1D.py b/qt_apps/DEMO_plot1D.py index 35f29d5..1f62009 100644 --- a/qt_apps/DEMO_plot1D.py +++ b/qt_apps/DEMO_plot1D.py @@ -40,11 +40,11 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from six.moves import zip -from matplotlib.backends.qt4_compat import QtGui, QtCore import sys import numpy as np from collections import OrderedDict +from bubblegum import QtCore, QtGui from bubblegum.qt_widgets import Stack1DMainWindow from bubblegum.messenger.mpl.stack_1d import Stack1DMessenger diff --git a/qt_apps/DEMO_plot1D_with_datainput.py b/qt_apps/DEMO_plot1D_with_datainput.py deleted file mode 100644 index 295aea8..0000000 --- a/qt_apps/DEMO_plot1D_with_datainput.py +++ /dev/null @@ -1,164 +0,0 @@ -# ###################################################################### -# Copyright (c) 2014, Brookhaven Science Associates, Brookhaven # -# National Laboratory. All rights reserved. # -# # -# Redistribution and use in source and binary forms, with or without # -# modification, are permitted provided that the following conditions # -# are met: # -# # -# * Redistributions of source code must retain the above copyright # -# notice, this list of conditions and the following disclaimer. # -# # -# * Redistributions in binary form must reproduce the above copyright # -# notice this list of conditions and the following disclaimer in # -# the documentation and/or other materials provided with the # -# distribution. # -# # -# * Neither the name of the Brookhaven Science Associates, Brookhaven # -# National Laboratory nor the names of its contributors may be used # -# to endorse or promote products derived from this software without # -# specific prior written permission. # -# # -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # -# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # -# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OTHERWISE) ARISING # -# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # -# POSSIBILITY OF SUCH DAMAGE. # -######################################################################## -""" -Example usage of StackScanner -' -""" -from __future__ import (absolute_import, division, print_function, - unicode_literals) - -from collections import OrderedDict -import sys -import os - -from matplotlib.backends.qt4_compat import QtGui, QtCore -import numpy as np - -from bubblegum.qt_widgets.OneDimStackWidget import OneDimStackMainWindow - -import logging -logger = logging.getLogger(__name__) - - -def data_gen(num_sets=1, phase_shift=0.1, vert_shift=0.1, horz_shift=0.1): - """ - Generate some data - - Parameters - ---------- - num_sets: int - number of 1-D data sets to generate - - Returns - ------- - x : np.ndarray - x-coordinates - y : list of np.ndarray - y-coordinates - """ - x_axis = np.arange(0, 25, .01) - x = [] - y = [] - for idx in range(num_sets): - x.append(x_axis + horz_shift) - y.append(np.sin(x_axis + idx * phase_shift) + idx * vert_shift) - - return x, y - - -def get_files(): - files = QtGui.QFileDialog.getOpenFileName(parent=None, - caption="File opener") - for file in files: - print(str(file)) - return files - - -def parse_files(files): - lbls = [] - x_datasets = [] - y_datasets = [] - if os.path.isdir(files): - for afile in files: - f = open(afile, 'r') - x, y = read_file(f) - lbls.append(str(afile)) - x_datasets.append(x) - y_datasets.append(y) - else: - f = open(files, 'r') - x, y = read_file(f) - lbls.append(str(files)) - x_datasets.append(x) - y_datasets.append(y) - - return lbls, x_datasets, y_datasets - - -def read_file(afile): - # probably need to call a specific file reader here - - # read data - x = None - y = None - - return x, y - - -class demo_1d(QtGui.QMainWindow): - - def __init__(self, parent=None): - QtGui.QMainWindow.__init__(self, parent) - # Generate data - num_sets = 100 - x_data, y_data = data_gen(num_sets=num_sets, phase_shift=0, - horz_shift=0, vert_shift=0) - od = OrderedDict() - for (lbl, x, y) in zip(range(num_sets), x_data, y_data): - od[lbl] = (x, y) - # init the 1d stack main window - self._widget = OneDimStackMainWindow(data_dict=od) - # add the demo buttons - - # declare button to generate data for testing/example purposes - self.btn_loaddata = QtGui.QPushButton("load data set", - parent=self._widget._ctrl_widget) - - self.btn_loaddata.clicked.connect(self.open_data) - layout = self._widget._ctrl_widget._widget.layout() - - layout.addRow("--- File IO Buttons ---", None) - layout.addRow(self.btn_loaddata, None) - - # connect signals to test harness - self.sig_add_real_data.connect( - self._widget._widget._canvas.sl_add_data) - - self.setCentralWidget(self._widget) - - # Qt Signals for Data loading - sig_add_real_data = QtCore.Signal(list, list, list) - - @QtCore.Slot() - def open_data(self): - files = get_files() - self.sig_add_real_data.emit(*parse_files(files)) - - -if __name__ == "__main__": - app = QtGui.QApplication(sys.argv) - tt = demo_1d() - tt.show() - sys.exit(app.exec_()) From f1a68d36662f437fce78811c014862cd2726bb6e Mon Sep 17 00:00:00 2001 From: Eric Dill Date: Thu, 13 Nov 2014 09:04:30 -0500 Subject: [PATCH 3/3] MNT: Unrefactored an overzealous refactor --- bubblegum/backend/mpl/stack_1d.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bubblegum/backend/mpl/stack_1d.py b/bubblegum/backend/mpl/stack_1d.py index ceff408..abb6582 100644 --- a/bubblegum/backend/mpl/stack_1d.py +++ b/bubblegum/backend/mpl/stack_1d.py @@ -222,9 +222,9 @@ def clear_data(self): Override abstract base class to also clear the ordered dict mpl lines """ # clear all data from the data_dict - self._data_dict._clear() + self._data_dict.clear() # clear all lines from the lines_dict - self._lines_dict._clear() + self._lines_dict.clear() # clear the artists self._ax.cla() # clear the list of keys