class PolygonSelection(HasStrictTraits): """Plots, and lets the user interact with, a 2D selection. Attributes ---------- polygon : Instance(numpy.ndarray) The polygon vertices view : Instance(IView) the IView that this view is wrapping. I suggest that if it's another ISelectionView, that its `interactive` property remain False. >.> interactive : Bool is this view interactive? Ie, can the user set the polygon verticies with mouse clicks? """ id = "edu.mit.synbio.cytoflow.views.polygon" friendly_id = "Polygon Selection" view = Instance(IView, transient = True) interactive = Bool(False, transient = True) vertices = List((Float, Float)) # internal state. _cursor = Instance(Cursor, transient = True) _path = Instance(mpl.path.Path, transient = True) _patch = Instance(mpl.patches.PathPatch, transient = True) _line = Instance(mpl.lines.Line2D, transient = True) _drawing = Bool(transient = True) def plot(self, experiment, **kwargs): """Plot self.view, and then plot the selection on top of it.""" self.view.plot(experiment, **kwargs) self._draw_poly() def is_valid(self, experiment): """If the decorated view is valid, we are too.""" return self.view.is_valid(experiment) @on_trait_change('vertices') def _draw_poly(self): ca = plt.gca() if self._patch and self._patch in ca.patches: self._patch.remove() if self._drawing or not self.vertices or len(self.vertices) < 3 \ or any([len(x) != 2 for x in self.vertices]): return patch_vert = np.concatenate((np.array(self.vertices), np.array((0,0), ndmin = 2))) self._patch = \ mpl.patches.PathPatch(mpl.path.Path(patch_vert, closed = True), edgecolor="black", linewidth = 1.5, fill = False) ca.add_patch(self._patch) plt.gcf().canvas.draw() @on_trait_change('interactive') def _interactive(self): if self.interactive: ax = plt.gca() self._cursor = Cursor(ax, horizOn = False, vertOn = False) self._cursor.connect_event('button_press_event', self._onclick) self._cursor.connect_event('motion_notify_event', self._onmove) else: self._cursor.disconnect_events() self._cursor = None def _onclick(self, event): """Update selection traits""" if(self._cursor.ignore(event)): return if event.dblclick: self._drawing = False self.vertices = map(tuple, self._path.vertices) self._path = None return ca = plt.gca() self._drawing = True if self._patch and self._patch in ca.patches: self._patch.remove() if self._path: vertices = np.concatenate((self._path.vertices, np.array((event.xdata, event.ydata), ndmin = 2))) else: vertices = np.array((event.xdata, event.ydata), ndmin = 2) self._path = mpl.path.Path(vertices, closed = False) self._patch = mpl.patches.PathPatch(self._path, edgecolor = "black", fill = False) ca.add_patch(self._patch) plt.gcf().canvas.draw() def _onmove(self, event): if(self._cursor.ignore(event) or not self._drawing or not self._path or self._path.vertices.shape[0] == 0 or not event.xdata or not event.ydata): return ca = plt.gca() if not ca: return if self._line and self._line in ca.lines: self._line.remove() xdata = [self._path.vertices[-1, 0], event.xdata] ydata = [self._path.vertices[-1, 1], event.ydata] self._line = mpl.lines.Line2D(xdata, ydata, linewidth = 1, color = "black") ca.add_line(self._line) plt.gcf().canvas.draw()
class QuadSelection(cytoflow.views.ScatterplotView): """Plots, and lets the user interact with, a quadrant gate. Attributes ---------- op : Instance(Range2DOp) The instance of Range2DOp that we're viewing / editing huefacet : Str The conditioning variable to plot multiple colors subset : Str The string passed to `Experiment.query()` to subset the data before plotting interactive : Bool is this view interactive? Ie, can the user set the threshold with a mouse click? Notes ----- We inherit `xfacet` and `yfacet` from `cytoflow.views.ScatterplotView`, but they must both be unset! Examples -------- In an IPython notebook with `%matplotlib notebook` >>> q = flow.QuadOp(name = "Quad", ... xchannel = "V2-A", ... ychannel = "Y2-A")) >>> qv = q.default_view() >>> qv.interactive = True >>> qv.plot(ex2) """ id = Constant('edu.mit.synbio.cytoflow.views.quad') friendly_id = Constant("Quadrant Selection") op = Instance(IOperation) name = DelegatesTo('op') xchannel = DelegatesTo('op') ychannel = DelegatesTo('op') interactive = Bool(False, transient = True) # internal state. _ax = Any(transient = True) _hline = Instance(Line2D, transient = True) _vline = Instance(Line2D, transient = True) _cursor = Instance(Cursor, transient = True) def plot(self, experiment, **kwargs): """Plot the underlying scatterplot and then plot the selection on top of it.""" if not experiment: raise util.CytoflowOpError("No experiment specified") if not experiment: raise util.CytoflowViewError("No experiment specified") if self.xfacet: raise util.CytoflowViewError("RangeSelection.xfacet must be empty or `Undefined`") if self.yfacet: raise util.CytoflowViewError("RangeSelection.yfacet must be empty or `Undefined`") super(QuadSelection, self).plot(experiment, **kwargs) self._ax = plt.gca() self._draw_lines() self._interactive() @on_trait_change('op.xthreshold, op.ythreshold', post_init = True) def _draw_lines(self): if not self._ax: return if self._hline and self._hline in self._ax.lines: self._hline.remove() if self._vline and self._vline in self._ax.lines: self._vline.remove() if self.op.xthreshold and self.op.ythreshold: self._hline = plt.axhline(self.op.ythreshold, linewidth = 3, color = 'blue') self._vline = plt.axvline(self.op.xthreshold, linewidth = 3, color = 'blue') plt.draw_if_interactive() @on_trait_change('interactive', post_init = True) def _interactive(self): if self._ax and self.interactive: self._cursor = Cursor(self._ax, horizOn = True, vertOn = True, color = 'blue') self._cursor.connect_event('button_press_event', self._onclick) elif self._cursor: self._cursor.disconnect_events() self._cursor = None def _onclick(self, event): """Update the threshold location""" self.op.xthreshold = event.xdata self.op.ythreshold = event.ydata
class ECE4012: def __init__(self, figure, filename, toolbar): self.dfCal = None self.fig = figure self.toolbar = toolbar self.filename = filename self.rect = None # Function used to setup graph after class is created or after new data is loaded def initializer(self, type): self.fig.clear() print("clear figure") self.fig.subplots_adjust(hspace=1.0) self.annoteText = "annotated" self.isAnnotate = False self.fig.canvas.mpl_connect('button_release_event', self.onrelease) self.fig.canvas.mpl_connect('button_press_event', self.onclick) self.fig.canvas.mpl_connect('key_press_event', self.onpress) self.fig.canvas.mpl_connect('pick_event', self.onpick) if type == "DB": self.searchOpenCalibration(self.filename) self.conn = self.getConnection(self.filename) t = time.time() df = pd.read_sql_query("select * from acc;", self.conn) elapsed = time.time() - t print("time taken to read data was {:f}".format(elapsed)) elif type == "CSV": print("CSV getting Calibration") self.searchOpenCalibration(self.filename) print("CSV opening file") df = pd.read_csv(self.filename) print("CSV opened file") print("Starting") # Apply calibration if calibration data is loaded if self.dfCal is not None: df["valuex"] = np.subtract(df["valuex"], self.dfCal['Values'][ self.XOffset]) / self.dfCal['Values'][self.XGain] df["valuey"] = np.subtract(df["valuey"], self.dfCal['Values'][ self.YOffset]) / self.dfCal['Values'][self.YGain] df["valuez"] = np.subtract(df["valuez"], self.dfCal['Values'][ self.ZOffset]) / self.dfCal['Values'][self.ZGain] self.fig.subplots_adjust(bottom=0.3, left=0.2, hspace=1.0) # setup some constants to be used in Scale Selection panel self.labels = ['30 sec', '5 min', '30 min', '1 hr', '2 hr'] sec30 = pd.Timedelta(30, unit='s') min5 = pd.Timedelta(5, unit='m') min30 = pd.Timedelta(30, unit='m') hr1 = pd.Timedelta(1, unit='h') hr2 = pd.Timedelta(2, unit='h') # Scale Selection panel made from Radio Buttons self.scaleDeltaArray = [sec30, min5, min30, hr1, hr2] self.rax = self.fig.add_axes([0.02, 0.7, 0.11, 0.15]) self.rax.set_title('Scale') self.check = RadioButtons(self.rax, self.labels) self.fig.canvas.mpl_connect('button_press_event', self.onclick) self.check.on_clicked(self.changeScale) # Data Set Selection Check Box self.rcbax = self.fig.add_axes([0.02, 0.25, 0.11, 0.15]) self.rcbax.set_title('DataSet') self.checkBox = CheckButtons(self.rcbax, ('valuex', 'valuey', 'valuez', 'mag'), (True, True, True, True)) self.checkBox.on_clicked(self.oncheck) xs = np.linspace(15, 21, 200) horiz_line_dataz = np.array([0.41 for i in range(len(xs))]) self.rcbax.plot(xs, horiz_line_dataz, 'C3-') horiz_line_datam = np.array([0.26 for i in range(len(xs))]) self.rcbax.plot(xs, horiz_line_datam, 'C0-') horiz_line_datay = np.array([0.58 for i in range(len(xs))]) self.rcbax.plot(xs, horiz_line_datay, 'C2-') horiz_line_datax = np.array([0.73 for i in range(len(xs))]) self.rcbax.plot(xs, horiz_line_datax, 'C1-') # Create magnitude value of acceleration data in a dataframe and substract 1g to eliminate gravity df["mag"] = np.sqrt(np.square(df["valuex"]) + np.square(df['valuey']) + \ np.square(df["valuez"]))-1.0 print("Normalize") ya_min = df.loc[:, "mag"].min() - 0.1 ya_max = df.loc[:, "mag"].max() + 0.1 print("y limit (max={:f},min={:f})".format(ya_max, ya_min)) print("Convert Date Time") print(df["epoch"].iloc[0]) print(df["epoch"].iloc[1]) # Convert epoch to datetime if type == "DB": print("epoch type", df.dtypes[0]) df['epoch'] = pd.to_datetime(df['epoch'], unit='ms') elif type == "CSV": print("epoch type", df.dtypes[0]) if 'annotated' not in df.columns: df['epoch'] = pd.to_datetime(df['epoch'], unit='ms') else: df['epoch'] = pd.to_datetime(df['epoch']) print("epoch type after", df.dtypes[0]) # Adjusting epoch time to be in Eastern Time Zone df['epoch'] = df['epoch'].dt.tz_localize('UTC').dt.tz_convert( 'America/New_York') # Create external numpy array to manage timestamp data to be sure that data will be datetime vepoch = df['epoch'] self.datevalues = vepoch.dt.to_pydatetime() # Create annotated and colorHex columns in the dataframe for capturing annotation if 'annotated' not in df.columns: charArr = np.chararray((len(df["mag"], )), unicode=True) charArr[:] = ' ' df["annotated"] = pd.DataFrame(charArr) df["colorHex"] = pd.DataFrame(charArr) # Define format for graph ticks self.hoursFmt = mdates.DateFormatter('%a-%m-%d %H:%M:%S') self.HMSFmt = mdates.DateFormatter('%H:%M:%S') # Get total time of the data in hours startDate = df["epoch"].iloc[0] self.startDate = startDate endDate = df["epoch"].iloc[-1] totaltime = int(endDate.timestamp() * 1000) - \ int(startDate.timestamp() * 1000) total_hours = totaltime / 1000 / 3600 #totaltime in hours total_minutes = total_hours * 60 print("total hours {:f}".format(total_hours)) # Create variable to track drag time self.timeTrack = 0 # Create variable to track initial click xpos self.xpos = 0 # Create class attribute with the data to be used in the rest of class functions self.df = df print("Creating Top Graph") # First axis representing Top Graph self.ax = self.fig.add_subplot(2, 1, 1) # Second axis representing Bottom Graph print("Creating Bottom Graph") self.ax2 = self.fig.add_subplot(2, 1, 2) # Adjust distance between toolbar and graph to give more space self.fig.subplots_adjust(bottom=0.145) # Adjust distance between toolbar and graph if data is 24 hours or more if total_hours > 24: self.fig.subplots_adjust(hspace=1.3) print("Start Plot Setting") # Set axis titles to graphs self.ax.set_xlabel("Time") self.ax2.set_xlabel("Time") self.ax.set_ylabel("Acceleration (g)") self.ax2.set_ylabel("Acceleration (g)") # Adjust y-axis range to maximum and minimum of magnitude self.ax.set_ylim(ya_min, ya_max) self.ax2.set_ylim(ya_min, ya_max) # Set title with word Calibrated if calibration was applied bottomtitle = 'Magnitude Graph for Start Date ' + "{:%Y-%m-%d}".format( startDate) if self.dfCal is not None: bottomtitle = 'Calibrated ' + bottomtitle self.ax.set_title(bottomtitle) toptitle = 'Zoomed-In Graph for Start Date ' + "{:%Y-%m-%d}".format( startDate) if self.dfCal is not None: toptitle = 'Calibrated ' + toptitle self.ax2.set_title(toptitle) # Prepare x-axis range to display initially in bottom graph start_time = df["epoch"].iloc[0] print("start time={v}".format(v=start_time)) # If data loaded is longer than 1 hour, GUI will show the first hour on bottom graph if total_hours > 1: end_time = start_time + hr1 elif total_minutes > 30: end_time = start_time + min30 elif total_minutes > 5: end_time = start_time + min5 else: end_time = start_time + sec30 print("end time={v}".format(v=end_time)) self.startX = start_time self.endX = end_time # Set x-axis limit with previous selection self.ax2.set_xlim(start_time, end_time) print("Set Limit") # Create data mask with informaton being displayed mask = (df["epoch"] >= start_time) & (df["epoch"] <= end_time) print("Plotting") # Plot top graph t = time.time() # To increase speed to graph, only plot first 4 points and then # substitute the data with function set_data, this saves several seconds of load time self.plotTop, = self.ax.plot(df["epoch"].iloc[0:3], df["mag"].iloc[0:3], visible=True) self.plotTop.set_data(self.datevalues, df["mag"]) self.ax.relim() self.ax.autoscale_view(True, True, True) self.fig.canvas.draw() elapsed = time.time() - t print("time taken to top graph magnitude was {:f}".format(elapsed)) print(len(df.loc[mask, "epoch"])) # Plot bottom graph t = time.time() # Initially graphs only magnitude # To increase speed to graph, only plot first 4 points and then # substitute the data with function set_data, this saves several seconds of load time self.lm, = self.ax2.plot(df["epoch"].iloc[0:3], df["mag"].iloc[0:3], visible=True) self.lm.set_data(self.datevalues, df["mag"]) self.ax2.relim() self.ax2.autoscale_view(True, True, True) self.fig.canvas.draw() elapsed = time.time() - t print("time taken to bottom graph magnitude was {:f}".format(elapsed)) t = time.time() # Create thread to plot x value in the bottom graph in background mode # to avoid startup delay try: thread1 = myThread(1, "valuex", self) thread1.start() except: print("Error: unable to start thread") elapsed = time.time() - t print("time taken to bottom graph valuex was {:f}".format(elapsed)) t = time.time() # Create thread to plot y value in the bottom graph in background mode # to avoid startup delay try: thread2 = myThread(2, "valuey", self) thread2.start() except: print("Error: unable to start thread") elapsed = time.time() - t print("time taken to bottom graph valuey was {:f}".format(elapsed)) t = time.time() # Create thread to plot z value in the bottom graph in background mode # to avoid startup delay try: thread3 = myThread(3, "valuez", self) thread3.start() except: print("Error: unable to start thread") elapsed = time.time() - t print("time taken to bottom graph valuez was {:f}".format(elapsed)) # x in the range tX = df.loc[mask, "epoch"] self.ax2.set_xlim(tX.iloc[0], tX.iloc[len(tX) - 1]) # Set tick format and number on top graph self.ax.xaxis.set_major_formatter(self.HMSFmt) self.ax.get_xaxis().set_major_locator(LinearLocator(numticks=12)) # Setup format of ticks to Weekday month/day if data is 24 hours or more if total_hours > 24: self.ax.xaxis.set_major_formatter(self.hoursFmt) # Rotate labels on x-axis 45 degrees and set font size to 8 self.ax.tick_params(axis='x', labelsize=8, rotation=45) # Select initial random color for random annotation self.color = self.colorChoose(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) self.removedObj = None # Refresh the graph self.fig.canvas.draw() # Prepare graph to display red cursor on top graph self.fig.canvas.mpl_connect('motion_notify_event', self.onmouseover) self.cursor = Cursor(self.ax, useblit=True, horizOn=False, color='red', linewidth=2) self.cursor.connect_event('button_press_event', self.cursorOnclick) # Setup initial value for the scale to 30 seconds self.currentDelta = pd.Timedelta(30, unit='s') # Set tick format and number on bottom graph self.ax2.xaxis.set_major_formatter(self.HMSFmt) self.ax2.get_xaxis().set_major_locator(LinearLocator(numticks=7)) # Set function to format coordinates to be displayed on bottom right corner self.ax.format_coord = self.format_coord # Call function to search for annotation in the data # if it was loaded from CSV file and graph them if type == "CSV" and 'annotated' in df.columns: print("calling searchForAnnotation") self.searchForAnnotation() # Function that derives values to be shown on bottom right corner. # It will display Timestamp and magnitude of where cursor is located def format_coord(self, x, y): dateclickOn = mdates.num2date(x) idx = self.df['epoch'].searchsorted(dateclickOn) idx = idx - 1 vx = self.df['valuex'].values[idx][0] vy = self.df['valuey'].values[idx][0] vz = self.df['valuez'].values[idx][0] vm = self.df['mag'].values[idx][0] return "{:%H:%M:%S} M:{:1.3f}".format(dateclickOn, vm) # Function to read calibrationResultsXXXXXXXXXXXX.csv # to obtain offset and gain values of x,y, and z-axis # from MetaWear device def readCalibration(self, filename): self.dfCal = pd.read_csv(filename) print('Results: %s', self.dfCal) print('title: %s', self.dfCal['Titles']) print('values: %s', self.dfCal['Values']) print('ZGain: %s', self.dfCal['Values'][1]) self.ZOffset = 0 self.ZGain = 1 self.YOffset = 2 self.YGain = 3 self.XOffset = 4 self.XGain = 5 # Function used to redraw top and bottom graphs # with calibrated values applied to x, y, and z values def redrawAfterCalibrate(self): print("Computing new data") #Computes new x, y, and z values with calibration if self.dfCal is not None: self.df["valuex"] = np.subtract( self.df["valuex"], self.dfCal['Values'][ self.XOffset]) / self.dfCal['Values'][self.XGain] self.df["valuey"] = np.subtract( self.df["valuey"], self.dfCal['Values'][ self.YOffset]) / self.dfCal['Values'][self.YGain] self.df["valuez"] = np.subtract( self.df["valuez"], self.dfCal['Values'][ self.ZOffset]) / self.dfCal['Values'][self.ZGain] print("Computing new mag") self.df["mag"] = np.sqrt(np.square(self.df["valuex"]) + np.square(self.df['valuey']) + \ np.square(self.df["valuez"]))-1.0 print("setting titles") # Bottom Title changed to say Calibrated bottomtitle = 'Magnitude Graph for Start Date ' + "{:%Y-%m-%d}".format( self.startDate) if self.dfCal is not None: bottomtitle = 'Calibrated ' + bottomtitle self.ax.set_title(bottomtitle) # Top Title changed to say Calibrated toptitle = 'Zoomed-In Graph for Start Date ' + "{:%Y-%m-%d}".format( self.startDate) if self.dfCal is not None: toptitle = 'Calibrated ' + toptitle self.ax2.set_title(toptitle) print("redraw graph") # Refreshes graphs with new data values self.plotTop.set_ydata(self.df["mag"]) self.lm.set_ydata(self.df["mag"]) self.lx.set_ydata(self.df["valuex"]) self.ly.set_ydata(self.df["valuey"]) self.lz.set_ydata(self.df["valuez"]) self.fig.canvas.draw() # Function used to determine where mouse event occurs def onmouseover(self, event): if event.inaxes == self.rax: None if event.inaxes == self.ax: self.cursor.onmove(event) # Function used to determine if calibration file is found in same directory # as GUI application def searchOpenCalibration(self, filename): directory = os.path.dirname(filename) print("directory=" + directory) tosearch = re.compile('^calibrationResults.*') for sChild in os.listdir(directory): print("file=" + sChild) if tosearch.match(sChild) is not None: if directory == "": directory = "." calibfilename = directory + "/" + sChild print("calibfilename=" + calibfilename) self.readCalibration(calibfilename) break # Function used to determine if annotations have been applied to # recently opened csv file def searchForAnnotation(self): colorKick = self.colorChoose(255, 0, 0) colorSleep = self.colorChoose(0, 0, 255) colorRandom = self.colorChoose(0, 255, 0) randomColor = self.color maskKick = self.df['annotated'] == "Kick" maskSleep = self.df['annotated'] == "Sleep" maskRandom = self.df['annotated'] == "Random" maskColorRandom = self.df['annotated'] == "annotated" print("masked found") # Creates mask of locations of annotations # Calls reDrawAnnotation to replot rectangles on graphs if len(self.df.loc[maskKick, 'epoch']) > 0: self.reDrawAnnotation(maskKick, colorKick) if len(self.df.loc[maskSleep, 'epoch']) > 0: self.reDrawAnnotation(maskSleep, colorSleep) if len(self.df.loc[maskRandom, 'epoch']) > 0: self.reDrawAnnotation(maskRandom, colorRandom) if len(self.df.loc[maskColorRandom, 'epoch']) > 0: self.reDrawAnnotation(maskColorRandom, randomColor) # Function used to redraw all annotations seen when # opening csv file that has annotations def reDrawAnnotation(self, mask, color): [ymin, ymax] = self.ax.get_ylim() height = ymax - ymin # Determines index positions of annotations positionKick = np.array(self.df.index[mask].tolist()) # Determines where gaps between annotations occur diff = np.subtract(positionKick[1:-1], positionKick[0:-2]) maskcuts = diff > 1 posInCuts = np.where(maskcuts) arrposcut = posInCuts[0] arrpostcut1 = arrposcut + 1 # Initializes first seen annotation xmin = self.df.loc[mask, 'epoch'].iloc[0] xmax = self.df.loc[mask, 'epoch'].iloc[-1] xmin = xmin.to_pydatetime() xmax = xmax.to_pydatetime() # converts values to proper time format print("xmin={v1} xmax={v2}".format(v1=xmin, v2=xmax)) xmin = mdates.date2num(xmin) xmax = mdates.date2num(xmax) print("len(posInCuts)", len(posInCuts[0])) print("xmin={v1} xmax={v2}".format(v1=xmin, v2=xmax)) # For loop used to run through and create rectangles to plot for annotations for idx in range(len(posInCuts[0])): print("idx=", idx) print("xmin={v1} xmax={v2}".format( v1=xmin, v2=self.df['epoch'].iloc[positionKick[arrposcut[idx]]])) rectmin = xmin rectmax = self.df['epoch'].iloc[positionKick[arrposcut[idx]]] rectmax = rectmax.to_pydatetime() rectmax = mdates.date2num(rectmax) width = rectmax - rectmin rect = ECERectangle(rectmin, ymin, width, height, color=color, index=1) rect2 = ECERectangle(xmin, ymin, width, height, color=color, index=2) rect.setNext(rect2) rect2.setPrev(rect) self.ax.add_patch(rect) self.ax2.add_patch(rect2) self.fig.canvas.draw() xmin = self.df['epoch'].iloc[positionKick[arrpostcut1[idx]]] xmin = xmin.to_pydatetime() xmin = mdates.date2num(xmin) rectmin = xmin rectmax = xmax width = rectmax - rectmin rect = ECERectangle(rectmin, ymin, width, height, color=color, index=1) rect2 = ECERectangle(xmin, ymin, width, height, color=color, index=2) rect.setNext(rect2) rect2.setPrev(rect) self.ax.add_patch(rect) self.ax2.add_patch(rect2) print("xmin={v1} xmax={v2}".format(v1=xmin, v2=xmax)) self.fig.canvas.draw() # Sets up sqlite3 connection to database file def getConnection(self, filename): """ get the connection of sqlite given file name :param filename: :return: sqlite3 connection """ conn = sqlite3.connect(filename) return conn # Old Function used to read calibration file. No longer relevant def readCalibration(self, filename): self.dfCal = pd.read_csv(filename) print('Results: %s', self.dfCal) print('title: %s', self.dfCal['Titles']) print('values: %s', self.dfCal['Values']) print('ZGain: %s', self.dfCal['Values'][1]) self.ZOffset = 0 self.ZGain = 1 self.YOffset = 2 self.YGain = 3 self.XOffset = 4 self.XGain = 5 # Function used to create calibration button. No longer relevant def createCalibrationAction(self): image_file = 'square-upload' callback = self.toolbar.importCal text = "Calibrate" tooltip_text = "Calibrate" a = self.toolbar.addAction(self.toolbar._icon(image_file + '.png'), text, callback) print("action created") self.toolbar._actions[callback] = a print("action added to array") a.setToolTip(tooltip_text) # Function to execute query def executeQuery(self, query): return None # Function used to set range of colors def colorChoose(self, r, g, b): """ create a normalized color map give r, g, b in 0-255 scale :param r: red :param g: green :param b: blue :return: tuple of color that can be used for matplotlib """ if self.rangeOff(r) or self.rangeOff(g) or self.rangeOff(r): return (0, 0, 0) return r / 255.0, g / 255.0, b / 255.0 # Function to set range off def rangeOff(self, val): return (val > 255) or (val < 0) # Function to determine event of creating annotations def onpick(self, event): print("pick event") if isinstance(event.artist, Rectangle): patch = event.artist # patch.remove() self.removedObj = patch print('onpick1 patch:', patch.get_path()) else: print(event.artist.get_xdata()) print(event.artist.get_xdata()) print(len(event.artist.get_ydata())) # Function used to check if annotation is selected def onclick(self, event): if not self.toolbar._actions['zoom'].isChecked( ) and event.button == 1: # left = 1, middle = 2, right = 3 self.timeTrack = time.time() print("clicked {}".format(self.timeTrack)) print("clicked X: {}".format(event.xdata)) self.xpos = event.xdata # Function used to determine when annotation event occurs def onrelease(self, event): if not self.toolbar._actions['zoom'].isChecked() \ and event.button == 1 and self.isAnnotate: curTime = time.time() if (curTime - self.timeTrack) > PRE_DEF_CLICK_TIME: print("dragged") xmin = self.xpos xmax = event.xdata width = xmax - xmin [ymin, ymax] = self.ax.get_ylim() height = ymax - ymin self.create_annotation(xmin, xmax) rect = ECERectangle(xmin, ymin, width, height, color=self.color, index=1) rect2 = ECERectangle(xmin, ymin, width, height, color=self.color, index=2) rect.setNext(rect2) rect2.setPrev(rect) self.ax.add_patch(rect) self.ax2.add_patch(rect2) self.fig.canvas.draw() # Key commands running from keyboard def onpress(self, event): # print(event.key()) print(event.key) if event.key == 'r': self.annoteText = "Kick" self.color = self.colorChoose(255, 0, 0) elif event.key == 'b': self.annoteText = "Sleep" self.color = self.colorChoose(0, 0, 255) elif event.key == 'g': self.annoteText = "Random" self.color = self.colorChoose(0, 255, 0) elif event.key == "right": print("right pressed") self.startX += self.currentDelta self.endX += self.currentDelta self.ax2.set_xlim(self.startX, self.endX) self.run() elif event.key == "left": # TODO check left and right limit print("left pressed") self.startX -= self.currentDelta self.endX -= self.currentDelta self.ax2.set_xlim(self.startX, self.endX) self.run() elif event.key == "delete": print("delete") if self.removedObj is not None: if isinstance(self.removedObj, ECERectangle): print("deleting ECERect") rect_min_x = self.removedObj.get_x() rect_max_x = rect_min_x + self.removedObj.get_width() self.remove_annotation(rect_min_x, rect_max_x) ind = self.removedObj.getIndex() self.removedObj.remove() if ind == 1: nextRect = self.removedObj.getNext() else: nextRect = self.removedObj.getPrev() self.removedObj = None if nextRect.axes is not None: nextRect.remove() self.fig.canvas.draw() # Redraws graphs def run(self): self.fig.canvas.draw() # Creates annotation to dataframe def create_annotation(self, xmin, xmax): print("Annoated") cond = self.get_x_in_ranges(xmin, xmax) self.df.loc[cond, 'annotated'] = self.annoteText self.df.loc[cond, 'colorHex'] = self.get_color_in_hex() # Deletes annotation labels def remove_annotation(self, xmin, xmax): cond = self.get_x_in_ranges(xmin, xmax) self.df.loc[cond, 'annotated'] = " " self.df.loc[cond, 'colorHex'] = " " # Obtain mask of range of x values def get_x_in_ranges(self, xmin, xmax): return (self.df["epoch"] <= mdates.num2date(xmax)) \ & (self.df["epoch"] >= mdates.num2date(xmin)) # Get hexdecimal color def get_color_in_hex(self): return "#{:02X}{:02X}{:02X}".format(int(self.color[0] * 255), int(self.color[1] * 255), int(self.color[2] * 255)) # Unselect annotation when not active def setSelect(self): self.isAnnotate = not self.isAnnotate # Function used to determine what click and hold effect does for annotations def cursorOnclick(self, event): 'on button press we will see if the mouse is over us ' if (self.ax == event.inaxes) and (not self.isAnnotate): xdata = event.xdata dateclicked = mdates.num2date(xdata) self.startX = dateclicked dateEnd = dateclicked + self.currentDelta self.endX = dateEnd mask = (self.df["epoch"] >= dateclicked) & (self.df["epoch"] <= dateEnd) tX = self.df.loc[mask, "epoch"] self.ax2.set_xlim(tX.iloc[0], tX.iloc[len(tX) - 1]) self.ax2.get_xaxis().set_major_locator(LinearLocator(numticks=12)) self.ax2.autoscale_view(True, True, True) width = mdates.date2num(dateEnd) - xdata [ymin, ymax] = self.ax.get_ylim() height = ymax - ymin if self.rect is not None: self.rect.remove() self.rect = Rectangle((xdata, ymin), width, height, fill=True, alpha=0.4, color=(1, 0, 0), picker=False) self.ax.add_patch(self.rect) self.fig.canvas.draw() # Sets checks on visibility of x,y, and z -axis plots def oncheck(self, label): if label == 'valuex': self.lx.set_visible(not self.lx.get_visible()) elif label == 'valuey': self.ly.set_visible(not self.ly.get_visible()) elif label == 'valuez': self.lz.set_visible(not self.lz.get_visible()) elif label == 'mag': self.lm.set_visible(not self.lm.get_visible()) self.fig.canvas.draw() # Adjust time scale values to plot on bottom graph def changeScale(self, label): index = self.labels.index(label) self.currentDelta = self.scaleDeltaArray[index]
class ThresholdSelection(cytoflow.views.HistogramView): """ Plots, and lets the user interact with, a threshold on the X axis. TODO - beautify! Attributes ---------- op : Instance(ThresholdOp) the ThresholdOp we're working on. huefacet : Str The conditioning variable to show multiple colors on this plot subset : Str the string passed to Experiment.subset() defining the subset we plot interactive : Bool is this view interactive? Notes ----- We inherit `xfacet` and `yfacet` from `cytoflow.views.HistogramView`, but they must both be unset! Examples -------- In an IPython notebook with `%matplotlib notebook` >>> t = flow.ThresholdOp(name = "Threshold", ... channel = "Y2-A") >>> tv = t.default_view() >>> tv.plot(ex2) >>> tv.interactive = True >>> # .... draw a threshold on the plot >>> ex3 = thresh.apply(ex2) """ id = Constant('edu.mit.synbio.cytoflow.views.threshold') friendly_id = Constant("Threshold Selection") op = Instance(IOperation) name = DelegatesTo('op') channel = DelegatesTo('op') interactive = Bool(False, transient = True) # internal state _ax = Any(transient = True) _line = Instance(Line2D, transient = True) _cursor = Instance(Cursor, transient = True) def plot(self, experiment, **kwargs): """Plot the histogram and then plot the threshold on top of it.""" if not experiment: raise util.CytoflowViewError("No experiment specified") if self.xfacet: raise util.CytoflowViewError("ThresholdSelection.xfacet must be empty") if self.yfacet: raise util.CytoflowViewError("ThresholdSelection.yfacet must be empty") super(ThresholdSelection, self).plot(experiment, **kwargs) self._ax = plt.gca() self._draw_threshold() self._interactive() @on_trait_change('op.threshold', post_init = True) def _draw_threshold(self): if not self._ax or not self.op.threshold: return if self._line: # when used in the GUI, _draw_threshold gets called *twice* without # the plot being updated inbetween: and then the line can't be # removed from the plot, because it was never added. so check # explicitly first. this is likely to be an issue in other # interactive plots, too. if self._line and self._line in self._ax.lines: self._line.remove() self._line = None if self.op.threshold: self._line = plt.axvline(self.op.threshold, linewidth=3, color='blue') plt.draw_if_interactive() @on_trait_change('interactive', post_init = True) def _interactive(self): if self._ax and self.interactive: self._cursor = Cursor(self._ax, horizOn=False, vertOn=True, color='blue') self._cursor.connect_event('button_press_event', self._onclick) elif self._cursor: self._cursor.disconnect_events() self._cursor = None def _onclick(self, event): """Update the threshold location""" self.op.threshold = event.xdata
class FCGateManager(EventGenerator): """Manages gate creation widgets and gates.""" def __init__(self, ax, callback_list=None): self.gates = [] self.fig = ax.figure self.ax = ax self._plt_data = None self.active_gate = None self.sample = None self.canvas = self.fig.canvas self.key_handler_cid = self.canvas.mpl_connect( 'key_press_event', lambda event: key_press_handler(event, self.canvas, self)) self.pick_event_cid = self.canvas.mpl_connect('pick_event', self.pick_event_handler) self.gate_num = 1 self.current_channels = 'd1', 'd2' self.add_callback(callback_list) def disconnect_events(self): self.canvas.mpl_disconnect(self.key_handler_cid) self.canvas.mpl_disconnect(self.pick_event_cid) def pick_event_handler(self, event): """ Handles pick events """ info = { 'options': self.get_available_channels(), 'guiEvent': event.mouseevent.guiEvent, } if hasattr(self, 'xlabel_artist') and (event.artist == self.xlabel_artist): info['axis_num'] = 0 self.callback(Event('axis_click', info)) if hasattr(self, 'ylabel_artist') and (event.artist == self.ylabel_artist): info['axis_num'] = 1 self.callback(Event('axis_click', info)) def add_gate(self, gate): self.gates.append(gate) self.set_active_gate(gate) def remove_active_gate(self): if self.active_gate is not None: self.gates.remove(self.active_gate) self.active_gate.remove() self.active_gate = None def set_active_gate(self, gate): if self.active_gate is None: self.active_gate = gate gate.activate() elif self.active_gate is not gate: self.active_gate.inactivate() self.active_gate = gate gate.activate() def _get_next_gate_name(self): gate_name = 'gate{0}'.format(self.gate_num) self.gate_num += 1 return gate_name def _handle_gate_events(self, event): self.set_active_gate(event.info['caller']) def create_gate_widget(self, kind): def clean_drawing_tools(): self._drawing_tool.disconnect_events() self.canvas.draw_idle() self._drawing_tool = None def create_gate(*args): cancelled = False # TODO allow drawing tool to cancel verts = args[0] ch = self.current_channels verts = [dict(zip(ch, v)) for v in verts] if kind == 'poly': gate_type = PolyGate elif 'threshold' in kind or 'quad' in kind: gate_type = ThresholdGate # FIXME: This is very specific implementation if 'vertical' in kind: verts = [{ch[0]: v[ch[0]]} for v in verts] elif 'horizontal' in kind: if len(ch) == 1: cancelled = True else: verts = [{ch[1]: v[ch[1]]} for v in verts] if not cancelled: gate = BaseGate(verts, gate_type, name=self._get_next_gate_name(), callback_list=self._handle_gate_events) gate.spawn(ch, self.ax) self.add_gate(gate) clean_drawing_tools() def start_drawing(kind): if kind == 'poly': self._drawing_tool = PolyDrawer(self.ax, oncreated=create_gate, lineprops=dict(color='k', marker='o')) elif kind == 'quad': self._drawing_tool = Cursor(self.ax, vertOn=1, horizOn=1) elif kind == 'horizontal threshold': self._drawing_tool = Cursor(self.ax, vertOn=0, horizOn=1) elif kind == 'vertical threshold': self._drawing_tool = Cursor(self.ax, vertOn=1, horizOn=0) if isinstance(self._drawing_tool, Cursor): def finish_drawing(event): self._drawing_tool.clear(None) return create_gate([(event.xdata, event.ydata)]) self._drawing_tool.connect_event('button_press_event', finish_drawing) start_drawing(kind) #################### ### Loading Data ### #################### def load_fcs(self, filepath=None, parent=None): ax = self.ax if parent is None: parent = self.fig.canvas if filepath is None: from FlowCytometryTools.gui import dialogs filepath = dialogs.open_file_dialog('Select an FCS file to load', 'FCS files (*.fcs)|*.fcs', parent=parent) if filepath is not None: self.sample = FCMeasurement('temp', datafile=filepath) print('WARNING: Data is raw (not transformation).') self._sample_loaded_event() def load_measurement(self, measurement): self.sample = measurement.copy() self._sample_loaded_event() def _sample_loaded_event(self): if self.sample is not None: self.current_channels = list(self.sample.channel_names[0:2]) self.set_axes(self.current_channels, self.ax) def get_available_channels(self): return self.sample.channel_names def change_axis(self, axis_num, channel_name): """ TODO: refactor that and set_axes what to do with ax? axis_num: int axis number channel_name: str new channel to plot on that axis """ current_channels = list(self.current_channels) if len(current_channels) == 1: if axis_num == 0: new_channels = channel_name, else: new_channels = current_channels[0], channel_name else: new_channels = list(current_channels) new_channels[axis_num] = channel_name self.set_axes(new_channels, self.ax) def set_axes(self, channels, ax): """ channels : iterable of string each value corresponds to a channel names names must be unique """ # To make sure displayed as hist if len(set(channels)) == 1: channels = channels[0], self.current_channels = channels # Remove existing gates for gate in self.gates: gate.remove_spawned_gates() ## # Has a clear axis command inside!! # which will "force kill" spawned gates self.plot_data() for gate in self.gates: sgate = gate.spawn(channels, ax) gate._refresh_activation() def close(self): for gate in self.gates: gate.remove() self.disconnect_events() #################### ### Plotting Data ## #################### def plot_data(self): """Plots the loaded data""" # Clear the plot before plotting onto it self.ax.cla() if self.sample is None: return if self.current_channels is None: self.current_channels = self.sample.channel_names[:2] channels = self.current_channels channels_to_plot = channels[0] if len(channels) == 1 else channels self.sample.plot(channels_to_plot, ax=self.ax) xaxis = self.ax.get_xaxis() yaxis = self.ax.get_yaxis() self.xlabel_artist = xaxis.get_label() self.ylabel_artist = yaxis.get_label() self.xlabel_artist.set_picker(5) self.ylabel_artist.set_picker(5) self.fig.canvas.draw() def get_generation_code(self): """Return python code that generates all drawn gates.""" if len(self.gates) < 1: code = '' else: import_list = set( [gate._gencode_gate_class for gate in self.gates]) import_list = 'from FlowCytometryTools import ' + ', '.join( import_list) code_list = [gate.get_generation_code() for gate in self.gates] code_list.sort() code_list = '\n'.join(code_list) code = import_list + 2 * '\n' + code_list self.callback(Event('generated_code', {'code': code})) return code
class ThresholdSelection(cytoflow.views.HistogramView): """ Plots, and lets the user interact with, a threshold on the X axis. TODO - beautify! Attributes ---------- op : Instance(ThresholdOp) the ThresholdOp we're working on. huefacet : Str The conditioning variable to show multiple colors on this plot subset : Str the string passed to Experiment.subset() defining the subset we plot interactive : Bool is this view interactive? Notes ----- We inherit `xfacet` and `yfacet` from `cytoflow.views.HistogramView`, but they must both be unset! Examples -------- In an IPython notebook with `%matplotlib notebook` >>> t = flow.ThresholdOp(name = "Threshold", ... channel = "Y2-A") >>> tv = t.default_view() >>> tv.plot(ex2) >>> tv.interactive = True >>> # .... draw a threshold on the plot >>> ex3 = thresh.apply(ex2) """ id = Constant('edu.mit.synbio.cytoflow.views.threshold') friendly_id = Constant("Threshold Selection") op = Instance(IOperation) name = DelegatesTo('op') channel = DelegatesTo('op') threshold = DelegatesTo('op') interactive = Bool(False, transient=True) # internal state _ax = Any(transient=True) _line = Instance(Line2D, transient=True) _cursor = Instance(Cursor, transient=True) def plot(self, experiment, **kwargs): """Plot the histogram and then plot the threshold on top of it.""" if not experiment: raise util.CytoflowViewError("No experiment specified") if self.xfacet: raise util.CytoflowViewError( "ThresholdSelection.xfacet must be empty") if self.yfacet: raise util.CytoflowViewError( "ThresholdSelection.yfacet must be empty") super(ThresholdSelection, self).plot(experiment, **kwargs) self._ax = plt.gca() self._draw_threshold() self._interactive() @on_trait_change('threshold', post_init=True) def _draw_threshold(self): if not self._ax or not self.threshold: return if self._line: # when used in the GUI, _draw_threshold gets called *twice* without # the plot being updated inbetween: and then the line can't be # removed from the plot, because it was never added. so check # explicitly first. this is likely to be an issue in other # interactive plots, too. if self._line and self._line in self._ax.lines: self._line.remove() self._line = None if self.threshold: self._line = plt.axvline(self.threshold, linewidth=3, color='blue') plt.draw() @on_trait_change('interactive', post_init=True) def _interactive(self): if self._ax and self.interactive: self._cursor = Cursor(self._ax, horizOn=False, vertOn=True, color='blue', useblit=True) self._cursor.connect_event('button_press_event', self._onclick) elif self._cursor: self._cursor.disconnect_events() self._cursor = None def _onclick(self, event): """Update the threshold location""" # sometimes the axes aren't set up and we don't get xdata (??) if event.xdata: self.threshold = event.xdata
class FCGateManager(EventGenerator): """Manages gate creation widgets and gates.""" def __init__(self, ax, callback_list=None): self.gates = [] self.fig = ax.figure self.ax = ax self._plt_data = None self.active_gate = None self.sample = None self.canvas = self.fig.canvas self.key_handler_cid = self.canvas.mpl_connect('key_press_event', lambda event: key_press_handler(event, self.canvas, self)) self.pick_event_cid = self.canvas.mpl_connect('pick_event', self.pick_event_handler) self.gate_num = 1 self.current_channels = 'd1', 'd2' self.add_callback(callback_list) def disconnect_events(self): self.canvas.mpl_disconnect(self.key_handler_cid) self.canvas.mpl_disconnect(self.pick_event_cid) def pick_event_handler(self, event): """ Handles pick events """ info = {'options': self.get_available_channels(), 'guiEvent': event.mouseevent.guiEvent, } if hasattr(self, 'xlabel_artist') and (event.artist == self.xlabel_artist): info['axis_num'] = 0 self.callback(Event('axis_click', info)) if hasattr(self, 'ylabel_artist') and (event.artist == self.ylabel_artist): info['axis_num'] = 1 self.callback(Event('axis_click', info)) def add_gate(self, gate): self.gates.append(gate) self.set_active_gate(gate) def remove_active_gate(self): if self.active_gate is not None: self.gates.remove(self.active_gate) self.active_gate.remove() self.active_gate = None def set_active_gate(self, gate): if self.active_gate is None: self.active_gate = gate gate.activate() elif self.active_gate is not gate: self.active_gate.inactivate() self.active_gate = gate gate.activate() def _get_next_gate_name(self): gate_name = 'gate{0}'.format(self.gate_num) self.gate_num += 1 return gate_name def _handle_gate_events(self, event): self.set_active_gate(event.info['caller']) def create_gate_widget(self, kind): def clean_drawing_tools(): self._drawing_tool.disconnect_events() self.canvas.draw_idle() self._drawing_tool = None def create_gate(*args): cancelled = False # TODO allow drawing tool to cancel verts = args[0] ch = self.current_channels verts = [dict(zip(ch, v)) for v in verts] if kind == 'poly': gate_type = PolyGate elif 'threshold' in kind or 'quad' in kind: gate_type = ThresholdGate # FIXME: This is very specific implementation if 'vertical' in kind: verts = [{ch[0]: v[ch[0]]} for v in verts] elif 'horizontal' in kind: if len(ch) == 1: cancelled = True else: verts = [{ch[1]: v[ch[1]]} for v in verts] if not cancelled: gate = BaseGate(verts, gate_type, name=self._get_next_gate_name(), callback_list=self._handle_gate_events) gate.spawn(ch, self.ax) self.add_gate(gate) clean_drawing_tools() def start_drawing(kind): if kind == 'poly': self._drawing_tool = PolyDrawer(self.ax, oncreated=create_gate, lineprops=dict(color='k', marker='o')) elif kind == 'quad': self._drawing_tool = Cursor(self.ax, vertOn=1, horizOn=1) elif kind == 'horizontal threshold': self._drawing_tool = Cursor(self.ax, vertOn=0, horizOn=1) elif kind == 'vertical threshold': self._drawing_tool = Cursor(self.ax, vertOn=1, horizOn=0) if isinstance(self._drawing_tool, Cursor): def finish_drawing(event): self._drawing_tool.clear(None) return create_gate([(event.xdata, event.ydata)]) self._drawing_tool.connect_event('button_press_event', finish_drawing) start_drawing(kind) #################### ### Loading Data ### #################### def load_fcs(self, filepath=None, parent=None): ax = self.ax if parent is None: parent = self.fig.canvas if filepath is None: from FlowCytometryTools.gui import dialogs filepath = dialogs.open_file_dialog('Select an FCS file to load', 'FCS files (*.fcs)|*.fcs', parent=parent) if filepath is not None: self.sample = FCMeasurement('temp', datafile=filepath) print('WARNING: Data is raw (not transformation).') self._sample_loaded_event() def load_measurement(self, measurement): self.sample = measurement.copy() self._sample_loaded_event() def _sample_loaded_event(self): if self.sample is not None: self.current_channels = list(self.sample.channel_names[0:2]) self.set_axes(self.current_channels, self.ax) def get_available_channels(self): return self.sample.channel_names def change_axis(self, axis_num, channel_name): """ TODO: refactor that and set_axes what to do with ax? axis_num: int axis number channel_name: str new channel to plot on that axis """ current_channels = list(self.current_channels) if len(current_channels) == 1: if axis_num == 0: new_channels = channel_name, else: new_channels = current_channels[0], channel_name else: new_channels = list(current_channels) new_channels[axis_num] = channel_name self.set_axes(new_channels, self.ax) def set_axes(self, channels, ax): """ channels : iterable of string each value corresponds to a channel names names must be unique """ # To make sure displayed as hist if len(set(channels)) == 1: channels = channels[0], self.current_channels = channels # Remove existing gates for gate in self.gates: gate.remove_spawned_gates() ## # Has a clear axis command inside!! # which will "force kill" spawned gates self.plot_data() for gate in self.gates: sgate = gate.spawn(channels, ax) gate._refresh_activation() def close(self): for gate in self.gates: gate.remove() self.disconnect_events() #################### ### Plotting Data ## #################### def plot_data(self): """Plots the loaded data""" # Clear the plot before plotting onto it self.ax.cla() if self.sample is None: return if self.current_channels is None: self.current_channels = self.sample.channel_names[:2] channels = self.current_channels channels_to_plot = channels[0] if len(channels) == 1 else channels self.sample.plot(channels_to_plot, ax=self.ax) xaxis = self.ax.get_xaxis() yaxis = self.ax.get_yaxis() self.xlabel_artist = xaxis.get_label() self.ylabel_artist = yaxis.get_label() self.xlabel_artist.set_picker(5) self.ylabel_artist.set_picker(5) self.fig.canvas.draw() def get_generation_code(self): """Return python code that generates all drawn gates.""" if len(self.gates) < 1: code = '' else: import_list = set([gate._gencode_gate_class for gate in self.gates]) import_list = 'from FlowCytometryTools import ' + ', '.join(import_list) code_list = [gate.get_generation_code() for gate in self.gates] code_list.sort() code_list = '\n'.join(code_list) code = import_list + 2 * '\n' + code_list self.callback(Event('generated_code', {'code': code})) return code
class MaskEditor(ToolWindow, DoubleFileChooserDialog): def __init__(self, *args, **kwargs): self.mask = None self._undo_stack = [] self._im = None self._selector = None self._cursor = None self.exposureloader = None self.plot2d = None ToolWindow.__init__(self, *args, **kwargs) DoubleFileChooserDialog.__init__( self, self.widget, 'Open mask file...', 'Save mask file...', [('Mask files', '*.mat'), ('All files', '*')], self.instrument.config['path']['directories']['mask'], os.path.abspath(self.instrument.config['path']['directories']['mask']), ) def init_gui(self, *args, **kwargs): self.exposureloader = ExposureLoader(self.instrument) self.builder.get_object('loadexposure_expander').add(self.exposureloader) self.exposureloader.connect('open', self.on_loadexposure) self.plot2d = PlotImageWidget() self.builder.get_object('plotbox').pack_start(self.plot2d.widget, True, True, 0) self.builder.get_object('toolbar').set_sensitive(False) def on_loadexposure(self, exposureloader: ExposureLoader, im: Exposure): if self.mask is None: self.mask = im.mask self._im = im self.plot2d.set_image(im.intensity) self.plot2d.set_mask(self.mask) self.builder.get_object('toolbar').set_sensitive(True) def on_new(self, button): if self._im is None or self.mask is None: return False self.mask = np.ones_like(self.mask) self.plot2d.set_mask(self.mask) self.set_last_filename(None) def on_open(self, button): filename = self.get_open_filename() if filename is not None: mask = loadmat(filename) self.mask = mask[[k for k in mask.keys() if not k.startswith('__')][0]] self.plot2d.set_mask(self.mask) def on_save(self, button): filename = self.get_last_filename() if filename is None: return self.on_saveas(button) maskname = os.path.splitext(os.path.split(filename)[1])[0] savemat(filename, {maskname: self.mask}) def on_saveas(self, button): filename = self.get_save_filename(None) if filename is not None: self.on_save(button) def suggest_filename(self): return 'mask_dist_{0.year:d}{0.month:02d}{0.day:02d}.mat'.format(datetime.date.today()) def on_selectcircle_toggled(self, button): if button.get_active(): self.set_sensitive(False, 'Ellipse selection not ready', ['new_button', 'save_button', 'saveas_button', 'open_button', 'undo_button', 'selectrectangle_button', 'selectpolygon_button', 'pixelhunting_button', 'loadexposure_expander', 'close_button', self.plot2d.toolbar, self.plot2d.settings_expander]) while self.plot2d.toolbar.mode != '': # turn off zoom, pan, etc. modes. self.plot2d.toolbar.zoom() self._selector = EllipseSelector(self.plot2d.axis, self.on_ellipse_selected, rectprops={'facecolor': 'white', 'edgecolor': 'none', 'alpha': 0.7, 'fill': True, 'zorder': 10}, button=[1, ], interactive=False, lineprops={'zorder': 10}) self._selector.state.add('square') self._selector.state.add('center') else: assert isinstance(self._selector, EllipseSelector) self._selector.set_active(False) self._selector.set_visible(False) self._selector = None self.plot2d.replot(keepzoom=False) self.set_sensitive(True) def on_ellipse_selected(self, pos1, pos2): # pos1 and pos2 are mouse button press and release events, with xdata and ydata carrying # the two opposite corners of the bounding box of the circle. These are NOT the exact # button presses and releases! row = np.arange(self.mask.shape[0])[:, np.newaxis] column = np.arange(self.mask.shape[1])[np.newaxis, :] row0 = 0.5 * (pos1.ydata + pos2.ydata) col0 = 0.5 * (pos1.xdata + pos2.xdata) r2 = ((pos2.xdata - pos1.xdata) ** 2 + (pos2.ydata - pos1.ydata) ** 2) / 8 tobemasked = (row - row0) ** 2 + (column - col0) ** 2 <= r2 self._undo_stack.append(self.mask) if self.builder.get_object('mask_button').get_active(): self.mask &= ~tobemasked elif self.builder.get_object('unmask_button').get_active(): self.mask |= tobemasked elif self.builder.get_object('invertmask_button').get_active(): self.mask[tobemasked] = ~self.mask[tobemasked] else: pass self.builder.get_object('selectcircle_button').set_active(False) self.plot2d.set_mask(self.mask) def on_selectrectangle_toggled(self, button): if button.get_active(): self.set_sensitive(False, 'Rectangle selection not ready', ['new_button', 'save_button', 'saveas_button', 'open_button', 'undo_button', 'selectcircle_button', 'selectpolygon_button', 'pixelhunting_button', 'loadexposure_expander', 'close_button', self.plot2d.toolbar, self.plot2d.settings_expander]) while self.plot2d.toolbar.mode != '': # turn off zoom, pan, etc. modes. self.plot2d.toolbar.zoom() self._selector = RectangleSelector(self.plot2d.axis, self.on_rectangle_selected, rectprops={'facecolor': 'white', 'edgecolor': 'none', 'alpha': 0.7, 'fill': True, 'zorder': 10}, button=[1, ], interactive=False, lineprops={'zorder': 10}) else: self._selector.set_active(False) self._selector.set_visible(False) self._selector = None self.plot2d.replot(keepzoom=False) self.set_sensitive(True) def on_rectangle_selected(self, pos1, pos2): # pos1 and pos2 are mouse button press and release events, with xdata and ydata # carrying the two opposite corners of the bounding box of the rectangle. These # are NOT the exact button presses and releases! row = np.arange(self.mask.shape[0])[:, np.newaxis] column = np.arange(self.mask.shape[1])[np.newaxis, :] tobemasked = ((row >= min(pos1.ydata, pos2.ydata)) & (row <= max(pos1.ydata, pos2.ydata)) & (column >= min(pos1.xdata, pos2.xdata)) & (column <= max(pos1.xdata, pos2.xdata))) self._undo_stack.append(self.mask) if self.builder.get_object('mask_button').get_active(): self.mask = self.mask & (~tobemasked) elif self.builder.get_object('unmask_button').get_active(): self.mask = self.mask | tobemasked elif self.builder.get_object('invertmask_button').get_active(): self.mask[tobemasked] = ~self.mask[tobemasked] else: pass self.builder.get_object('selectrectangle_button').set_active(False) self.plot2d.set_mask(self.mask) def on_selectpolygon_toggled(self, button): if button.get_active(): self.set_sensitive(False, 'Polygon selection not ready', ['new_button', 'save_button', 'saveas_button', 'open_button', 'undo_button', 'selectrectangle_button', 'selectcircle_button', 'pixelhunting_button', 'loadexposure_expander', 'close_button', self.plot2d.toolbar, self.plot2d.settings_expander]) while self.plot2d.toolbar.mode != '': # turn off zoom, pan, etc. modes. self.plot2d.toolbar.zoom() self._selector = LassoSelector(self.plot2d.axis, self.on_polygon_selected, lineprops={'color': 'white', 'zorder': 10}, button=[1, ], ) else: self._selector.set_active(False) self._selector.set_visible(False) self._selector = None self.plot2d.replot(keepzoom=False) self.set_sensitive(True) def on_polygon_selected(self, vertices): path = Path(vertices) col, row = np.meshgrid(np.arange(self.mask.shape[1]), np.arange(self.mask.shape[0])) points = np.vstack((col.flatten(), row.flatten())).T tobemasked = path.contains_points(points).reshape(self.mask.shape) self._undo_stack.append(self.mask) if self.builder.get_object('mask_button').get_active(): self.mask = self.mask & (~tobemasked) elif self.builder.get_object('unmask_button').get_active(): self.mask = self.mask | tobemasked elif self.builder.get_object('invertmask_button').get_active(): self.mask[tobemasked] = ~self.mask[tobemasked] else: pass self.plot2d.set_mask(self.mask) self.builder.get_object('selectpolygon_button').set_active(False) def on_mask_toggled(self, button): pass def on_unmask_toggled(self, button): pass def on_invertmask_toggled(self, button): pass def on_pixelhunting_toggled(self, button): if button.get_active(): self._cursor = Cursor(self.plot2d.axis, useblit=False, color='white', lw=1) self._cursor.connect_event('button_press_event', self.on_cursorclick) while self.plot2d.toolbar.mode != '': # turn off zoom, pan, etc. modes. self.plot2d.toolbar.zoom() else: self._cursor.disconnect_events() self._cursor = None self._undo_stack.append(self.mask) self.plot2d.replot(keepzoom=False) def on_cursorclick(self, event): if (event.inaxes == self.plot2d.axis) and (self.plot2d.toolbar.mode == ''): self.mask[round(event.ydata), round(event.xdata)] ^= True self._cursor.disconnect_events() self._cursor = None self.plot2d.replot(keepzoom=True) self.on_pixelhunting_toggled(self.builder.get_object('pixelhunting_button')) def cleanup(self): super().cleanup() self._undo_stack = [] def on_undo(self, button): try: self.mask = self._undo_stack.pop() except IndexError: return self.plot2d.set_mask(self.mask)
class PolygonSelection(cytoflow.views.ScatterplotView): """Plots, and lets the user interact with, a 2D polygon selection. Attributes ---------- op : Instance(PolygonOp) The operation on which this selection view is operating huefacet : Str The conditioning variable to show multiple colors on this plot subset : Str The string for subsetting the plot interactive : Bool is this view interactive? Ie, can the user set the polygon verticies with mouse clicks? Notes ----- We inherit `xfacet` and `yfacet` from `cytoflow.views.ScatterPlotView`, but they must both be unset! Examples -------- In an IPython notebook with `%matplotlib notebook` >>> s = flow.ScatterplotView(xchannel = "V2-A", ... ychannel = "Y2-A") >>> poly = s.default_view() >>> poly.plot(ex2) >>> poly.interactive = True """ id = Constant('edu.mit.synbio.cytoflow.views.polygon') friendly_id = Constant("Polygon Selection") op = Instance(IOperation) name = DelegatesTo('op') xchannel = DelegatesTo('op') ychannel = DelegatesTo('op') interactive = Bool(False, transient=True) # internal state. _ax = Any(transient=True) _cursor = Instance(Cursor, transient=True) _path = Instance(mpl.path.Path, transient=True) _patch = Instance(mpl.patches.PathPatch, transient=True) _line = Instance(mpl.lines.Line2D, transient=True) _drawing = Bool(transient=True) _last_draw_time = Float(0.0, transient=True) _last_click_time = Float(0.0, transient=True) def plot(self, experiment, **kwargs): """Plot self.view, and then plot the selection on top of it.""" if not experiment: raise util.CytoflowViewError("No experiment specified") if self.xfacet: raise util.CytoflowViewError( "RangeSelection.xfacet must be empty or `Undefined`") if self.yfacet: raise util.CytoflowViewError( "RangeSelection.yfacet must be empty or `Undefined`") super(PolygonSelection, self).plot(experiment, **kwargs) self._ax = plt.gca() self._draw_poly() self._interactive() @on_trait_change('op.vertices', post_init=True) def _draw_poly(self): if not self._ax: return if self._patch and self._patch in self._ax.patches: self._patch.remove() if self._drawing or not self.op.vertices or len(self.op.vertices) < 3 \ or any([len(x) != 2 for x in self.op.vertices]): return patch_vert = np.concatenate( (np.array(self.op.vertices), np.array((0, 0), ndmin=2))) self._patch = \ mpl.patches.PathPatch(mpl.path.Path(patch_vert, closed = True), edgecolor="black", linewidth = 1.5, fill = False) self._ax.add_patch(self._patch) plt.draw_if_interactive() @on_trait_change('interactive', post_init=True) def _interactive(self): if self._ax and self.interactive: self._cursor = Cursor(self._ax, horizOn=False, vertOn=False) self._cursor.connect_event('button_press_event', self._onclick) self._cursor.connect_event('motion_notify_event', self._onmove) elif self._cursor: self._cursor.disconnect_events() self._cursor = None def _onclick(self, event): """Update selection traits""" if not self._ax: return if (self._cursor.ignore(event)): return # we have to check the wall clock time because the IPython notebook # doesn't seem to register double-clicks if event.dblclick or (time.clock() - self._last_click_time < 0.5): self._drawing = False self.op.vertices = map(tuple, self._path.vertices) self.op._xscale = plt.gca().get_xscale() self.op._yscale = plt.gca().get_yscale() self._path = None return self._last_click_time = time.clock() self._drawing = True if self._patch and self._patch in self._ax.patches: self._patch.remove() if self._path: vertices = np.concatenate((self._path.vertices, np.array((event.xdata, event.ydata), ndmin=2))) else: vertices = np.array((event.xdata, event.ydata), ndmin=2) self._path = mpl.path.Path(vertices, closed=False) self._patch = mpl.patches.PathPatch(self._path, edgecolor="black", fill=False) self._ax.add_patch(self._patch) plt.draw_if_interactive() def _onmove(self, event): if not self._ax: return if (self._cursor.ignore(event) or not self._drawing or not self._path or self._path.vertices.shape[0] == 0 or not event.xdata or not event.ydata): return # only draw 5 times/sec if (time.clock() - self._last_draw_time < 0.2): return self._last_draw_time = time.clock() if self._line and self._line in self._ax.lines: self._line.remove() xdata = [self._path.vertices[-1, 0], event.xdata] ydata = [self._path.vertices[-1, 1], event.ydata] self._line = mpl.lines.Line2D(xdata, ydata, linewidth=1, color="black") self._ax.add_line(self._line) plt.gcf().canvas.draw()
class QuadSelection(Op2DView, ScatterplotView): """Plots, and lets the user interact with, a quadrant gate. Attributes ---------- interactive : Bool is this view interactive? Ie, can the user set the threshold with a mouse click? Notes ----- We inherit :attr:`xfacet` and :attr:`yfacet` from :class:`cytoflow.views.ScatterplotView`, but they must both be unset! Examples -------- In an Jupyter notebook with `%matplotlib notebook` >>> q = flow.QuadOp(name = "Quad", ... xchannel = "V2-A", ... ychannel = "Y2-A")) >>> qv = q.default_view() >>> qv.interactive = True >>> qv.plot(ex2) """ id = Constant('edu.mit.synbio.cytoflow.views.quad') friendly_id = Constant("Quadrant Selection") xfacet = Constant(None) yfacet = Constant(None) # override the Op2DView xscale = util.ScaleEnum yscale = util.ScaleEnum xthreshold = DelegatesTo('op') ythreshold = DelegatesTo('op') interactive = Bool(False, transient=True) # internal state. _ax = Any(transient=True) _hline = Instance(Line2D, transient=True) _vline = Instance(Line2D, transient=True) _cursor = Instance(Cursor, transient=True) def plot(self, experiment, **kwargs): """ Plot the underlying scatterplot and then plot the selection on top of it. Parameters ---------- """ if experiment is None: raise util.CytoflowViewError('experiment', "No experiment specified") super().plot(experiment, **kwargs) self._ax = plt.gca() self._draw_lines() self._interactive() @on_trait_change('xthreshold, ythreshold', post_init=True) def _draw_lines(self): if not self._ax: return if self._hline and self._hline in self._ax.lines: self._hline.remove() if self._vline and self._vline in self._ax.lines: self._vline.remove() if self.xthreshold and self.ythreshold: self._hline = plt.axhline(self.ythreshold, linewidth=3, color='blue') self._vline = plt.axvline(self.xthreshold, linewidth=3, color='blue') plt.draw() @on_trait_change('interactive', post_init=True) def _interactive(self): if self._ax and self.interactive: self._cursor = Cursor(self._ax, horizOn=True, vertOn=True, color='blue', useblit=True) self._cursor.connect_event('button_press_event', self._onclick) elif self._cursor: self._cursor.disconnect_events() self._cursor = None def _onclick(self, event): """Update the threshold location""" self.xthreshold = event.xdata self.ythreshold = event.ydata
class PolygonSelection(cytoflow.views.ScatterplotView): """Plots, and lets the user interact with, a 2D polygon selection. Attributes ---------- op : Instance(PolygonOp) The operation on which this selection view is operating huefacet : Str The conditioning variable to show multiple colors on this plot subset : Str The string for subsetting the plot interactive : Bool is this view interactive? Ie, can the user set the polygon verticies with mouse clicks? Notes ----- We inherit `xfacet` and `yfacet` from `cytoflow.views.ScatterPlotView`, but they must both be unset! Examples -------- In an IPython notebook with `%matplotlib notebook` >>> s = flow.ScatterplotView(xchannel = "V2-A", ... ychannel = "Y2-A") >>> poly = s.default_view() >>> poly.plot(ex2) >>> poly.interactive = True """ id = Constant('edu.mit.synbio.cytoflow.views.polygon') friendly_id = Constant("Polygon Selection") op = Instance(IOperation) name = DelegatesTo('op') xchannel = DelegatesTo('op') ychannel = DelegatesTo('op') interactive = Bool(False, transient = True) # internal state. _ax = Any(transient = True) _cursor = Instance(Cursor, transient = True) _path = Instance(mpl.path.Path, transient = True) _patch = Instance(mpl.patches.PathPatch, transient = True) _line = Instance(mpl.lines.Line2D, transient = True) _drawing = Bool(transient = True) _last_draw_time = Float(0.0, transient = True) _last_click_time = Float(0.0, transient = True) def plot(self, experiment, **kwargs): """Plot self.view, and then plot the selection on top of it.""" if not experiment: raise util.CytoflowViewError("No experiment specified") if self.xfacet: raise util.CytoflowViewError("RangeSelection.xfacet must be empty or `Undefined`") if self.yfacet: raise util.CytoflowViewError("RangeSelection.yfacet must be empty or `Undefined`") super(PolygonSelection, self).plot(experiment, **kwargs) self._ax = plt.gca() self._draw_poly() self._interactive() @on_trait_change('op.vertices', post_init = True) def _draw_poly(self): if not self._ax: return if self._patch and self._patch in self._ax.patches: self._patch.remove() if self._drawing or not self.op.vertices or len(self.op.vertices) < 3 \ or any([len(x) != 2 for x in self.op.vertices]): return patch_vert = np.concatenate((np.array(self.op.vertices), np.array((0,0), ndmin = 2))) self._patch = \ mpl.patches.PathPatch(mpl.path.Path(patch_vert, closed = True), edgecolor="black", linewidth = 1.5, fill = False) self._ax.add_patch(self._patch) plt.draw_if_interactive() @on_trait_change('interactive', post_init = True) def _interactive(self): if self._ax and self.interactive: self._cursor = Cursor(self._ax, horizOn = False, vertOn = False) self._cursor.connect_event('button_press_event', self._onclick) self._cursor.connect_event('motion_notify_event', self._onmove) elif self._cursor: self._cursor.disconnect_events() self._cursor = None def _onclick(self, event): """Update selection traits""" if not self._ax: return if(self._cursor.ignore(event)): return # we have to check the wall clock time because the IPython notebook # doesn't seem to register double-clicks if event.dblclick or (time.clock() - self._last_click_time < 0.5): self._drawing = False self.op.vertices = map(tuple, self._path.vertices) self.op._xscale = plt.gca().get_xscale() self.op._yscale = plt.gca().get_yscale() self._path = None return self._last_click_time = time.clock() self._drawing = True if self._patch and self._patch in self._ax.patches: self._patch.remove() if self._path: vertices = np.concatenate((self._path.vertices, np.array((event.xdata, event.ydata), ndmin = 2))) else: vertices = np.array((event.xdata, event.ydata), ndmin = 2) self._path = mpl.path.Path(vertices, closed = False) self._patch = mpl.patches.PathPatch(self._path, edgecolor = "black", fill = False) self._ax.add_patch(self._patch) plt.draw_if_interactive() def _onmove(self, event): if not self._ax: return if(self._cursor.ignore(event) or not self._drawing or not self._path or self._path.vertices.shape[0] == 0 or not event.xdata or not event.ydata): return # only draw 5 times/sec if(time.clock() - self._last_draw_time < 0.2): return self._last_draw_time = time.clock() if self._line and self._line in self._ax.lines: self._line.remove() xdata = [self._path.vertices[-1, 0], event.xdata] ydata = [self._path.vertices[-1, 1], event.ydata] self._line = mpl.lines.Line2D(xdata, ydata, linewidth = 1, color = "black") self._ax.add_line(self._line) plt.gcf().canvas.draw()
class QuadSelection(cytoflow.views.ScatterplotView): """Plots, and lets the user interact with, a quadrant gate. Attributes ---------- op : Instance(Range2DOp) The instance of Range2DOp that we're viewing / editing huefacet : Str The conditioning variable to plot multiple colors subset : Str The string passed to `Experiment.query()` to subset the data before plotting interactive : Bool is this view interactive? Ie, can the user set the threshold with a mouse click? Notes ----- We inherit `xfacet` and `yfacet` from `cytoflow.views.ScatterplotView`, but they must both be unset! Examples -------- In an IPython notebook with `%matplotlib notebook` >>> q = flow.QuadOp(name = "Quad", ... xchannel = "V2-A", ... ychannel = "Y2-A")) >>> qv = q.default_view() >>> qv.interactive = True >>> qv.plot(ex2) """ id = Constant('edu.mit.synbio.cytoflow.views.quad') friendly_id = Constant("Quadrant Selection") op = Instance(IOperation) name = DelegatesTo('op') xchannel = DelegatesTo('op') ychannel = DelegatesTo('op') interactive = Bool(False, transient=True) # internal state. _ax = Any(transient=True) _hline = Instance(Line2D, transient=True) _vline = Instance(Line2D, transient=True) _cursor = Instance(Cursor, transient=True) def plot(self, experiment, **kwargs): """Plot the underlying scatterplot and then plot the selection on top of it.""" if not experiment: raise util.CytoflowOpError("No experiment specified") if not experiment: raise util.CytoflowViewError("No experiment specified") if self.xfacet: raise util.CytoflowViewError( "RangeSelection.xfacet must be empty or `Undefined`") if self.yfacet: raise util.CytoflowViewError( "RangeSelection.yfacet must be empty or `Undefined`") super(QuadSelection, self).plot(experiment, **kwargs) self._ax = plt.gca() self._draw_lines() self._interactive() @on_trait_change('op.xthreshold, op.ythreshold', post_init=True) def _draw_lines(self): if not self._ax: return if self._hline and self._hline in self._ax.lines: self._hline.remove() if self._vline and self._vline in self._ax.lines: self._vline.remove() if self.op.xthreshold and self.op.ythreshold: self._hline = plt.axhline(self.op.ythreshold, linewidth=3, color='blue') self._vline = plt.axvline(self.op.xthreshold, linewidth=3, color='blue') plt.draw_if_interactive() @on_trait_change('interactive', post_init=True) def _interactive(self): if self._ax and self.interactive: self._cursor = Cursor(self._ax, horizOn=True, vertOn=True, color='blue') self._cursor.connect_event('button_press_event', self._onclick) elif self._cursor: self._cursor.disconnect_events() self._cursor = None def _onclick(self, event): """Update the threshold location""" self.op.xthreshold = event.xdata self.op.ythreshold = event.ydata
class ECE4012: def __init__(self, figure, filename, toolbar): self.dfCal = None self.fig = figure self.toolbar = toolbar self.filename = filename self.rect = None def initializer(self, type): self.fig.clear() print("celar figure") self.fig.subplots_adjust(hspace=1.0) self.annoteText = "annotated" #self.fig = figure #self.toolbar = toolbar self.isAnnotate = False self.fig.canvas.mpl_connect('button_release_event', self.onrelease) self.fig.canvas.mpl_connect('button_press_event', self.onclick) self.fig.canvas.mpl_connect('key_press_event', self.onpress) self.fig.canvas.mpl_connect('pick_event', self.onpick) # print(self.fig) if type == "DB": self.searchOpenCalibration(self.filename) self.conn = self.getConnection(self.filename) # df = pd.read_sql_query("select * from acc LIMIT 100000;", self.conn) t = time.time() df = pd.read_sql_query("select * from acc;", self.conn) elapsed = time.time() - t print("time taken to read data was {:f}".format(elapsed)) elif type == "CSV": print("CSV getting Calibration") self.searchOpenCalibration(self.filename) print("CSV opening file") df = pd.read_csv(self.filename) print("CSV opened file") print("Starting") if self.dfCal is not None: df["valuex"] = np.subtract(df["valuex"], self.dfCal['Values'][ self.XOffset]) / self.dfCal['Values'][self.XGain] df["valuey"] = np.subtract(df["valuey"], self.dfCal['Values'][ self.YOffset]) / self.dfCal['Values'][self.YGain] df["valuez"] = np.subtract(df["valuez"], self.dfCal['Values'][ self.ZOffset]) / self.dfCal['Values'][self.ZGain] # DONE !!TODO this calibartion should change to either actual calibration from # metawear C # print(len(df["valuex"])) # avgX = np.sum(df["valuex"]) / len(df["valuex"]) # avgY = np.sum(df["valuey"]) / len(df["valuey"]) # avgZ = np.sum(df["valuez"]) / len(df["valuez"]) # df["valuex"] = np.subtract(df["valuex"], avgX) # df["valuey"] = np.subtract(df["valuey"], avgY) # df["valuez"] = np.subtract(df["valuez"], avgZ) #self.axTop = self.fig.add_subplot(2, 1, 1) #self.axBottom = self.fig.add_subplot(2, 1, 2) self.fig.subplots_adjust(bottom=0.3, left=0.2, hspace=1.0) self.labels = ['30 sec', '5 min', '30 min', '1 hr', '2 hr'] sec30 = pd.Timedelta(30, unit='s') min5 = pd.Timedelta(5, unit='m') min30 = pd.Timedelta(30, unit='m') hr1 = pd.Timedelta(1, unit='h') hr2 = pd.Timedelta(2, unit='h') # Scale Radio Buttons for Zoom self.scaleDeltaArray = [sec30, min5, min30, hr1, hr2] self.rax = self.fig.add_axes([0.02, 0.7, 0.11, 0.15]) self.rax.set_title('Scale') self.check = RadioButtons(self.rax, self.labels) self.fig.canvas.mpl_connect('button_press_event', self.onclick) # Data Set Selection Check Box self.rcbax = self.fig.add_axes([0.02, 0.25, 0.11, 0.15]) self.rcbax.set_title('DataSet') self.checkBox = CheckButtons(self.rcbax, ('valuex', 'valuey', 'valuez', 'mag'), (True, True, True, True)) self.checkBox.on_clicked(self.oncheck) xs = np.linspace(15, 21, 200) horiz_line_dataz = np.array([0.41 for i in range(len(xs))]) self.rcbax.plot(xs, horiz_line_dataz, 'C3-') horiz_line_datam = np.array([0.26 for i in range(len(xs))]) self.rcbax.plot(xs, horiz_line_datam, 'C0-') horiz_line_datay = np.array([0.58 for i in range(len(xs))]) self.rcbax.plot(xs, horiz_line_datay, 'C2-') horiz_line_datax = np.array([0.73 for i in range(len(xs))]) self.rcbax.plot(xs, horiz_line_datax, 'C1-') self.check.on_clicked(self.changeScale) df["mag"] = np.sqrt(np.square(df["valuex"]) + np.square(df['valuey']) + \ np.square(df["valuez"]))-1.0 print("Normalize") ya_min = df.loc[:, "mag"].min() - 0.1 ya_max = df.loc[:, "mag"].max() + 0.1 print("y limit (max={:f},min={:f})".format(ya_max, ya_min)) # print("Normalized") # print(type(ya_min)) # print(ya_max) # print(df["mag"]) # df["mag"] = df["mag"] - 1 # subtract 1g #Convert epoch to datetime to see the right value print("Convert Date Time") print(df["epoch"].iloc[0]) print(df["epoch"].iloc[1]) if type == "DB": print("epoch type", df.dtypes[0]) df['epoch'] = pd.to_datetime(df['epoch'], unit='ms') elif type == "CSV": print("epoch type", df.dtypes[0]) if 'annotated' not in df.columns: df['epoch'] = pd.to_datetime(df['epoch'], unit='ms') else: df['epoch'] = pd.to_datetime(df['epoch']) print("epoch type after", df.dtypes[0]) # Adjusting epoch time to be on Eastern Time Zone df['epoch'] = df['epoch'].dt.tz_localize('UTC').dt.tz_convert( 'America/New_York') vepoch = df['epoch'] self.datevalues = vepoch.dt.to_pydatetime() if 'annotated' not in df.columns: charArr = np.chararray((len(df["mag"], )), unicode=True) charArr[:] = ' ' df["annotated"] = pd.DataFrame(charArr) df["colorHex"] = pd.DataFrame(charArr) # print(df["epoch"]) # print(type(df['epoch'][0])) #Definition of x-axis formatter of tick # self.hoursFmt = mdates.DateFormatter('%Y-%m-%d %H:%M:%S') #Week Month/Day H:M:S self.hoursFmt = mdates.DateFormatter('%a-%m-%d %H:%M:%S') self.HMSFmt = mdates.DateFormatter('%H:%M:%S') #Get total time of the data in hours startDate = df["epoch"].iloc[0] self.startDate = startDate endDate = df["epoch"].iloc[-1] totaltime = int(endDate.timestamp() * 1000) - \ int(startDate.timestamp() * 1000) total_hours = totaltime / 1000 / 3600 #totaltime in hours total_minutes = total_hours * 60 print("total hours {:f}".format(total_hours)) #create variable to track drag time self.timeTrack = 0 #create variable to track initial click xpos self.xpos = 0 self.df = df print("Creating Top Graph") self.ax = self.fig.add_subplot(2, 1, 1) # second axis print("Creating Bottom Graph") self.ax2 = self.fig.add_subplot(2, 1, 2) #adjust distance between toolbar and graph to give more space self.fig.subplots_adjust(bottom=0.145) #adjust distance between toolbar and graph if data is 24 hours or more if total_hours > 24: self.fig.subplots_adjust(hspace=1.3) print("Start Plot Setting") # Set default values for x aixs ##changed temporary pls ignore # self.ax.set_ylim(1, 1.1) # self.ax2.set_ylim(1, 1.1) #self.ax.set_ylim(0, 8) #self.ax2.set_ylim(0, 8) self.ax.set_xlabel("Time (H:M:S)") self.ax2.set_xlabel("Time (H:M:S)") self.ax.set_ylabel("Acceleration (g)") self.ax2.set_ylabel("Acceleration (g)") #self.ax.set_title('Scale') self.ax.set_ylim(ya_min, ya_max) self.ax2.set_ylim(ya_min, ya_max) bottomtitle = 'Magnitude Graph for Start Date ' + "{:%Y-%m-%d}".format( startDate) if self.dfCal is not None: bottomtitle = 'Calibrated ' + bottomtitle self.ax.set_title(bottomtitle) toptitle = 'Zoomed-In Graph for Start Date ' + "{:%Y-%m-%d}".format( startDate) if self.dfCal is not None: toptitle = 'Calibrated ' + toptitle self.ax2.set_title(toptitle) # self.ya_min = ya_min # self.ya_max = ya_max # start date # print(type(df["epoch"])) # print(len(df["epoch"])) # print(df["epoch"]) # print() # TODO we assume time is not empty but it could be good to chek empty start_time = df["epoch"].iloc[0] print("start time={v}".format(v=start_time)) # print(type(start_time)) if total_hours > 1: end_time = start_time + hr1 elif total_minutes > 30: end_time = start_time + min30 elif total_minutes > 5: end_time = start_time + min5 else: end_time = start_time + sec30 print("end time={v}".format(v=end_time)) # print(type(start_time)) #end_time = start_time + pd.Timedelta(hours=1) #print(end_time) self.startX = start_time self.endX = end_time # set axis 2 self.ax2.set_xlim(start_time, end_time) print("Set Limit") # print(type(df["epoch"].iloc[0])) mask = (df["epoch"] >= start_time) & (df["epoch"] <= end_time) print("Plotting") # print(mask) # print(df.loc[mask, "epoch"]) # plot t = time.time() self.plotTop, = self.ax.plot(df["epoch"].iloc[0:3], df["mag"].iloc[0:3], visible=True) self.plotTop.set_data(self.datevalues, df["mag"]) self.ax.relim() self.ax.autoscale_view(True, True, True) self.fig.canvas.draw() #self.ax.plot(df["epoch"], df["mag"]) elapsed = time.time() - t print("time taken to top graph magnitud was {:f}".format(elapsed)) # print(df.loc[mask, "epoch"]) print(len(df.loc[mask, "epoch"])) t = time.time() self.lm, = self.ax2.plot(df["epoch"].iloc[0:3], df["mag"].iloc[0:3], visible=True) self.lm.set_data(self.datevalues, df["mag"]) self.ax2.relim() self.ax2.autoscale_view(True, True, True) self.fig.canvas.draw() #self.lm, = self.ax2.plot(df["epoch"], df["mag"],visible=True) elapsed = time.time() - t print("time taken to bottom graph magnitud was {:f}".format(elapsed)) t = time.time() try: thread1 = myThread(1, "valuex", self) thread1.start() except: print("Error: unable to start thread") #self.lx, = self.ax2.plot(df["epoch"], df["valuex"],visible=False) elapsed = time.time() - t print("time taken to bottom graph valuex was {:f}".format(elapsed)) t = time.time() try: thread2 = myThread(2, "valuey", self) thread2.start() except: print("Error: unable to start thread") #self.ly, = self.ax2.plot(df["epoch"], df["valuey"],visible=False) elapsed = time.time() - t print("time taken to bottom graph valuey was {:f}".format(elapsed)) t = time.time() try: thread3 = myThread(3, "valuez", self) thread3.start() except: print("Error: unable to start thread") #self.lz, = self.ax2.plot(df["epoch"], df["valuez"],visible=False) elapsed = time.time() - t print("time taken to bottom graph valuez was {:f}".format(elapsed)) # x in the range tX = df.loc[mask, "epoch"] self.ax2.set_xlim(tX.iloc[0], tX.iloc[len(tX) - 1]) # self.ax.xaxis.set_major_formatter(self.hoursFmt) self.ax.xaxis.set_major_formatter(self.HMSFmt) self.ax.get_xaxis().set_major_locator(LinearLocator(numticks=12)) #setup format of ticks to Weekday month/day if data is 24 hours or more if total_hours > 24: self.ax.xaxis.set_major_formatter(self.hoursFmt) #Rotate labels on x-axis 45 degrees and set font size to 8 self.ax.tick_params(axis='x', labelsize=8, rotation=45) # self.ax.title("Magnitude") # self.ax2.plot(df["epoch"], df["valuex"], # color=self.colorChoose(255, 0, 255)) # self.ax2.plot(df["epoch"], df["valuey"], # color=self.colorChoose(255, 0, 0)) # self.ax2.plot(df["epoch"], df["valuez"], # color=self.colorChoose(255, 255, 0)) self.color = self.colorChoose(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) self.removedObj = None # self.ax.add_patch(rect) self.fig.canvas.draw() self.fig.canvas.mpl_connect('motion_notify_event', self.onmouseover) self.cursor = Cursor(self.ax, useblit=True, horizOn=False, color='red', linewidth=2) self.cursor.connect_event('button_press_event', self.cursorOnclick) self.currentDelta = pd.Timedelta(30, unit='s') self.ax2.xaxis.set_major_formatter(self.HMSFmt) self.ax2.get_xaxis().set_major_locator(LinearLocator(numticks=7)) #data text axes #self.txtax = self.fig.add_axes([0.75, 0.85, 0.11, 0.05]) #self.txtax.axis('off') self.ax.format_coord = self.format_coord if type == "CSV" and 'annotated' in df.columns: print("calling searchForAnnotation") self.searchForAnnotation() #self.txt = self.ax.text(0.8, 1.05, 'X,Y,Z Values', transform=self.ax.transAxes) #self.txt = self.txtax.text(0.05, 0.05, 'X,Y,Z Values', transform=self.txtax.transAxes) def format_coord(self, x, y): dateclickOn = mdates.num2date(x) idx = self.df['epoch'].searchsorted(dateclickOn) idx = idx - 1 #logger.debug("dateclickOn:%s idx=%s", dateclickOn,idx) vx = self.df['valuex'].values[idx][0] vy = self.df['valuey'].values[idx][0] vz = self.df['valuez'].values[idx][0] vm = self.df['mag'].values[idx][0] #print("d: {:%H:%M:%S} vx:{:1.5f} vy:{:1.5f} vz:{:1.5f}".format(dateclickOn,vx,vy,vz)) #self.txt.set_text('%H:%M:%S x=%1.3f, y=%1.3f, z=%1.3f' % (dateclickOn,vx, vy,vz)) return "{:%H:%M:%S} M:{:1.3f}".format(dateclickOn, vm) def readCalibration(self, filename): self.dfCal = pd.read_csv(filename) print('Results: %s', self.dfCal) print('title: %s', self.dfCal['Titles']) print('values: %s', self.dfCal['Values']) print('ZGain: %s', self.dfCal['Values'][1]) self.ZOffset = 0 self.ZGain = 1 self.YOffset = 2 self.YGain = 3 self.XOffset = 4 self.XGain = 5 def redrawAfterCalibrate(self): print("Computing new data") if self.dfCal is not None: self.df["valuex"] = np.subtract( self.df["valuex"], self.dfCal['Values'][ self.XOffset]) / self.dfCal['Values'][self.XGain] self.df["valuey"] = np.subtract( self.df["valuey"], self.dfCal['Values'][ self.YOffset]) / self.dfCal['Values'][self.YGain] self.df["valuez"] = np.subtract( self.df["valuez"], self.dfCal['Values'][ self.ZOffset]) / self.dfCal['Values'][self.ZGain] print("Computing new mag") self.df["mag"] = np.sqrt(np.square(self.df["valuex"]) + np.square(self.df['valuey']) + \ np.square(self.df["valuez"]))-1.0 print("setting titles") bottomtitle = 'Magnitude Graph for Start Date ' + "{:%Y-%m-%d}".format( self.startDate) if self.dfCal is not None: bottomtitle = 'Calibrated ' + bottomtitle self.ax.set_title(bottomtitle) toptitle = 'Zoomed-In Graph for Start Date ' + "{:%Y-%m-%d}".format( self.startDate) if self.dfCal is not None: toptitle = 'Calibrated ' + toptitle self.ax2.set_title(toptitle) print("redraw graph") self.plotTop.set_ydata(self.df["mag"]) self.lm.set_ydata(self.df["mag"]) self.lx.set_ydata(self.df["valuex"]) self.ly.set_ydata(self.df["valuey"]) self.lz.set_ydata(self.df["valuez"]) self.fig.canvas.draw() def onmouseover(self, event): if event.inaxes == self.rax: None #logger.debug("click on radiobutton") #self.check._clicked(event) if event.inaxes == self.ax: self.cursor.onmove(event) def searchOpenCalibration(self, filename): directory = os.path.dirname(filename) print("directory=" + directory) tosearch = re.compile('^calibrationResults.*') for sChild in os.listdir(directory): print("file=" + sChild) if tosearch.match(sChild) is not None: if directory == "": directory = "." calibfilename = directory + "/" + sChild print("calibfilename=" + calibfilename) self.readCalibration(calibfilename) break def searchForAnnotation(self): colorKick = self.colorChoose(255, 0, 0) colorSleep = self.colorChoose(0, 0, 255) colorRandom = self.colorChoose(0, 255, 0) randomColor = self.color maskKick = self.df['annotated'] == "Kick" maskSleep = self.df['annotated'] == "Sleep" maskRandom = self.df['annotated'] == "Random" maskColorRandom = self.df['annotated'] == "annotated" print("masked found") if len(self.df.loc[maskKick, 'epoch']) > 0: self.reDrawAnnotation(maskKick, colorKick) if len(self.df.loc[maskSleep, 'epoch']) > 0: self.reDrawAnnotation(maskSleep, colorSleep) if len(self.df.loc[maskRandom, 'epoch']) > 0: self.reDrawAnnotation(maskRandom, colorRandom) if len(self.df.loc[maskColorRandom, 'epoch']) > 0: self.reDrawAnnotation(maskColorRandom, randomColor) def reDrawAnnotation(self, mask, color): [ymin, ymax] = self.ax.get_ylim() height = ymax - ymin positionKick = np.array(self.df.index[mask].tolist()) diff = np.subtract(positionKick[1:-1], positionKick[0:-2]) maskcuts = diff > 1 posInCuts = np.where(maskcuts) arrposcut = posInCuts[0] arrpostcut1 = arrposcut + 1 xmin = self.df.loc[mask, 'epoch'].iloc[0] xmax = self.df.loc[mask, 'epoch'].iloc[-1] xmin = xmin.to_pydatetime() xmax = xmax.to_pydatetime() #xmin=int(xmin.timestamp() * 1000)/1000 #xmax=int(xmax.timestamp() * 1000)/1000 print("xmin={v1} xmax={v2}".format(v1=xmin, v2=xmax)) xmin = mdates.date2num(xmin) xmax = mdates.date2num(xmax) print("len(posInCuts)", len(posInCuts[0])) print("xmin={v1} xmax={v2}".format(v1=xmin, v2=xmax)) for idx in range(len(posInCuts[0])): print("idx=", idx) print("xmin={v1} xmax={v2}".format( v1=xmin, v2=self.df['epoch'].iloc[positionKick[arrposcut[idx]]])) rectmin = xmin rectmax = self.df['epoch'].iloc[positionKick[arrposcut[idx]]] rectmax = rectmax.to_pydatetime() rectmax = mdates.date2num(rectmax) #rectmax=int(rectmax.timestamp() * 1000)/1000 #rectmax=mdates.num2date(rectmax) width = rectmax - rectmin rect = ECERectangle(rectmin, ymin, width, height, color=color, index=1) rect2 = ECERectangle(xmin, ymin, width, height, color=color, index=2) rect.setNext(rect2) rect2.setPrev(rect) self.ax.add_patch(rect) self.ax2.add_patch(rect2) self.fig.canvas.draw() xmin = self.df['epoch'].iloc[positionKick[arrpostcut1[idx]]] xmin = xmin.to_pydatetime() xmin = mdates.date2num(xmin) #xmin=int(xmin.timestamp() * 1000)/1000 #xmin=mdates.num2date(xmin) rectmin = xmin rectmax = xmax #rectmax=int(rectmax.timestamp() * 1000)/1000 width = rectmax - rectmin rect = ECERectangle(rectmin, ymin, width, height, color=color, index=1) rect2 = ECERectangle(xmin, ymin, width, height, color=color, index=2) rect.setNext(rect2) rect2.setPrev(rect) self.ax.add_patch(rect) self.ax2.add_patch(rect2) print("xmin={v1} xmax={v2}".format(v1=xmin, v2=xmax)) self.fig.canvas.draw() def getConnection(self, filename): """ get the connection of sqlite given file name :param filename: :return: sqlite3 connection """ conn = sqlite3.connect(filename) return conn def readCalibration(self, filename): self.dfCal = pd.read_csv(filename) print('Results: %s', self.dfCal) print('title: %s', self.dfCal['Titles']) print('values: %s', self.dfCal['Values']) print('ZGain: %s', self.dfCal['Values'][1]) self.ZOffset = 0 self.ZGain = 1 self.YOffset = 2 self.YGain = 3 self.XOffset = 4 self.XGain = 5 def createCalibrationAction(self): image_file = 'square-upload' callback = self.toolbar.importCal text = "Calibrate" tooltip_text = "Calibrate" a = self.toolbar.addAction(self.toolbar._icon(image_file + '.png'), text, callback) print("action created") self.toolbar._actions[callback] = a print("action added to array") a.setToolTip(tooltip_text) def executeQuery(self, query): return None def colorChoose(self, r, g, b): """ create a normalized color map give r, g, b in 0-255 scale :param r: red :param g: green :param b: blue :return: tuple of color that can be used for matplotlib """ if self.rangeOff(r) or self.rangeOff(g) or self.rangeOff(r): return (0, 0, 0) return r / 255.0, g / 255.0, b / 255.0 def rangeOff(self, val): return (val > 255) or (val < 0) def onpick(self, event): print("pick event") if isinstance(event.artist, Rectangle): patch = event.artist # patch.remove() self.removedObj = patch print('onpick1 patch:', patch.get_path()) else: print(event.artist.get_xdata()) print(event.artist.get_xdata()) print(len(event.artist.get_ydata())) # self.fig.canvas.draw() # def onselect(self, xmin, xmax): # print(xmin) # print(xmax) # width = xmax - xmin # [ymin, ymax] = self.ax.get_ylim() # height = ymax - ymin # # rect = matpat.Rectangle((xmin, ymin), width, height, # fill=True, alpha=0.4, # color=self.color) # self.ax.add_patch(rect) def onclick(self, event): if not self.toolbar._actions['zoom'].isChecked( ) and event.button == 1: # left = 1, middle = 2, right = 3 self.timeTrack = time.time() print("clicked {}".format(self.timeTrack)) print("clicked X: {}".format(event.xdata)) self.xpos = event.xdata # [ymin, ymax] = self.ax.get_ylim() # self.ax.annotate('sth', xy=(event.xdata, event.ydata), # xytext=(event.xdata, ymax), # arrowprops=dict(facecolor='black', shrink=0.05),) # self.fig.canvas.draw() def onrelease(self, event): if not self.toolbar._actions['zoom'].isChecked() \ and event.button == 1 and self.isAnnotate: #and not self.toolbar._actions['pan'].isChecked() \ curTime = time.time() if (curTime - self.timeTrack) > PRE_DEF_CLICK_TIME: print("dragged") xmin = self.xpos xmax = event.xdata width = xmax - xmin # print(xmin) # print(xmax) # print(type(xmin)) # print(mdates.num2date(xmin)) # print(mdates.num2date(xmax)) # print(type(xmax)) # print(width) [ymin, ymax] = self.ax.get_ylim() height = ymax - ymin #annotation problem temporary # print(any(cond)) self.create_annotation(xmin, xmax) # print(height) rect = ECERectangle(xmin, ymin, width, height, color=self.color, index=1) rect2 = ECERectangle(xmin, ymin, width, height, color=self.color, index=2) # Rectangle((xmin, ymin), width, height, # fill=True, alpha=0.4, # color=self.color, picker=True) rect.setNext(rect2) rect2.setPrev(rect) self.ax.add_patch(rect) self.ax2.add_patch(rect2) # self.ax2.add_patch(rect) self.fig.canvas.draw() def onpress(self, event): # print(event.key()) print(event.key) if event.key == 'r': self.annoteText = "Kick" self.color = self.colorChoose(255, 0, 0) elif event.key == 'b': self.annoteText = "Sleep" self.color = self.colorChoose(0, 0, 255) elif event.key == 'g': self.annoteText = "Random" self.color = self.colorChoose(0, 255, 0) elif event.key == "right": print("right pressed") self.startX += self.currentDelta self.endX += self.currentDelta self.ax2.set_xlim(self.startX, self.endX) # mask = (self.df["epoch"] >= self.startX) & \ # (self.df["epoch"] <= self.endX) # self.ax2.plot(self.df.loc[mask, "epoch"], # self.df.loc[mask, "mag"], '#1f77b4') self.run() elif event.key == "left": # TODO check left and right limit print("left pressed") self.startX -= self.currentDelta self.endX -= self.currentDelta self.ax2.set_xlim(self.startX, self.endX) # mask = (self.df["epoch"] >= self.startX) & \ # (self.df["epoch"] <= self.endX) # self.ax2.plot(self.df.loc[mask, "epoch"], # self.df.loc[mask, "mag"], '#1f77b4') self.run() elif event.key == "delete": print("delete") if self.removedObj is not None: if isinstance(self.removedObj, ECERectangle): print("deleting ECERect") rect_min_x = self.removedObj.get_x() rect_max_x = rect_min_x + self.removedObj.get_width() self.remove_annotation(rect_min_x, rect_max_x) ind = self.removedObj.getIndex() self.removedObj.remove() if ind == 1: nextRect = self.removedObj.getNext() else: nextRect = self.removedObj.getPrev() self.removedObj = None # print(type()) if nextRect.axes is not None: nextRect.remove() self.fig.canvas.draw() def run(self): self.fig.canvas.draw() def create_annotation(self, xmin, xmax): print("Annoated") cond = self.get_x_in_ranges(xmin, xmax) self.df.loc[cond, 'annotated'] = self.annoteText self.df.loc[cond, 'colorHex'] = self.get_color_in_hex() def remove_annotation(self, xmin, xmax): cond = self.get_x_in_ranges(xmin, xmax) self.df.loc[cond, 'annotated'] = " " self.df.loc[cond, 'colorHex'] = " " def get_x_in_ranges(self, xmin, xmax): return (self.df["epoch"] <= mdates.num2date(xmax)) \ & (self.df["epoch"] >= mdates.num2date(xmin)) def get_color_in_hex(self): return "#{:02X}{:02X}{:02X}".format(int(self.color[0] * 255), int(self.color[1] * 255), int(self.color[2] * 255)) def setSelect(self): self.isAnnotate = not self.isAnnotate def cursorOnclick(self, event): 'on button press we will see if the mouse is over us ' if (self.ax == event.inaxes) and (not self.isAnnotate): xdata = event.xdata dateclicked = mdates.num2date(xdata) self.startX = dateclicked dateEnd = dateclicked + self.currentDelta self.endX = dateEnd # df2,epoch,endDate=self.createBottomGraph(dateclicked) # mask = (df2["epoch"] >= dateclicked) & (df2["epoch"] <= endDate) # self.ax2.clear() # self.ax2.set_ylim(1, 1.1) # self.ax2.plot(df2.loc[mask, "epoch"], df2.loc[mask, "mag"]) mask = (self.df["epoch"] >= dateclicked) & (self.df["epoch"] <= dateEnd) tX = self.df.loc[mask, "epoch"] # print(tX) self.ax2.set_xlim(tX.iloc[0], tX.iloc[len(tX) - 1]) # locator = mdates.SecondLocator(interval=120) self.ax2.get_xaxis().set_major_locator(LinearLocator(numticks=12)) # self.ax2.xaxis.set_major_locator(locator) # self.ax2.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S')) # self.ax2.relim() self.ax2.autoscale_view(True, True, True) # # x_ticks = np.append(self.ax2.get_xticks(), mdates.date2num(df2.iloc[0,0])) # x_ticks = np.append(x_ticks,mdates.date2num(df2.iloc[-1,0])) #self.ax2.set_xticks(x_ticks) # #self.ax2.tick_params(axis='x', labelsize=8,rotation=45) # width = mdates.date2num(dateEnd) - xdata [ymin, ymax] = self.ax.get_ylim() height = ymax - ymin if self.rect is not None: self.rect.remove() self.rect = Rectangle((xdata, ymin), width, height, fill=True, alpha=0.4, color=(1, 0, 0), picker=False) self.ax.add_patch(self.rect) self.fig.canvas.draw() def oncheck(self, label): if label == 'valuex': self.lx.set_visible(not self.lx.get_visible()) elif label == 'valuey': self.ly.set_visible(not self.ly.get_visible()) elif label == 'valuez': self.lz.set_visible(not self.lz.get_visible()) elif label == 'mag': self.lm.set_visible(not self.lm.get_visible()) self.fig.canvas.draw() def changeScale(self, label): index = self.labels.index(label) self.currentDelta = self.scaleDeltaArray[index]