class viewer(graphplot): ''' Do: viewer = viewer(window) viewer.connect() To instantiate and use ''' def __init__(self, nodes_obj, window=None, figsize=None, idxlist=[], usekm=False): # window are the [xmin,xmax,ymin,ymax] dimensions of the viewer self.coordunits = "coords" if usekm: self.coordunits = "coords_km" # Create nodedict if nodes is a dataframe if "DataFrame" in str(type(nodes_obj)): nodedict = nodes_obj.to_dict("index") else: nodedict = nodes_obj self.figsize = (9, 9) if not figsize else figsize self.fig_main = plt.figure(figsize=self.figsize) self.grid = plt.GridSpec(2, 2, hspace=0.1, wspace=0.1, width_ratios=[2.2, 1], height_ratios=[0.8, 1]) self.axs = [self.fig_main.add_subplot(self.grid[:, 0])] self.axs.append(self.fig_main.add_subplot(self.grid[0, 1])) # Initialize parent graph object super().__init__(nodedict, idxlist, (self.figsize[0] * 0.75, self.figsize[1] * 0.75), self.fig_main, self.axs[0], usekm=usekm) self.axs[0].set_aspect('equal', adjustable='box', anchor="NW") # Draw main graph self.drawgraph() # Setup viewer if not window: # window = [self.xlims[0]+0.4*self.graphwidth, # self.xlims[0]+0.6*self.graphwidth, # self.ylims[0]+0.4*self.graphheight, # self.ylims[0]+0.6*self.graphheight] dw = 1. / long2km if usekm: dw = 1. window = [ self.xlims[0] + 0.5 * self.graphwidth - dw, self.xlims[0] + 0.5 * self.graphwidth + dw, self.ylims[0] + 0.5 * self.graphheight - dw, self.ylims[0] + 0.5 * self.graphheight + dw ] xmin, xmax, ymin, ymax = window[0], window[1], window[2], window[3] self.window = Rectangle((xmin, ymin), xmax - xmin, ymax - ymin, fill=False, linewidth=1.6, edgecolor="orangered") self.axs[0].add_patch(self.window) # Create inset axis xmin, ymin, xmax, ymax = self.get_rect_coords(self.window) self.axs[1].set_ylim(ymin, ymax) self.axs[1].set_xlim(xmin, xmax) self.axs[1].set_aspect('equal', adjustable='box', anchor="NE") self.axs[1].set_xticks([]) self.axs[1].set_yticks([]) self.press = None self.updateinset() def updateinset(self): # Remove any artists if present self.axs[1].clear() # Collect which nodes are included insetnodes = {} xmin, ymin, xmax, ymax = self.get_rect_coords(self.window) for key, node in self.nodes.items(): x, y = node[self.coordunits] if x > xmin and x < xmax and y > ymin and y < ymax: insetnodes.update({key: node}) self.plotnode(self.axs[1], x, y) edges = node["nbrs"] for e in edges: if e in self.idxlist: nbr = self.nodes[e][self.coordunits] self.plotLine(self.axs[1], x, y, nbr[0], nbr[1]) self.axs[1].set_ylim(ymin, ymax) self.axs[1].set_xlim(xmin, xmax) #self.axs[1].set_aspect('equal', adjustable='box',anchor="NE") self.axs[1].set_xticks([]) self.axs[1].set_yticks([]) # pickle.dump(self., file('myplot.pickle', 'w')) def connect(self): 'connect to all the events we need' self.cidpress = self.window.figure.canvas.mpl_connect( 'button_press_event', self.on_press) self.cidrelease = self.window.figure.canvas.mpl_connect( 'button_release_event', self.on_release) self.cidmotion = self.window.figure.canvas.mpl_connect( 'motion_notify_event', self.on_motion) def on_press(self, event): 'on button press we will see if the mouse is over us and store some data' if event.inaxes != self.window.axes: return contains, attrd = self.window.contains(event) self.window.set_x(event.xdata - self.window.get_width() * 0.5) self.window.set_y(event.ydata - self.window.get_height() * 0.5) if True: # Update attributes for motion print('event contains', self.window.xy) x0, y0 = self.window.xy self.press = x0, y0, event.xdata, event.ydata def on_motion(self, event): 'on motion we will move the window if the mouse is over us' if self.press is None: return if event.inaxes != self.window.axes: return x0, y0, xpress, ypress = self.press dx = event.xdata - xpress dy = event.ydata - ypress #print('x0=%f, xpress=%f, event.xdata=%f, dx=%f, x0+dx=%f' % # (x0, xpress, event.xdata, dx, x0+dx)) self.window.set_x(x0 + dx) self.window.set_y(y0 + dy) self.window.figure.canvas.draw() def on_release(self, event): 'on release we reset the press data' self.press = None self.updateinset() self.window.figure.canvas.draw() def disconnect(self): 'disconnect all the stored connection ids' self.window.figure.canvas.mpl_disconnect(self.cidpress) self.window.figure.canvas.mpl_disconnect(self.cidrelease) self.window.figure.canvas.mpl_disconnect(self.cidmotion)
class RectROI(ROI): def __init__(self, ax, fig, canvas, red=0.5, green=0.5, blue=0.5): ROI.__init__(self, ax, fig, canvas) self.x0 = 0 self.y0 = 0 self.x1 = 0 self.y1 = 0 self.line_color = (red, green, blue) self.rect = None return def button_press_callback(self, event): if event.inaxes: if event.button == 1: # If you press the left mouse button if self.rect is None: self.x0 = event.xdata self.y0 = event.ydata return def button_release_callback(self, event): # When the user releases the mouse button, make sure the ROI line # no longer moves with the mouse. if event.button == 1: if self.rect is None: self.x1 = event.xdata self.y1 = event.ydata width = self.x1 - self.x0 height = self.y1 - self.y0 self.rect = Rectangle((self.x0, self.y0), width, height, color=self.line_color, fill=False, picker=True, visible=True, figure=self.fig) ax = event.inaxes ax.add_patch(self.rect) self.fig.canvas.draw() self.grab_line = None return def motion_notify_callback(self, event): ''' This is called when the user moves the mouse over the plot. It will change the size or position of the ROI. ''' if event.inaxes: if (event.button == 1) and (not (self.grab_line is None)): # Change the position of the bottom right corner of the ROI # as the mouse is dragged across the image. self.x1 = event.xdata self.y1 = event.ydata width = self.x1 - self.x0 height = self.y1 - self.y0 self.rect.set_width(width) self.rect.set_height(height) self.fig.canvas.draw() if (event.button == 3) and (not (self.grab_line is None)): # Change the position of the top left corner of the ROI # as the mouse is dragged across the image. self.x0 = event.xdata self.y0 = event.ydata self.rect.set_xy((self.x0, self.y0)) self.fig.canvas.draw() return def object_picked_callback(self, event): # Set the line grabbed to the object that is clicked on. contains, attrd = self.rect.contains(event.mouseevent) if contains: self.grab_line = event.artist return def AddLines(self): if self.rect is not None: self.ax.add_patch(self.rect) self.ax.figure.canvas.draw() return def RemoveLines(self): try: self.rect.remove() self.fig.canvas.draw() except AttributeError: return return def GetDimensions(self): dim_list = [] dim_list.append(self.x0) dim_list.append(self.y0) dim_list.append(self.x1) dim_list.append(self.y1) return dim_list def EditROI(self): if self.x0 == 0 or self.y0 == 0 or self.x1 == 0 or self.y1 == 0: return width = self.x0 - self.x1 height = self.y0 - self.y1 if self.rect is None: self.rect = Rectangle((self.x1, self.y1), width, height, color=self.line_color, fill=False, picker=True, visible=True, figure=self.fig, axes=self.fig.gca()) ax = self.fig.gca() ax.add_patch(self.rect) else: self.rect.set_height(height) self.rect.set_width(width) self.rect.set_xy((self.x1, self.y1)) self.fig.canvas.draw() return def SetXY(self, x, y, corner=1): if corner == 1: self.x0 = x self.y0 = y else: self.x1 = x self.y1 = y return
class DraggableRectangle(object): def __init__(self, axes, rectwidth=100, lrwidth=10, recty=100): self.ax = axes # plt.gca() self.rect_x = axes.get_xlim()[1] - rectwidth - lrwidth self.rect = Rectangle((self.rect_x, 0), rectwidth, recty, facecolor='g', alpha=0.2) self.rectl = Rectangle((self.rect_x - lrwidth, 0), lrwidth, recty, facecolor='b', alpha=0.8) self.rectr = Rectangle((self.rect_x + rectwidth, 0), lrwidth, recty, facecolor='b', alpha=0.8) self.ax.add_patch(self.rect) self.ax.add_patch(self.rectl) self.ax.add_patch(self.rectr) self.rect_width = rectwidth self.press = None # 0, self.pressl = None self.pressr = None self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press) self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release) self.ax.figure.canvas.mpl_connect('motion_notify_event', self.on_motion) def on_press(self, event): if event.inaxes != self.rect.axes: return contains, attrd = self.rect.contains(event) containsl, attrdl = self.rectl.contains(event) containsr, attrdr = self.rectr.contains(event) if contains: x0, y0 = self.rect.xy self.press = x0, y0, event.xdata, event.ydata elif containsl: xl, yl = self.rectl.xy self.pressl = xl, yl, event.xdata, event.ydata elif containsr: xr, yr = self.rectr.xy self.pressr = xr, yr, event.xdata, event.ydata else: return def on_release(self, event): # print ('release') self.press = None self.pressl = None self.pressr = None self.ax.figure.canvas.draw() def on_motion(self, event): 'on motion we will move the rect if the mouse is over us' if self.press is None and self.pressl is None and self.pressr is None: return if event.inaxes != self.rect.axes and event.inaxes != self.rectl.axes and event.inaxes != self.rectr.axes: return rectwidth = self.rect_width if self.press != None: x0, y0, xpress, ypress = self.press dx = event.xdata - xpress rectl_x = x0 + dx - self.rectl.get_width() # to tell the left edge overriding if rectl_x < 0: rectl_x = 0 # to tell the right edge overriding if rectl_x + self.rectl.get_width( ) + rectwidth + self.rectr.get_width() > self.ax.get_xlim()[1] - 1: rectl_x = self.ax.get_xlim( )[1] - rectwidth - self.rectr.get_width( ) - self.rectl.get_width() print("self.ax.get_xlim()[1]=", self.ax.get_xlim()[1], " rectLx=", rectl_x) self.rect_x = rectl_x + self.rectl.get_width() self.rect.set_x(self.rect_x) self.rectl.set_x(rectl_x) self.rectr.set_x(self.rect_x + self.rect.get_width()) # self.rect.figure.canvas.draw() elif self.pressl != None: if event.xdata >= self.rectr.get_x() - 10: self.rect_width = 10 return xl, yl, xpress, ypress = self.pressl dx = event.xdata - xpress rectl_x = xl + dx #- self.rectl.get_width() # to tell the left edge overriding if rectl_x < 0: self.rect_width = self.rectr.get_x() - self.rectl.get_width() rectl_x = 0 self.rectl.set_x(rectl_x) # draw rect self.rect.set_x(rectl_x + self.rectl.get_width()) xr = self.rectr.get_x() rectwidth = xr - rectl_x - self.rectl.get_width() # self.rectl.figure.canvas.draw() elif self.pressr != None: if event.xdata <= self.rectl.get_x() + self.rectl.get_width() + 10: self.rect_width = 10 return xr, yr, xpress, ypress = self.pressr dx = event.xdata - xpress rectr_x = xr + dx # to tell the right edge overriding xlim = self.ax.get_xlim()[1] print('xlim=', xlim) if rectr_x + self.rectr.get_width() > xlim: self.rect_width = xlim - self.rectl.get_x( ) - self.rectl.get_width() rectr_x = xlim - self.rectr.get_width() self.rectr.set_x(rectr_x) # draw rect rectwidth = rectr_x - self.rectl.get_x() - self.rectl.get_width() # self.rectr.figure.canvas.draw() self.rect.set_width(rectwidth) self.rect_width = rectwidth self.ax.figure.canvas.draw() # self.rectl.figure.canvas.draw() # why we just draw rect here, but rectl and rectr are drawed too ? def reset_rects(self, xl, xr): xlim = self.ax.get_xlim()[1] if xr > xlim: return if xl < 0: return self.rectl.set_x(xl) self.rectr.set_x(xr - self.rectr.get_width()) self.rect_x = xl + self.rectl.get_width() self.rect.set_x(self.rect_x) self.rect_width = xr - self.rectr.get_width() - self.rect_x self.rect.set_width(self.rect_width) self.ax.figure.canvas.draw() def is_pressed(self): r = 0 if self.press: r = 1 elif self.pressl: r = 2 elif self.pressr: r = 3 else: r = 0 return r def get_rect_x1(self): return int(self.rectl.get_x()) def get_rect_x2(self): return int(self.rectr.get_x() + self.rectr.get_width() - 1)
class ClickableRectangle: def __init__(self, fig, ax, colors_cube, f, i_f, j_f): self.f = f self.i_f = i_f self.j_f = j_f self.colors_cube = colors_cube self.x = i_f self.y = j_f self.color = self.colors_cube[f, i_f, j_f] self.fig = fig self.ax = ax self.rect = Rectangle( (self.x, self.y), width=1, height=1, facecolor=self.color, linewidth=2, edgecolor='k', picker=True ) self.patch = self.ax.add_patch(self.rect) self.active = False clicker = self.fig.canvas.mpl_connect( 'button_press_event', lambda e: self.onclick(e)) presser = self.fig.canvas.mpl_connect( 'key_press_event', lambda e: self.keypress(e)) def onclick(self, event): # Was the click on the same axis as the rectangle? if event.inaxes != self.rect.axes: self.active = False return # Was the click inside the rectangle? contains, attrd = self.rect.contains(event) if not contains: self.active = False return # Only concerned with double click events if event.dblclick: # Set active self.active = True def keypress(self, event): if not self.active: return elif event.key in ['w', 'W']: self.color = 'white' elif event.key in ['o', 'O']: self.color = 'orange' elif event.key in ['r', 'R']: self.color = 'red' elif event.key in ['g', 'G']: self.color = 'green' elif event.key in ['b', 'B']: self.color = 'blue' elif event.key in ['y', 'Y']: self.color = 'yellow' elif event.key == 'enter': self.active = False self.colors_cube[self.f, self.i_f, self.j_f] = self.color self.patch.set_facecolor(self.color) self.fig.canvas.draw()