""" Boxsum Class: determine 2 rectangular area to compute the sum of pixel of a Data. """ import numpy from PyQt4 import QtGui from PyQt4 import QtCore from sas.qtgui.Utilities.GuiUtils import formatNumber from BaseInteractor import BaseInteractor from sas.sascalc.dataloader.manipulations import Boxavg from sas.sascalc.dataloader.manipulations import Boxsum from sas.qtgui.Plotting.SlicerModel import SlicerModel class BoxSumCalculator(BaseInteractor): """ Boxsum Class: determine 2 rectangular area to compute the sum of pixel of a Data. Uses PointerInteractor , VerticalDoubleLine,HorizontalDoubleLine. @param zorder: Artists with lower zorder values are drawn first. @param x_min: the minimum value of the x coordinate @param x_max: the maximum value of the x coordinate @param y_min: the minimum value of the y coordinate @param y_max: the maximum value of the y coordinate """ def __init__(self, base, axes, color='black', zorder=3): BaseInteractor.__init__(self, base, axes, color=color) # list of Boxsmun markers self.markers = [] self.axes = axes self._model = None self.update_model = False # connect the artist for the motion self.connect = self.base.connect # when qmax is reached the selected line is reset the its previous value self.qmax = min(self.base.data.xmax, self.base.data.xmin) # Define the boxsum limits self.xmin = -1 * 0.5 * min(numpy.fabs(self.base.data.xmax), numpy.fabs(self.base.data.xmin)) self.ymin = -1 * 0.5 * min(numpy.fabs(self.base.data.xmax), numpy.fabs(self.base.data.xmin)) self.xmax = 0.5 * min(numpy.fabs(self.base.data.xmax), numpy.fabs(self.base.data.xmin)) self.ymax = 0.5 * min(numpy.fabs(self.base.data.xmax), numpy.fabs(self.base.data.xmin)) # center of the boxSum self.center_x = 0.0002 self.center_y = 0.0003 # Number of points on the plot self.nbins = 20 # Define initial result the summation self.count = 0 self.error = 0 self.total = 0 self.totalerror = 0 self.points = 0 # Flag to determine if the current figure has moved # set to False == no motion , set to True== motion self.has_move = False # Create Boxsum edges self.horizontal_lines = HorizontalDoubleLine(self, self.axes, color='blue', zorder=zorder, y=self.ymax, x=self.xmax, center_x=self.center_x, center_y=self.center_y) self.horizontal_lines.qmax = self.qmax self.vertical_lines = VerticalDoubleLine(self, self.axes, color='black', zorder=zorder, y=self.ymax, x=self.xmax, center_x=self.center_x, center_y=self.center_y) self.vertical_lines.qmax = self.qmax self.center = PointInteractor(self, self.axes, color='grey', zorder=zorder, center_x=self.center_x, center_y=self.center_y) # Save the name of the slicer panel associate with this slicer self.panel_name = "" # Update and post slicer parameters self.update_model = False self.update() self.postData() # set up the model self._model = QtGui.QStandardItemModel(1, 9) self.setModelFromParams() self.update_model = True self._model.itemChanged.connect(self.setParamsFromModel) def setModelFromParams(self): """ Set up the Qt model for data handling between controls """ parameters = self.getParams() # Crete/overwrite model items self._model.setData(self._model.index(0, 0), QtCore.QVariant(formatNumber(parameters['Height']))) self._model.setData(self._model.index(0, 1), QtCore.QVariant(formatNumber(parameters['Width']))) self._model.setData(self._model.index(0, 2), QtCore.QVariant(formatNumber(parameters['center_x']))) self._model.setData(self._model.index(0, 3), QtCore.QVariant(formatNumber(parameters['center_y']))) self.setReadOnlyParametersFromModel() def model(self): ''' model accessor ''' return self._model def setReadOnlyParametersFromModel(self): """ Cast model content onto "read-only" subset of parameters """ parameters = self.getParams() self._model.setData(self._model.index(0, 4), QtCore.QVariant(formatNumber(parameters['avg']))) self._model.setData(self._model.index(0, 5), QtCore.QVariant(formatNumber(parameters['avg_error']))) self._model.setData(self._model.index(0, 6), QtCore.QVariant(formatNumber(parameters['sum']))) self._model.setData(self._model.index(0, 7), QtCore.QVariant(formatNumber(parameters['sum_error']))) self._model.setData(self._model.index(0, 8), QtCore.QVariant(formatNumber(parameters['num_points']))) def setParamsFromModel(self): """ Cast model content onto params dict """ params = {} params["Height"] = float(self.model().item(0, 0).text()) params["Width"] = float(self.model().item(0, 1).text()) params["center_x"] = float(self.model().item(0, 2).text()) params["center_y"] = float(self.model().item(0, 3).text()) self.update_model = False self.setParams(params) self.setReadOnlyParametersFromModel() self.update_model = True def setPanelName(self, name): """ Store the name of the panel associated to this slicer @param name: the name of this panel """ self.panel_name = name def setLayer(self, n): """ Allow adding plot to the same panel :param n: the number of layer """ self.layernum = n self.update() def clear(self): """ Clear the slicer and all connected events related to this slicer """ self.clear_markers() self.horizontal_lines.clear() self.vertical_lines.clear() self.center.clear() self.base.connect.clearall() def update(self): """ Respond to changes in the model by recalculating the profiles and resetting the widgets. """ # check if the center point has moved and update the figure accordingly if self.center.has_move: self.center.update() self.horizontal_lines.update(center=self.center) self.vertical_lines.update(center=self.center) # check if the horizontal lines have moved and # update the figure accordingly if self.horizontal_lines.has_move: self.horizontal_lines.update() self.vertical_lines.update(y1=self.horizontal_lines.y1, y2=self.horizontal_lines.y2, height=self.horizontal_lines.half_height) # check if the vertical lines have moved and # update the figure accordingly if self.vertical_lines.has_move: self.vertical_lines.update() self.horizontal_lines.update(x1=self.vertical_lines.x1, x2=self.vertical_lines.x2, width=self.vertical_lines.half_width) def save(self, ev): """ Remember the roughness for this layer and the next so that we can restore on Esc. """ self.horizontal_lines.save(ev) self.vertical_lines.save(ev) self.center.save(ev) def postData(self): """ Get the limits of the boxsum and compute the sum of the pixel contained in that region and the error on that sum """ # the region of the summation x_min = self.horizontal_lines.x2 x_max = self.horizontal_lines.x1 y_min = self.vertical_lines.y2 y_max = self.vertical_lines.y1 #computation of the sum and its error box = Boxavg(x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max) self.count, self.error = box(self.base.data) # Dig out number of points summed, SMK & PDB, 04/03/2013 boxtotal = Boxsum(x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max) self.total, self.totalerror, self.points = boxtotal(self.base.data) if self.update_model: self.setModelFromParams() self.draw() def moveend(self, ev): """ After a dragging motion this function is called to compute the error and the sum of pixel of a given data 2D """ # compute error an d sum of data's pixel self.postData() def restore(self): """ Restore the roughness for this layer. """ self.horizontal_lines.restore() self.vertical_lines.restore() self.center.restore() def getParams(self): """ Store a copy of values of parameters of the slicer into a dictionary. :return params: the dictionary created """ params = {} params["Width"] = numpy.fabs(self.vertical_lines.half_width) * 2 params["Height"] = numpy.fabs(self.horizontal_lines.half_height) * 2 params["center_x"] = self.center.x params["center_y"] = self.center.y params["num_points"] = self.points params["avg"] = self.count params["avg_error"] = self.error params["sum"] = self.total params["sum_error"] = self.totalerror return params def getResult(self): """ Return the result of box summation """ result = {} result["num_points"] = self.points result["avg"] = self.count result["avg_error"] = self.error result["sum"] = self.total result["sum_error"] = self.totalerror return result def setParams(self, params): """ Receive a dictionary and reset the slicer with values contained in the values of the dictionary. :param params: a dictionary containing name of slicer parameters and values the user assigned to the slicer. """ x_max = numpy.fabs(params["Width"]) / 2 y_max = numpy.fabs(params["Height"]) / 2 self.center_x = params["center_x"] self.center_y = params["center_y"] # update the slicer given values of params self.center.update(center_x=self.center_x, center_y=self.center_y) self.horizontal_lines.update(center=self.center, width=x_max, height=y_max) self.vertical_lines.update(center=self.center, width=x_max, height=y_max) # compute the new error and sum given values of params self.postData() def draw(self): """ Redraw canvas""" self.base.draw() class PointInteractor(BaseInteractor): """ Draw a point that can be dragged with the marker. this class controls the motion the center of the BoxSum """ def __init__(self, base, axes, color='black', zorder=5, center_x=0.0, center_y=0.0): BaseInteractor.__init__(self, base, axes, color=color) # Initialization the class self.markers = [] self.axes = axes # center coordinates self.x = center_x self.y = center_y # saved value of the center coordinates self.save_x = center_x self.save_y = center_y # Create a marker self.center_marker = self.axes.plot([self.x], [self.y], linestyle='', marker='s', markersize=10, color=self.color, alpha=0.6, pickradius=5, label="pick", zorder=zorder, visible=True)[0] # Draw a point self.center = self.axes.plot([self.x], [self.y], linestyle='-', marker='', color=self.color, visible=True)[0] # Flag to determine the motion this point self.has_move = False # connecting the marker to allow them to move self.connect_markers([self.center_marker]) # Update the figure self.update() def setLayer(self, n): """ Allow adding plot to the same panel @param n: the number of layer """ self.layernum = n self.update() def clear(self): """ Clear this figure and its markers """ self.clear_markers() self.center.remove() self.center_marker.remove() def update(self, center_x=None, center_y=None): """ Draw the new roughness on the graph. """ if center_x != None: self.x = center_x if center_y != None: self.y = center_y self.center_marker.set(xdata=[self.x], ydata=[self.y]) self.center.set(xdata=[self.x], ydata=[self.y]) def save(self, ev): """ Remember the roughness for this layer and the next so that we can restore on Esc. """ self.save_x = self.x self.save_y = self.y def moveend(self, ev): """ """ self.has_move = False self.base.moveend(ev) def restore(self): """ Restore the roughness for this layer. """ self.y = self.save_y self.x = self.save_x def move(self, x, y, ev): """ Process move to a new position, making sure that the move is allowed. """ self.x = x self.y = y self.has_move = True self.base.base.update() def setCursor(self, x, y): """ """ self.move(x, y, None) self.update() class VerticalDoubleLine(BaseInteractor): """ Draw 2 vertical lines moving in opposite direction and centered on a point (PointInteractor) """ def __init__(self, base, axes, color='black', zorder=5, x=0.5, y=0.5, center_x=0.0, center_y=0.0): BaseInteractor.__init__(self, base, axes, color=color) # Initialization the class self.markers = [] self.axes = axes # Center coordinates self.center_x = center_x self.center_y = center_y # defined end points vertical lignes and their saved values self.y1 = y + self.center_y self.save_y1 = self.y1 delta = self.y1 - self.center_y self.y2 = self.center_y - delta self.save_y2 = self.y2 self.x1 = x + self.center_x self.save_x1 = self.x1 delta = self.x1 - self.center_x self.x2 = self.center_x - delta self.save_x2 = self.x2 # # save the color of the line self.color = color # the height of the rectangle self.half_height = numpy.fabs(y) self.save_half_height = numpy.fabs(y) # the with of the rectangle self.half_width = numpy.fabs(self.x1 - self.x2) / 2 self.save_half_width = numpy.fabs(self.x1 - self.x2) / 2 # Create marker self.right_marker = self.axes.plot([self.x1], [0], linestyle='', marker='s', markersize=10, color=self.color, alpha=0.6, pickradius=5, label="pick", zorder=zorder, visible=True)[0] # Define the left and right lines of the rectangle self.right_line = self.axes.plot([self.x1, self.x1], [self.y1, self.y2], linestyle='-', marker='', color=self.color, visible=True)[0] self.left_line = self.axes.plot([self.x2, self.x2], [self.y1, self.y2], linestyle='-', marker='', color=self.color, visible=True)[0] # Flag to determine if the lines have moved self.has_move = False # Connection the marker and draw the pictures self.connect_markers([self.right_marker]) self.update() def setLayer(self, n): """ Allow adding plot to the same panel :param n: the number of layer """ self.layernum = n self.update() def clear(self): """ Clear this slicer and its markers """ self.clear_markers() self.right_marker.remove() self.right_line.remove() self.left_line.remove() def update(self, x1=None, x2=None, y1=None, y2=None, width=None, height=None, center=None): """ Draw the new roughness on the graph. :param x1: new maximum value of x coordinates :param x2: new minimum value of x coordinates :param y1: new maximum value of y coordinates :param y2: new minimum value of y coordinates :param width: is the width of the new rectangle :param height: is the height of the new rectangle :param center: provided x, y coordinates of the center point """ # Save the new height, witdh of the rectangle if given as a param if width is not None: self.half_width = width if height is not None: self.half_height = height # If new center coordinates are given draw the rectangle # given these value if center is not None: self.center_x = center.x self.center_y = center.y self.x1 = self.half_width + self.center_x self.x2 = -self.half_width + self.center_x self.y1 = self.half_height + self.center_y self.y2 = -self.half_height + self.center_y self.right_marker.set(xdata=[self.x1], ydata=[self.center_y]) self.right_line.set(xdata=[self.x1, self.x1], ydata=[self.y1, self.y2]) self.left_line.set(xdata=[self.x2, self.x2], ydata=[self.y1, self.y2]) return # if x1, y1, y2, y3 are given draw the rectangle with this value if x1 is not None: self.x1 = x1 if x2 is not None: self.x2 = x2 if y1 is not None: self.y1 = y1 if y2 is not None: self.y2 = y2 # Draw 2 vertical lines and a marker self.right_marker.set(xdata=[self.x1], ydata=[self.center_y]) self.right_line.set(xdata=[self.x1, self.x1], ydata=[self.y1, self.y2]) self.left_line.set(xdata=[self.x2, self.x2], ydata=[self.y1, self.y2]) def save(self, ev): """ Remember the roughness for this layer and the next so that we can restore on Esc. """ self.save_x2 = self.x2 self.save_y2 = self.y2 self.save_x1 = self.x1 self.save_y1 = self.y1 self.save_half_height = self.half_height self.save_half_width = self.half_width def moveend(self, ev): """ After a dragging motion reset the flag self.has_move to False """ self.has_move = False self.base.moveend(ev) def restore(self): """ Restore the roughness for this layer. """ self.y2 = self.save_y2 self.x2 = self.save_x2 self.y1 = self.save_y1 self.x1 = self.save_x1 self.half_height = self.save_half_height self.half_width = self.save_half_width def move(self, x, y, ev): """ Process move to a new position, making sure that the move is allowed. """ self.x1 = x delta = self.x1 - self.center_x self.x2 = self.center_x - delta self.half_width = numpy.fabs(self.x1 - self.x2) / 2 self.has_move = True self.base.base.update() def setCursor(self, x, y): """ Update the figure given x and y """ self.move(x, y, None) self.update() class HorizontalDoubleLine(BaseInteractor): """ Select an annulus through a 2D plot """ def __init__(self, base, axes, color='black', zorder=5, x=0.5, y=0.5, center_x=0.0, center_y=0.0): BaseInteractor.__init__(self, base, axes, color=color) # Initialization the class self.markers = [] self.axes = axes # Center coordinates self.center_x = center_x self.center_y = center_y self.y1 = y + self.center_y self.save_y1 = self.y1 delta = self.y1 - self.center_y self.y2 = self.center_y - delta self.save_y2 = self.y2 self.x1 = x + self.center_x self.save_x1 = self.x1 delta = self.x1 - self.center_x self.x2 = self.center_x - delta self.save_x2 = self.x2 self.color = color self.half_height = numpy.fabs(y) self.save_half_height = numpy.fabs(y) self.half_width = numpy.fabs(x) self.save_half_width = numpy.fabs(x) self.top_marker = self.axes.plot([0], [self.y1], linestyle='', marker='s', markersize=10, color=self.color, alpha=0.6, pickradius=5, label="pick", zorder=zorder, visible=True)[0] # Define 2 horizotnal lines self.top_line = self.axes.plot([self.x1, -self.x1], [self.y1, self.y1], linestyle='-', marker='', color=self.color, visible=True)[0] self.bottom_line = self.axes.plot([self.x1, -self.x1], [self.y2, self.y2], linestyle='-', marker='', color=self.color, visible=True)[0] # Flag to determine if the lines have moved self.has_move = False # connection the marker and draw the pictures self.connect_markers([self.top_marker]) self.update() def setLayer(self, n): """ Allow adding plot to the same panel @param n: the number of layer """ self.layernum = n self.update() def clear(self): """ Clear this figure and its markers """ self.clear_markers() self.top_marker.remove() self.bottom_line.remove() self.top_line.remove() def update(self, x1=None, x2=None, y1=None, y2=None, width=None, height=None, center=None): """ Draw the new roughness on the graph. :param x1: new maximum value of x coordinates :param x2: new minimum value of x coordinates :param y1: new maximum value of y coordinates :param y2: new minimum value of y coordinates :param width: is the width of the new rectangle :param height: is the height of the new rectangle :param center: provided x, y coordinates of the center point """ # Save the new height, witdh of the rectangle if given as a param if width is not None: self.half_width = width if height is not None: self.half_height = height # If new center coordinates are given draw the rectangle # given these value if center is not None: self.center_x = center.x self.center_y = center.y self.x1 = self.half_width + self.center_x self.x2 = -self.half_width + self.center_x self.y1 = self.half_height + self.center_y self.y2 = -self.half_height + self.center_y self.top_marker.set(xdata=[self.center_x], ydata=[self.y1]) self.top_line.set(xdata=[self.x1, self.x2], ydata=[self.y1, self.y1]) self.bottom_line.set(xdata=[self.x1, self.x2], ydata=[self.y2, self.y2]) return # if x1, y1, y2, y3 are given draw the rectangle with this value if x1 is not None: self.x1 = x1 if x2 is not None: self.x2 = x2 if y1 is not None: self.y1 = y1 if y2 is not None: self.y2 = y2 # Draw 2 vertical lines and a marker self.top_marker.set(xdata=[self.center_x], ydata=[self.y1]) self.top_line.set(xdata=[self.x1, self.x2], ydata=[self.y1, self.y1]) self.bottom_line.set(xdata=[self.x1, self.x2], ydata=[self.y2, self.y2]) def save(self, ev): """ Remember the roughness for this layer and the next so that we can restore on Esc. """ self.save_x2 = self.x2 self.save_y2 = self.y2 self.save_x1 = self.x1 self.save_y1 = self.y1 self.save_half_height = self.half_height self.save_half_width = self.half_width def moveend(self, ev): """ After a dragging motion reset the flag self.has_move to False """ self.has_move = False self.base.moveend(ev) def restore(self): """ Restore the roughness for this layer. """ self.y2 = self.save_y2 self.x2 = self.save_x2 self.y1 = self.save_y1 self.x1 = self.save_x1 self.half_height = self.save_half_height self.half_width = self.save_half_width def move(self, x, y, ev): """ Process move to a new position, making sure that the move is allowed. """ self.y1 = y delta = self.y1 - self.center_y self.y2 = self.center_y - delta self.half_height = numpy.fabs(self.y1) - self.center_y self.has_move = True self.base.base.update() def setCursor(self, x, y): """ Update the figure given x and y """ self.move(x, y, None) self.update()