def __init__(self, stop_list): pygame.init() self.stop_list = stop_list self.screen = pygame.display.set_mode(SIZE) pygame.display.set_caption("bus-stop") self.set_screen() self.clock = pygame.time.Clock() self.all_bus = pygame.sprite.Group() self.legend = Legend() self.running = True
def __init__(self, data=None, **kwtraits): if 'origin' in kwtraits: self.default_origin = kwtraits.pop('origin') if "title" in kwtraits: title = kwtraits.pop("title") else: title = None super(Plot, self).__init__(**kwtraits) if data is not None: if isinstance(data, AbstractPlotData): self.data = data elif type(data) in (ndarray, tuple, list): self.data = ArrayPlotData(data) else: raise ValueError, "Don't know how to create PlotData for data" \ "of type " + str(type(data)) if not self._title: self._title = PlotLabel(font="swiss 16", visible=False, overlay_position="top", component=self) if title is not None: self.title = title if not self.legend: self.legend = Legend(visible=False, align="ur", error_icon="blank", padding=10, component=self) # ensure that we only get displayed once by new_window() self._plot_ui_info = None return
def legend(self, handles, labels, loc, **kwargs): """ Place a legend in the figure. Labels are a sequence of strings, handles is a sequence of line or patch instances, and loc can be a string or an integer specifying the legend location USAGE: legend( (line1, line2, line3), ('label1', 'label2', 'label3'), 'upper right') The LOC location codes are 'best' : 0, (currently not supported, defaults to upper right) 'upper right' : 1, (default) 'upper left' : 2, 'lower left' : 3, 'lower right' : 4, 'right' : 5, 'center left' : 6, 'center right' : 7, 'lower center' : 8, 'upper center' : 9, 'center' : 10, The legend instance is returned """ handles = flatten(handles) l = Legend(self, handles, labels, loc, isaxes=False, **kwargs) self._set_artist_props(l) self.legends.append(l) return l
def __init__(self, figure, axis=None): """ Create the drawable with the figure. :param figure: The figure that the :class:`~Drawable` class wraps. This is mainly used to get the figure renderer. :type figure: :class:`matplotlib.figure.Figure` :param axis: The axis (or subplot) where to plot visualizations. If `None` is not given, the plot's main subplot is used instead. :type axis: `None` or :class:`matplotlib.axis.Axis` """ self.figure = figure self.axis = plt.gca() if axis is None else axis self.caption = Annotation(self) self.annotations = [] self.legend = Legend(self) self.timeseries = None
def createLegendWidget(self): # Create the map legend widget and associate to the canvas """ self.legend = Legend(self) self.legend.setObjectName("theMapLegend") self.LegendDock = QDockWidget("Layers", self) self.LegendDock.setObjectName("legend") self.LegendDock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.LegendDock.setWidget(self.legend) self.LegendDock.setContentsMargins(9, 9, 9, 9) self.addDockWidget(Qt.LeftDockWidgetArea, self.LegendDock)
def legend(self, handles, labels, *args, **kwargs): """ Place a legend in the figure. Labels are a sequence of strings, handles is a sequence of line or patch instances, and loc can be a string or an integer specifying the legend location USAGE: legend( (line1, line2, line3), ('label1', 'label2', 'label3'), 'upper right') The LOC location codes are 'best' : 0, (currently not supported for figure legends) 'upper right' : 1, 'upper left' : 2, 'lower left' : 3, 'lower right' : 4, 'right' : 5, 'center left' : 6, 'center right' : 7, 'lower center' : 8, 'upper center' : 9, 'center' : 10, loc can also be an (x,y) tuple in figure coords, which specifies the lower left of the legend box. figure coords are (0,0) is the left, bottom of the figure and 1,1 is the right, top. The legend instance is returned. The following kwargs are supported: loc = "upper right" # numpoints = 4 # the number of points in the legend line prop = FontProperties(size='smaller') # the font property pad = 0.2 # the fractional whitespace inside the legend border markerscale = 0.6 # the relative size of legend markers vs. original shadow # if True, draw a shadow behind legend labelsep = 0.005 # the vertical space between the legend entries handlelen = 0.05 # the length of the legend lines handletextsep = 0.02 # the space between the legend line and legend text axespad = 0.02 # the border between the axes and legend edge """ handles = flatten(handles) l = Legend(self, handles, labels, *args, **kwargs) self._set_artist_props(l) self.legends.append(l) return l
class Window: def __init__(self, stop_list): pygame.init() self.stop_list = stop_list self.screen = pygame.display.set_mode(SIZE) pygame.display.set_caption("bus-stop") self.set_screen() self.clock = pygame.time.Clock() self.all_bus = pygame.sprite.Group() self.legend = Legend() self.running = True def draw_stop(self, stop): pos = stop.pos pygame.draw.circle(self.bground, (200, 0, 20), pos, RAD_STOP) def draw_line(self, p1, p2, color): pygame.draw.line(self.bground, color, p1, p2) def add_bus(self, bus): b = sprite.Bus(bus) self.all_bus.add(b) def set_screen(self): self.screen.fill((0, 0, 0)) self.bground = self.screen.copy() for stop in self.stop_list: self.draw_stop(stop) pygame.display.flip() def loop(self): while self.running: self.clock.tick(60) self.screen.blit(self.bground, (0, 0)) self.legend.draw(self.screen) self.all_bus.update() self.all_bus.draw(self.screen) pygame.display.flip()
def add_legend_to_map(the_map): """Add a default legend to a folium map. Parameters ---------- the_map : object like folium.folium.Map The map we are to add the tiles to. """ labels = [] for key, val in COLORS_PARTY.items(): labels.append({'text': key, 'color': val, 'opacity': OPACITY}) legend = Legend(title='Partier', labels=labels) the_map.add_child(legend)
def __init__(self): self.img = None self.legend = Legend() self.title = Title() self.autosize = App.cfg['image']['autosize'] self.autosize_minpx = App.cfg['image']['autosize_minpx'] self.basewidth = App.cfg['image']['basewidth'] self.baseheight = App.cfg['image']['baseheight'] self.padding = App.cfg['image']['padding'] self.showborder = App.cfg['image']['border'] self.showtitle = App.cfg['title']['show'] self.showlegend = App.cfg['legend']['show'] self.showgrid = App.cfg['axis']['grid'] self.showaxis = App.cfg['axis']['show'] self.showflat = App.cfg['image']['flatline'] self.showtimestamp = App.cfg['image']['timestamp'] self.timestampcolor = App.cfg['image']['timestampcolor'] self.bordercolor = App.cfg['image']['bordercolor'] self.streetcolor = App.cfg['image']['streetcolor'] self.flatcolor = App.cfg['image']['flatcolor'] self.gridcolor = App.cfg['axis']['gridcolor'] self.axiscolor = App.cfg['axis']['color'] self.axisfont = App.cfg['axis']['font'] self.bgcolor = App.cfg['image']['bgcolor']
def __init__(self, json_obj): self.title = json_obj["title"] self.label = json_obj["label"] self.legend = [] for legend in json_obj["legend"]: self.legend.append(Legend(legend)) self.rows = json_obj["rows"] self.columns = json_obj["columns"] self.bars = [] for bar in json_obj["bars"]: self.bars.append(Bar(bar)) self.bar_height = json_obj["bar_height"]
def processTrackerGGLegendData(o, all_legend_names): legend_data = list() #process reponse data for legend in all_legend_names: #set default kills to 0 kills = 0; for item in o["data"]: #if legend name matches a player data legend name then attempt to get kill data if item.get("metadata").get("name") == legend: #if kill data is available then set value of kills if item.get("stats") and item.get("stats").get("kills") and item.get("stats").get("kills").get("value"): kills = int(item["stats"]["kills"]["value"]) #initialise Legend object for each legend using name and kills as parameters. legend_data.append(Legend(legend, kills)) pass return legend_data
def update(self, string): QObjectCleanupHandler().add(self.layout()) scroll_bar_position = self.scroll.horizontalScrollBar().value() for widgets in self.findChildren(QWidget): widgets.deleteLater() dictionary = json.loads(string) credit = dictionary["creditos"] dictionary = dictionary["malla"] hbox = QHBoxLayout() legend = Legend() hbox.addWidget(legend) for semester_number in [ str(index) for index in range(1, len(dictionary) + 1) ]: height = self.geometry().height() * semester_height_scale semester = Semester(number=semester_number, credit=credit[semester_number], height=height) semester.add_subjects(dictionary[semester_number]) hbox.addWidget(semester) semester.send_semester_update.connect(self.receive_semester_update) semester.send_subject_state_update.connect( self.receive_subject_state_update) groupBox = QGroupBox() groupBox.setLayout(hbox) self.scroll = QScrollArea() self.scroll.setWidget(groupBox) self.scroll.setWidgetResizable(True) self.scroll.horizontalScrollBar().setValue(scroll_bar_position) hbox = QHBoxLayout() hbox.addWidget(self.scroll) self.setLayout(hbox)
def addLegend (self, pairs): if len (pairs) == 0: return """self.leg = Legend (width = self.settings.width, textHeight = self.settings.legendTextHeight) keys = pairs.keys () keys.sort () for k in keys: self.leg.addKey (k, pairs[k]) self.leg.y = self.settings.height - self.leg.height self.canvas.move (0, self.leg.height + 20) self.canvas.changeSize (0, -(self.leg.height + 20)) self.draw (self.leg)""" self.leg = Legend (width = self.settings.width / 4.0, textHeight = self.settings.legendTextHeight) keys = pairs.keys () keys.sort () for k in keys: self.leg.addKey (k, pairs[k]) self.canvas.changeSize (-self.leg.width, 0) self.leg.y = self.settings.height - (self.canvas.height + self.canvas.y) self.leg.x = self.canvas.x + self.canvas.width #self.leg.y = self.settings.height - self.leg.height #self.canvas.move (0, self.leg.height + 20) self.draw (self.leg)
def __init__(self, *args, **kwargs): """ Sets up the UI, binds the events etc """ super(French75, self).__init__(*args, **kwargs) #WorldState.Instance() = WorldState.Instance() self.Maximize() (WorldState.Instance().dispW, WorldState.Instance().dispH) = self.GetSize() splitter_far_right = wx.SplitterWindow(self, -1) splitter_horiz_middle = wx.SplitterWindow(splitter_far_right, -1) splitter_vert_middle = wx.SplitterWindow(splitter_horiz_middle) splitter_far_right_middle = wx.SplitterWindow(splitter_far_right, -1) self.legend_panel = scrolled.ScrolledPanel(splitter_vert_middle, -1) self.graph_panel = wx.Panel(splitter_vert_middle, -1) self.legend_panel.SetBackgroundColour(_BG_COLOUR) self.graph_panel.SetBackgroundColour(_BG_COLOUR) """ Animation Panel Setup """ self.animation_panel = scrolled.ScrolledPanel(splitter_horiz_middle, -1) self.animation_panel.SetBackgroundColour(_BG_COLOUR) self.btn_animate_play = wx.Button(self.animation_panel, -1, 'Play') self.btn_animate_play.Bind(wx.EVT_BUTTON, self.play_animation) self.slider_time = wx.Slider( self.animation_panel, -1, value=0, minValue=0, maxValue=WorldState.Instance().session_dict['max_time'], style=wx.SL_AUTOTICKS | wx.SL_HORIZONTAL | wx.SL_LABELS) self.slider_time.Bind(wx.EVT_SLIDER, self.move_animation) self.slider_time.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.released_slider) self.drop_down_species = wx.ComboBox(self.animation_panel, -1, style=wx.CB_READONLY) self.drop_down_species.Bind(wx.wx.EVT_COMBOBOX, self.change_animation_species) self.switch_animation_button = wx.Button(self.animation_panel, -1, "<->") self.switch_animation_button.Bind(wx.EVT_BUTTON, self.switch_animation) self.drop_down_files = wx.ComboBox(self.animation_panel, -1, style=wx.CB_READONLY) self.drop_down_files.Bind(wx.wx.EVT_COMBOBOX, self.change_animation_file) line1 = wx.StaticLine(self.animation_panel, -1, style=wx.LI_VERTICAL) line3 = wx.StaticLine(self.animation_panel, -1, style=wx.LI_VERTICAL) line2 = wx.StaticLine(self.animation_panel) animation_vbox = wx.BoxSizer(wx.VERTICAL) animation_hbox = wx.BoxSizer(wx.HORIZONTAL) self.animation_panels_hbox = wx.BoxSizer(wx.HORIZONTAL) animation_hbox.Add(self.drop_down_files, 0, wx.ALL, 10) animation_hbox.Add(self.switch_animation_button, 0, wx.TOP, 12) animation_hbox.Add(self.drop_down_species, 0, wx.ALL, 10) animation_hbox.Add(line1, 0, wx.EXPAND | wx.ALL, 5) animation_hbox.Add(self.btn_animate_play, 0, wx.TOP, 12) animation_hbox.Add(line3, 0, wx.EXPAND | wx.ALL, 5) animation_hbox.Add(self.slider_time, 0, wx.BOTTOM | wx.EXPAND | wx.ALIGN_RIGHT, 5) animation_vbox.Add(animation_hbox, 0) animation_vbox.Add(line2, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 10) animation_vbox.Add(self.animation_panels_hbox) self.animation_panel.SetSizer(animation_vbox) self.animation_panel.Layout() self.animation_panel.SetupScrolling(scroll_y=False) """ Attached Files Panel Setup """ attachment_panel = wx.Panel(splitter_far_right_middle, -1) attachment_panel.SetBackgroundColour(_BG_COLOUR) attached_files_vbox = wx.BoxSizer(wx.VERTICAL) attached_file_toolbar = wx.BoxSizer(wx.HORIZONTAL) attached_label = wx.StaticText(attachment_panel, -1, "Attached Files:") attached_files_vbox.Add(attached_label, 0, wx.EXPAND | wx.TOP | wx.LEFT, 5) self.attached_file_list = wx.ListBox(attachment_panel, -1, size=(300, 300)) attached_files_vbox.Add(self.attached_file_list) self.add_files_button = wx.Button(attachment_panel, -1, "Add") self.add_files_button.Bind(wx.EVT_BUTTON, self.attach_file) self.open_files_button = wx.Button(attachment_panel, -1, "Open") self.open_files_button.Bind(wx.EVT_BUTTON, self.open_attached_file) attached_file_toolbar.Add(self.add_files_button, 0, wx.ALL, 5) attached_file_toolbar.Add(self.open_files_button, 0, wx.ALL, 5) attached_files_vbox.Add(attached_file_toolbar, 0, wx.ALL | wx.ALIGN_CENTRE, 5) attachment_panel.SetSizer(attached_files_vbox) attached_files_vbox.Fit(self) """ Animation Annotations Panel Setup """ annotation_panel = wx.Panel(splitter_far_right_middle, -1) annotation_panel.SetBackgroundColour(_BG_COLOUR) anime_annotations_vbox = wx.BoxSizer(wx.VERTICAL) anime_annotations_toolbar = wx.BoxSizer(wx.HORIZONTAL) anime_annotations_label = wx.StaticText(annotation_panel, -1, "Animation Annotations:") anime_annotations_vbox.Add(anime_annotations_label, 0, wx.EXPAND | wx.TOP | wx.LEFT, 5) self.anime_annotations_list = wx.ListBox(annotation_panel, -1, size=(300, 300)) anime_annotations_vbox.Add(self.anime_annotations_list) self.add_anime_annotation_button = wx.Button(annotation_panel, -1, "Add") self.add_anime_annotation_button.Bind(wx.EVT_BUTTON, self.add_anime_annotation) self.delete_anime_annotation_button = wx.Button( annotation_panel, -1, "Remove") self.delete_anime_annotation_button.Bind(wx.EVT_BUTTON, self.remove_anime_annotation) anime_annotations_toolbar.Add(self.add_anime_annotation_button, 0, wx.ALL, 5) anime_annotations_toolbar.Add(self.delete_anime_annotation_button, 0, wx.ALL, 5) anime_annotations_vbox.Add(anime_annotations_toolbar, 0, wx.ALL | wx.ALIGN_CENTRE, 5) annotation_panel.SetSizer(anime_annotations_vbox) anime_annotations_vbox.Fit(self) """ Graph Panel Setup """ (graph_width, graph_height) = calc_graph_size(_DPI, _COLS, _NUM_OF_SIDEBARS, _PHI) graph_fig = Figure((graph_width, graph_height)) graph_fig.set_facecolor('white') self.graph_canvas = FigCanvas(self.graph_panel, -1, graph_fig) self.graph_axes = graph_fig.add_subplot(111) graph_vbox = wx.BoxSizer(wx.VERTICAL) graph_vbox.Add(self.graph_canvas) self.toolbar = BioPepaToolbar(self.graph_canvas) (toolW, toolH) = self.toolbar.GetSizeTuple() graph_vbox.Add(self.toolbar) self.graph_panel.SetSizer(graph_vbox) graph_vbox.Fit(self) self.graph_canvas.mpl_connect('button_press_event', self.onclick) self.graph_canvas.mpl_connect('motion_notify_event', self.move_mouse) """ Legend Panel Setup """ title = wx.StaticText(self.legend_panel, wx.ID_ANY, 'Legend', style=wx.ALIGN_LEFT) font = wx.Font(28, wx.DEFAULT, wx.NORMAL, wx.BOLD) title.SetFont(font) vbox_leg = wx.BoxSizer(wx.VERTICAL) vbox_leg.Add(title, flag=wx.CENTER) self.legend_panel.SetSizer(vbox_leg) self.legend_panel.Layout() """ Menubar Setup """ self.SetMenuBar(self.build_menu_bar()) """ WorldState Stuff """ WorldState.Instance().drop_down_species = self.drop_down_species WorldState.Instance().drop_down_files = self.drop_down_files WorldState.Instance( ).create_cell_segments_by_species = self.create_cell_segments_by_species WorldState.Instance( ).create_cell_segments_by_file = self.create_cell_segments_by_file WorldState.Instance().graph_canvas = self.graph_canvas WorldState.Instance().play_animation = self.real_play_animation WorldState.Instance( ).create_cell_segments_by_file = self.create_cell_segments_by_file WorldState.Instance( ).create_cell_segments_by_species = self.create_cell_segments_by_species WorldState.Instance().time_slider = self.slider_time WorldState.Instance( ).anime_annotations_list = self.anime_annotations_list WorldState.Instance().graph_axes = self.graph_axes WorldState.Instance().graph_width = graph_width WorldState.Instance().graph_height = graph_height WorldState.Instance().legend = Legend(self.legend_panel) WorldState.Instance().update_title = self.SetTitle WorldState.Instance().get_title = self.GetTitle """ Whole UI Setup """ splitter_far_right.SplitVertically(splitter_horiz_middle, splitter_far_right_middle) splitter_horiz_middle.SplitHorizontally(splitter_vert_middle, self.animation_panel) splitter_vert_middle.SplitVertically(self.graph_panel, self.legend_panel) splitter_far_right_middle.SplitHorizontally(attachment_panel, annotation_panel) splitter_far_right.SetSashPosition((5 * WorldState.Instance().dispW / 6) + 10) splitter_vert_middle.SetSashPosition((4 * WorldState.Instance().dispW / 6) - 10) splitter_horiz_middle.SetSashPosition((graph_height * _DPI) + toolH + 5) splitter_far_right_middle.SetSashPosition(WorldState.Instance().dispH / 2) self.SetTitle(_TITLE) self.enable_all(False) self.filem_new_session.Enable(True) self.filem_load_session.Enable(True) self.Maximize()
if __name__ == "__main__": args = args() map_fp_lst = args.input_imgs print("Running mapping") # Open image files layers = [] for map_fp in map_fp_lst: im = Image.open(map_fp) print(map_fp, im.format, im.size, im.mode) layers.append(im) #im.show() # Get a legend for this map legend = Legend() if args.legend: legend = Legend(layers) else: print("Loading legend.json") legend = Legend.load('legend.json') if args.specials_dir: if not os.path.isdir(args.specials_dir): print("Cannot open directory of special composites: {}".format(args.specials_dir)) print("Skipping special composites") else: print("Using specials directory '{}'".format(args.specials_dir)) else: print("Skipping special composites")
class BaseGraph (PrintableCanvas): def __init__ (self, canvasType, **attr): if attr.has_key ('settings') and attr['settings']: self.applySettings (attr['settings']) else: self.formatSettings (blank ()) if attr.has_key ('width'): self.settings.width = attr['width'] if attr.has_key ('height'): self.settings.height = attr['height'] view = ViewBox (0, 0, self.settings.width, self.settings.height) PrintableCanvas.__init__ (self, viewBox = view, **attr) self.addScript (self.jsUtils ()) self.addScript (self.jsHighlight ()) self.dataGroup = Grouping () if canvasType: self.attachCanvas (canvasType, **attr) else: self.canvas = None self.draw (self.dataGroup) self.initialFormatting () def attachCanvas (self, canvasType, **attr): self.canvas = canvasType (width = self.settings.width - self.settings.leftMargin - self.settings.rightMargin, height = self.settings.height - self.settings.topMargin - self.settings.bottomMargin, x = self.settings.leftMargin, y = self.settings.topMargin, id='canvas-root', **attr) self.dataGroup.draw (self.canvas) def jsUtils (self): return """ function registerEvent (object, event, method, scope, capture) { if (!scope) scope = window; if (!capture) capture = false; var func = function (event) { if (!event) event = window.event; return method.call (scope, event); } if (object.addEventListener) object.addEventListener (event, func, capture); else if (object.attachEvent) object.attachEvent (event, method, func, capture); else return false; return true; } function ViewBox (x, y, width, height) { this.x = parseFloat (x); this.y = parseFloat (y); this.width = parseFloat (width); this.height = parseFloat (height); this.quadrant = function (v) { var midX = this.x + (this.width / 2.0); var midY = this.y + (this.height / 2.0); if (v.y <= midY) { if (v.x >= midX) return 1; else return 2; } else { if (v.x <= midX) return 3; else return 4; } } } function getView (viewer) { var view = viewer.getAttribute ('viewBox'); view = view.split (' '); return new ViewBox (view[0], view[1], view[2], view[3]); } function vect (x, y) { this.x = x; this.y = y; } function pos (node) { var x, y; if (node.getAttribute ('x')) { x = node.getAttribute ('x'); y = node.getAttribute ('y'); } else if (node.getAttribute ('cx')) { x = node.getAttribute ('cx'); y = node.getAttribute ('cy'); } x = parseFloat (x); y = parseFloat (y); return new vect (x, y); } """ def jsHighlight (self): return """ function highlight (event) { this.setAttribute ('fill', this.getAttribute ('highlight-fill')); } function unHighlight (event) { this.setAttribute ('fill', this.getAttribute ('default-fill')); } function addHighlights (node) { if (node.getAttribute) { if (node.getAttribute ('has-highlight')) { node.setAttribute ('default-fill', node.getAttribute ('fill')); registerEvent (node, 'mouseover', highlight, node); registerEvent (node, 'mouseout', unHighlight, node); } for (var i = 0; i < node.childNodes.length; i ++) { addHighlights (node.childNodes[i]); } } } registerEvent (window, 'load', function () { var root = document.getElementById ('canvas-root'); addHighlights (root); }); """ def applySettings (self, filenameOrDict): if type (filenameOrDict) == str: file = open (filenameOrDict) buffer = file.read () setList = [] for child in buffer.split ('\n'): if len (child) == 0: continue if child.startswith ('#'): continue pair = match ('^([^=]+)=(.*)$', child) if pair is None: print 'Warning, Bad formatting in line: ' + child continue key = pair.group (1) value = pair.group (2) setList.append ((key.strip (), value.strip ())) settings = blank () for key, value in setList: setattr (settings, key, value) elif type (filenameOrDict) == dict: settings = blank () for key, value in filenameOrDict.iteritems (): setattr (settings, key, str (value)) else: raise RuntimeError ('Bad type for settings') self.formatSettings (settings) def formatSettings (self, settings): addAttr (settings, 'width', float, 300.0) addAttr (settings, 'height', float, 200.0) addAttr (settings, 'fixedWidth', float, None) addAttr (settings, 'titleSize', float, 10.0) addAttr (settings, 'xLabelSize', float, 8.0) addAttr (settings, 'yLabelSize', float, 8.0) addAttr (settings, 'y2LabelSize', float, 8.0) addAttr (settings, 'leftMargin', float, 10.0) addAttr (settings, 'rightMargin', float, 10.0) addAttr (settings, 'topMargin', float, 10.0) addAttr (settings, 'bottomMargin', float, 10.0) addAttr (settings, 'titleSpace', float, 10.0) addAttr (settings, 'xLabelSpace', float, 10.0) addAttr (settings, 'yLabelSpace', float, 10.0) addAttr (settings, 'y2LabelSpace', float, 10.0) addAttr (settings, 'tooltipSize', float, 7.0) addAttr (settings, 'legendTextHeight', float, 7.0) self.settings = settings def initialFormatting (self): # Format Label Group self.labels = Group (className = 'labels') # Format Title self.title = Text (text = '', id = 'title', textHeight= self.settings.titleSize, horizontalAnchor = 'center') # Format X Label self.xlabel = Text (text = '', id = 'xlabel', textHeight = self.settings.xLabelSize, verticalAnchor = 'top', horizontalAnchor = 'center') # Format Y Label self.ylabel = Group () ylabelRotate = Rotate (-90) self.ylabel.appendTransform (ylabelRotate) self.ylabelText = Text (text = '', id = 'ylabel', textHeight = self.settings.yLabelSize, horizontalAnchor = 'center') self.ylabel.draw (self.ylabelText) # Format Y2 Label self.y2label = Group () y2labelRotate = Rotate (90, self.settings.width, 0) self.y2label.appendTransform (y2labelRotate) self.y2labelText = Text (text = '', id = 'y2label', textHeight = self.settings.y2LabelSize, horizontalAnchor = 'center') self.y2label.draw (self.y2labelText) self.leg = None def positionLabels (self): if self.canvas: topY = self.settings.height - (self.canvas.height + self.canvas.y) midX = self.canvas.x + (self.canvas.width) / 2.0 midY = topY + (self.canvas.height) / 2.0 else: midX = self.settings.width / 2.0 midY = self.settings.height / 2.0 # Title Position self.title.move (self.settings.width / 2.0, self.settings.topMargin) #self.title.move (midX, self.settings.topMargin) # Y Label Position self.ylabelText.move (-midY, self.settings.leftMargin) # X Label Position if self.leg: modX = self.leg.height + 30 else: modX = 0 self.xlabel.move (midX, self.settings.height -modX - self.settings.bottomMargin) #self.xlabel.move (midX, self.settings.height - self.canvas.y + self.xlabel.textHeight + 10) # Y2 Label Position self.y2labelText.move (self.settings.width + midY, self.settings.rightMargin) def setTitle (self, title): self.title.setText (title) self.labels.draw (self.title) if self.canvas: deltaY = self.title.height + self.settings.titleSpace self.canvas.changeSize (0, -deltaY) def setXLabel (self, xlabel): self.xlabel.setText (xlabel) self.labels.draw (self.xlabel) if self.canvas: deltaY = self.xlabel.height + self.settings.xLabelSpace self.canvas.move (0, deltaY) self.canvas.changeSize (0, -deltaY) def setYLabel (self, ylabel): self.ylabelText.setText (ylabel) self.labels.draw (self.ylabel) if self.canvas: deltaX = self.ylabelText.height + self.settings.yLabelSpace self.canvas.move (deltaX, 0) self.canvas.changeSize (-deltaX, 0) def addSubtitle (self, text): t = Text (text = text, x = self.settings.width - 5, y = self.settings.height - 5, horizontalAnchor = 'right', verticalAnchor = 'bottom', textHeight = 6, ) self.canvas.move (0, 12) self.canvas.changeSize (0, -12) self.draw (t) def addLegend (self, pairs): if len (pairs) == 0: return """self.leg = Legend (width = self.settings.width, textHeight = self.settings.legendTextHeight) keys = pairs.keys () keys.sort () for k in keys: self.leg.addKey (k, pairs[k]) self.leg.y = self.settings.height - self.leg.height self.canvas.move (0, self.leg.height + 20) self.canvas.changeSize (0, -(self.leg.height + 20)) self.draw (self.leg)""" self.leg = Legend (width = self.settings.width / 4.0, textHeight = self.settings.legendTextHeight) keys = pairs.keys () keys.sort () for k in keys: self.leg.addKey (k, pairs[k]) self.canvas.changeSize (-self.leg.width, 0) self.leg.y = self.settings.height - (self.canvas.height + self.canvas.y) self.leg.x = self.canvas.x + self.canvas.width #self.leg.y = self.settings.height - self.leg.height #self.canvas.move (0, self.leg.height + 20) self.draw (self.leg) def setY2Label (self, ylabel): self.y2labelText.setText (ylabel) self.labels.draw (self.y2label) if self.canvas: deltaX = self.y2labelText.height + self.settings.y2LabelSpace self.canvas.changeSize (-deltaX, 0) def setSVG (self): self.finalize () attr = PrintableCanvas.setSVG (self) if self.settings.fixedWidth: height = self.settings.fixedWidth * (self.settings.height / self.settings.width) attr.update ([('width', self.settings.fixedWidth), ('height', height)]) return attr def finalize (self): self.dataGroup.transform.set (-1, 1, 1) self.dataGroup.transform.set (self.viewBox.y + self.viewBox.height, 1, 2) self.positionLabels () if len (self.labels) > 0: self.drawAt (self.labels, 1)
def visualize(self): import matplotlib.patches as mpatches self.fig, self.ax = plt.subplots(3, 1, sharex=True) self.ax[0].set_title('Click on legend rectangle to toggle data on/off', fontdict={'fontsize' : 8 }) self.fig.set_size_inches(12, 7, forward=True) plt.subplots_adjust(left=0.05, bottom=0.15, right=0.98, hspace=0.15) # bpdiff_trans = [] # major transition self.ax = self.ax[::-1] # major가 같은 것 들만 print('Run matplotlib...') for i in range(len(self.bpdiff)): sc = [ {'major' : [], 'minor': [] }, {'major' : [], 'minor': [] }, {'major' : [], 'minor': [] }, {'major' : [], 'minor': [] } ] self.draw_vspans(self.ax[i], self.B.range_dumas) for a in range(4): cond1 = self.bpdiff[i]['Mj_seq_x'] == a # major a -> minor b for b in range(4): if a == b: # sc[a]['major'].append(None) # sc[a]['minor'].append(None) continue # 1. minor가 같은 것들 처리 : 같은 색상 cond2 = self.bpdiff[i]['Mn_seq_y'] == b # x는 cond3 = logical_or(self.bpdiff[i]['minor_x'] == 0, self.bpdiff[i]['Mn_seq_x'] == b) cond3 = logical_and(cond2, cond3) d = self.B.get_bpdiff_cond(index=i, cond=logical_and(cond1, cond3)) #bpdiff[i][ logical_and(cond1, cond3).values ] if len(d) > 0: y_ = [ linspace(d_.maf_x, d_.maf_y, self.seq_size) for d_ in d[['maf_x', 'maf_y']].itertuples() ] x_ = array(y_) for j in range(len(d)): x_[j].fill( d[self.duma_position].values[j] ) c = [ self.xlin for _ in range(len(x_))] sc[a]['minor'].append(self.ax[i].scatter(x_, y_, c=c, cmap=mcols.cmap(self.mcolor[b]), s=self.markersize, linewidth=0.0)) # draw major scatter y_ = self.get_uppery(y_) x_ = [ ii[0] for ii in x_] sc[a]['major'].append(self.ax[i].scatter(x_, y_, color=self.basecolors[a], label=self.basepair[a], s=self.markersize-1)) # # 2. minor가 바뀌는 것들 처리 : colors diverging # 각 basetype -> b type, target이 b, 즉, b로 변한것 cond2 = logical_and(self.bpdiff[i]['Mn_seq_x'] != b, self.bpdiff[i]['minor_x'] > 0) cond3 = logical_and(cond2, self.bpdiff[i]['Mn_seq_y'] == b) d = self.bpdiff[i][ logical_and(cond1, cond3).values ] if len(d) > 0: self.draw_minor_transition(self.ax[i], sc, d, a, b) # end for b #end for a self.sct.append(sc) self.ax[i].set_ylim([0, 50]) self.ax[i].set_ylabel('Variation of MAF(%)') self.ax[i].set_title(self.ylabel[i], loc='left', fontdict={'fontsize':13, 'verticalalignment':'top', 'color':'black', 'backgroundcolor':'#FEFEFE'}) # ZoomSlider of Dumas position zaxes = plt.axes([0.08, 0.07, 0.90, 0.03], facecolor='lightgoldenrodyellow') self.zslider = ZoomSlider(zaxes, 'Dumas Position', valmin=self.B.minpos, valmax=self.B.maxpos, valleft=self.B.minpos, valright=self.B.maxpos, color='lightblue', valstep=1.0) self.zslider.on_changed(self.update_xlim) from legend import Legend spatches = [ mpatches.Patch(color=self.basecolors[idx], label=self.basepair[idx]) for idx in range(4) ] leg_basepair = Legend(self.fig, { 'spatches' : spatches, 'label' : self.basepair, 'bbox':(0.66, .91, 0.33, .102), 'loc' : 3, 'ncol' : 4, 'mode' : 'expand', 'borderaxespad' : 0.}) dumalabels = [ key for key in self.dumas.keys()] spatches = [ mpatches.Patch(color='grey', label=dumalabels[idx]) for idx in range(3) ] leg_dumas = Legend(self.fig, { 'spatches' : spatches, 'label' : dumalabels, 'bbox':(0.05, .91, 0.33, .102), 'loc' : 3, 'ncol' : 3, 'mode' : 'expand', 'borderaxespad' : 0.}) spatches = [ mpatches.Patch(color=['black', 'magenta'][idx], label=['base', 'variation'][idx]) for idx in range(2) ] self.leg_filter = Legend(self.fig, { 'spatches' : spatches, 'label' : ['base_0.0', 'variation_5.0'], 'bbox':(0.40, .91, 0.24, .102), 'loc' : 3, 'ncol' : 3, 'mode' : 'expand', 'borderaxespad' : 0.}) for x in self.ax: self.annot.append(x.annotate("", xy=(0,0), xytext=(20,-30),textcoords="offset points", bbox=dict(boxstyle="round", fc="w"), arrowprops=dict(arrowstyle="->"))) # annotate over other axes x.figure.texts.append(x.texts.pop()) # # x.callbacks.connect('xlim_changed', xlim_changed_event) for c in self.B.range_dumas.keys(): self.annot_gere[c] = [] ay_ = 0.85 if c == self.duma_Repeat else 0.7 if c == self.duma_ORF: ay_ = 1. for d in self.dumas[c]: self.annot_gere[c].append(self.ax[-1].annotate(d, xy=(self.B.range_dumas[c][d]['min'], 50*ay_), xytext=(-20,10), textcoords="offset points", bbox=dict(boxstyle="round", fc="w"), arrowprops=dict(arrowstyle="fancy"))) for an_ in self.annot: an_.set_visible(False) an_.set_zorder(10) for c in self.B.range_dumas.keys(): for an_ in self.annot_gere[c]: an_.set_visible(False) # legend pick event self.fig.canvas.mpl_connect('pick_event', self.onpick) leg_dumas.pick() self.fig.canvas.mpl_connect("button_press_event", self.click_) # ax[-1].text(105500, 49, 'IRS', fontdict={'fontsize':5}) # buttonaxes = plt.axes([0.89, .95, 0.1, 0.025]) # bnext = Button(buttonaxes, 'Export') # bnext.on_clicked(ExportToParallelCoordGraph) plt.show()
class visualization: def __init__(self, book): # from pandas import ExcelFile, DataFrame, merge from VizBook import vizbook self.duma_position = book.col_DumaPosition self.duma_Genome = book.col_GenomeStructure self.duma_Repeat = book.col_RepeatRegion self.duma_ORF = book.col_ORF self.dumas_col = [ self.duma_position, self.duma_Genome, self.duma_Repeat, self.duma_ORF ] self.dumas = { self.duma_Genome : [], self.duma_Repeat: [], self.duma_ORF : [] } self.spans = dict() #{ duma_Genome : dict(), duma_Repeat : dict(), duma_ORF : dict()} self.seq_size, self.markersize = 100, 10 self.xlin = linspace(0.0, 1.0, self.seq_size) self.mcolor = [ 'Blues', 'Orgs', 'Grns', 'Reds' ] self.dcolor = [ [None, 'BuOg', 'BuGn', 'BuRd' ], ['OgBu', None, 'OgGn', 'OgRd'], ['GnBu', 'GnOg', None, 'GnRd'], ['RdBu', 'RdOg', 'RdGn', None] ] self.basecolors = ['blue', 'orange', 'lightgreen', 'red'] self.basepair = ['A', 'G', 'C', 'T'] self.annot, self.sct, self.leg = [], [], None self.annot_gere = dict() self.B = None self.bpdiff = None self.major_visiable = [ True, True, True, True] self.base_range, self.bidx = [ 0., 5., 10., 15., 20., 25., 30., 35., 40., 45.], 0 self.variation_range, self.vidx = [ 5., 10., 15., 20., 25., 30., 35., 40., 45.], 0 self.B = vizbook(book) self.B.preprocessing() self.dumas = self.B.dumas_info self.nsheet = self.B.nsheet self.bpdiff = self.B.bpdiff self.ylabel = self.B.datalabel # 임의지정을 통한 교환 self.ylabel[1], self.ylabel[2] = self.ylabel[2], self.ylabel[1] self.bpdiff[1], self.bpdiff[2] = self.bpdiff[2], self.bpdiff[1] # bpdiff_trans[1], bpdiff_trans[2] = bpdiff_trans[2], bpdiff_trans[1] def visualize(self): import matplotlib.patches as mpatches self.fig, self.ax = plt.subplots(3, 1, sharex=True) self.ax[0].set_title('Click on legend rectangle to toggle data on/off', fontdict={'fontsize' : 8 }) self.fig.set_size_inches(12, 7, forward=True) plt.subplots_adjust(left=0.05, bottom=0.15, right=0.98, hspace=0.15) # bpdiff_trans = [] # major transition self.ax = self.ax[::-1] # major가 같은 것 들만 print('Run matplotlib...') for i in range(len(self.bpdiff)): sc = [ {'major' : [], 'minor': [] }, {'major' : [], 'minor': [] }, {'major' : [], 'minor': [] }, {'major' : [], 'minor': [] } ] self.draw_vspans(self.ax[i], self.B.range_dumas) for a in range(4): cond1 = self.bpdiff[i]['Mj_seq_x'] == a # major a -> minor b for b in range(4): if a == b: # sc[a]['major'].append(None) # sc[a]['minor'].append(None) continue # 1. minor가 같은 것들 처리 : 같은 색상 cond2 = self.bpdiff[i]['Mn_seq_y'] == b # x는 cond3 = logical_or(self.bpdiff[i]['minor_x'] == 0, self.bpdiff[i]['Mn_seq_x'] == b) cond3 = logical_and(cond2, cond3) d = self.B.get_bpdiff_cond(index=i, cond=logical_and(cond1, cond3)) #bpdiff[i][ logical_and(cond1, cond3).values ] if len(d) > 0: y_ = [ linspace(d_.maf_x, d_.maf_y, self.seq_size) for d_ in d[['maf_x', 'maf_y']].itertuples() ] x_ = array(y_) for j in range(len(d)): x_[j].fill( d[self.duma_position].values[j] ) c = [ self.xlin for _ in range(len(x_))] sc[a]['minor'].append(self.ax[i].scatter(x_, y_, c=c, cmap=mcols.cmap(self.mcolor[b]), s=self.markersize, linewidth=0.0)) # draw major scatter y_ = self.get_uppery(y_) x_ = [ ii[0] for ii in x_] sc[a]['major'].append(self.ax[i].scatter(x_, y_, color=self.basecolors[a], label=self.basepair[a], s=self.markersize-1)) # # 2. minor가 바뀌는 것들 처리 : colors diverging # 각 basetype -> b type, target이 b, 즉, b로 변한것 cond2 = logical_and(self.bpdiff[i]['Mn_seq_x'] != b, self.bpdiff[i]['minor_x'] > 0) cond3 = logical_and(cond2, self.bpdiff[i]['Mn_seq_y'] == b) d = self.bpdiff[i][ logical_and(cond1, cond3).values ] if len(d) > 0: self.draw_minor_transition(self.ax[i], sc, d, a, b) # end for b #end for a self.sct.append(sc) self.ax[i].set_ylim([0, 50]) self.ax[i].set_ylabel('Variation of MAF(%)') self.ax[i].set_title(self.ylabel[i], loc='left', fontdict={'fontsize':13, 'verticalalignment':'top', 'color':'black', 'backgroundcolor':'#FEFEFE'}) # ZoomSlider of Dumas position zaxes = plt.axes([0.08, 0.07, 0.90, 0.03], facecolor='lightgoldenrodyellow') self.zslider = ZoomSlider(zaxes, 'Dumas Position', valmin=self.B.minpos, valmax=self.B.maxpos, valleft=self.B.minpos, valright=self.B.maxpos, color='lightblue', valstep=1.0) self.zslider.on_changed(self.update_xlim) from legend import Legend spatches = [ mpatches.Patch(color=self.basecolors[idx], label=self.basepair[idx]) for idx in range(4) ] leg_basepair = Legend(self.fig, { 'spatches' : spatches, 'label' : self.basepair, 'bbox':(0.66, .91, 0.33, .102), 'loc' : 3, 'ncol' : 4, 'mode' : 'expand', 'borderaxespad' : 0.}) dumalabels = [ key for key in self.dumas.keys()] spatches = [ mpatches.Patch(color='grey', label=dumalabels[idx]) for idx in range(3) ] leg_dumas = Legend(self.fig, { 'spatches' : spatches, 'label' : dumalabels, 'bbox':(0.05, .91, 0.33, .102), 'loc' : 3, 'ncol' : 3, 'mode' : 'expand', 'borderaxespad' : 0.}) spatches = [ mpatches.Patch(color=['black', 'magenta'][idx], label=['base', 'variation'][idx]) for idx in range(2) ] self.leg_filter = Legend(self.fig, { 'spatches' : spatches, 'label' : ['base_0.0', 'variation_5.0'], 'bbox':(0.40, .91, 0.24, .102), 'loc' : 3, 'ncol' : 3, 'mode' : 'expand', 'borderaxespad' : 0.}) for x in self.ax: self.annot.append(x.annotate("", xy=(0,0), xytext=(20,-30),textcoords="offset points", bbox=dict(boxstyle="round", fc="w"), arrowprops=dict(arrowstyle="->"))) # annotate over other axes x.figure.texts.append(x.texts.pop()) # # x.callbacks.connect('xlim_changed', xlim_changed_event) for c in self.B.range_dumas.keys(): self.annot_gere[c] = [] ay_ = 0.85 if c == self.duma_Repeat else 0.7 if c == self.duma_ORF: ay_ = 1. for d in self.dumas[c]: self.annot_gere[c].append(self.ax[-1].annotate(d, xy=(self.B.range_dumas[c][d]['min'], 50*ay_), xytext=(-20,10), textcoords="offset points", bbox=dict(boxstyle="round", fc="w"), arrowprops=dict(arrowstyle="fancy"))) for an_ in self.annot: an_.set_visible(False) an_.set_zorder(10) for c in self.B.range_dumas.keys(): for an_ in self.annot_gere[c]: an_.set_visible(False) # legend pick event self.fig.canvas.mpl_connect('pick_event', self.onpick) leg_dumas.pick() self.fig.canvas.mpl_connect("button_press_event", self.click_) # ax[-1].text(105500, 49, 'IRS', fontdict={'fontsize':5}) # buttonaxes = plt.axes([0.89, .95, 0.1, 0.025]) # bnext = Button(buttonaxes, 'Export') # bnext.on_clicked(ExportToParallelCoordGraph) plt.show() def draw_vspans(self, ax_, range_dumas): from numpy import random # global range_dumas # random.seed(1234) ymax = 50 for c in range_dumas.keys(): if c == self.duma_Genome: ymax = 0.7 elif c == self.duma_Repeat: ymax = 0.85 else: ymax = 1 for d in range_dumas[c].keys(): # r, g, b = random.uniform(0, 1, 3) self.spans[c+str(d)] = self.spans.get(c+str(d), []) self.spans[c+str(d)].append(ax_.axvspan(range_dumas[c][d]['min'], range_dumas[c][d]['max'], 0, ymax, color=scols.get_color(), alpha=0.1)) scols.cur = 0 def update_xlim(self, val): if self.zslider.valleft > self.zslider.valright: return for x in self.ax: x.axes.set_xlim([self.zslider.valleft, self.zslider.valright]) self.fig.canvas.draw_idle() def onpick(self, event): patch = event.artist vis = True if patch.get_alpha()==0.3 else False label = patch.get_label() color = 'grey' # basepair 클릭시 일관성을 위해... (필터링과) # color랑 몇가지 변수만 잘 설정해서 filtering로 넘어가기 if label == 'A' or label == 'G' or label == 'C' or label == 'T': nmajor = self.basepair.index( label ) color = self.basecolors[nmajor] self.major_visiable[ nmajor ] = vis for sc in self.sct: for mj, mn in zip(sc[nmajor]['major'], sc[nmajor]['minor']): mj.set_visible( vis ) mn.set_visible( vis ) elif label == self.duma_ORF or label ==self.duma_Repeat or label ==self.duma_Genome: for d in self.B.range_dumas[label].keys(): for sc in self.spans[label+str(d)]: sc.set_visible(vis) for an_ in self.annot_gere[label]: an_.set_visible(vis) else: if label == 'base' : self.bidx += 1 else: self.vidx += 1 if self.bidx == len(self.base_range): self.bidx = 0 if self.vidx == len(self.variation_range): self.vidx = 0 range_, pid, color = (str(self.base_range[self.bidx]), 0, 'black') if label == 'base' else (str(self.variation_range[self.vidx]), 1, 'magenta') self.leg_filter.get_legend().get_texts()[pid].set_text(label +'_'+ range_) for i in range(len(self.bpdiff)): for a in range(4): if not self.major_visiable[a]: continue cond1 = self.bpdiff[i]['Mj_seq_x'] == a if len( self.bpdiff[i][ cond1.values ] ) > 0: cond2 = logical_and(self.bpdiff[i]['maf_x'] >= self.base_range[self.bidx], abs(self.bpdiff[i]['maf_y'] - self.bpdiff[i]['maf_x']) >= self.variation_range[self.vidx]) vis_d = logical_and(cond1, cond2) from numpy import isin for scmj, scmn in zip(self.sct[i][a]['major'], self.sct[i][a]['minor']): mj_vis_arr = isin(scmj.get_offsets()[:,0], self.bpdiff[i][vis_d.values][self.duma_position].tolist() ) mn_vis_arr = isin(scmn.get_offsets()[:,0], self.bpdiff[i][vis_d.values][self.duma_position].tolist() ) mjcolors_ = scmj.get_facecolors() mncolors_ = scmn.get_facecolors() if len(mjcolors_) == len(mj_vis_arr): mjcolors_[:,-1][ mj_vis_arr ] = 1 mjcolors_[:,-1][ logical_not(mj_vis_arr) ] = 0 else: mjc = list(mjcolors_[0][:-1]) # !! mjcolors_ = [ mjc + [1.] if mi else mjc+[0.] for mi in mj_vis_arr ] if len(mncolors_) == len(mn_vis_arr): mncolors_[:,-1][ mn_vis_arr ] = 1 mncolors_[:,-1][ logical_not(mn_vis_arr) ] = 0 scmj.set_facecolors(mjcolors_) scmj.set_edgecolors(mjcolors_) scmn.set_facecolors(mncolors_) scmn.set_edgecolors(mncolors_) if vis: patch.set_alpha(1.0) else: patch.set_facecolor(color) patch.set_alpha(0.3) self.fig.canvas.draw_idle() def click_(self, event): if event.xdata is None: return pos = int(event.xdata) # print(pos) epos = 20. minpos = pos - epos maxpos = pos + epos for i in range(len(self.bpdiff)): self.annot[i].set_visible(False) # bpdiff 0에서 찾기 -> 일관성.. # binary search pos ind_ = self.bpdiff[i][[self.duma_position]].values.ravel().searchsorted(pos, side='left') if ind_ >= len(self.bpdiff[i][[self.duma_position]].values): ind_ = ind_ - 1 spos = self.bpdiff[i][[self.duma_position]].values[ ind_ ] ind_1 = ind_ - 1 spos_1 = self.bpdiff[i][[self.duma_position]].values[ ind_1 ] # print(ind_, spos, spos_1) ind_, spos = (ind_, spos) if abs(pos-spos) < abs(pos-spos_1) else (ind_1,spos_1) # print(ind_, spos) # print(bpdiff[i].values[ind_]) ge = self.bpdiff[i][[self.duma_Genome]].values[ind_][0] re = self.bpdiff[i][[self.duma_Repeat]].values[ind_][0] orf_ = self.bpdiff[i][[self.duma_ORF]].values[ind_][0] # print(ge, re, orf_) bp_ = self.bpdiff[i].iloc[ind_] if spos < minpos or maxpos < spos: pass # tagging else: # minpos <= spos and spos <= maxpos: for sc_ in self.sct[i]: for s_ in sc_['major']: if s_ == None: continue # xy data -> xy (position in scatter)로 변환하는 방법 if s_.get_offsets().__contains__(spos): ind = where(s_.get_offsets() == spos) pos_ = s_.get_offsets()[ind[0]] pos_[0][1] = bp_['maf_y'] # print(pos_) self.annot[i].xy = pos_[0] text = "mj/mn : {}/{}\nmaf :{}->{}\nGnSt : {}\nRpRg : {}\nORF : {}".format(self.basepair[int(bp_[['Mj_seq_y']].values[0])], self.basepair[int(bp_[['Mn_seq_y']].values[0])].lower(), round(float(bp_['maf_x']), 2), round(float(bp_['maf_y']), 2), ge,re,orf_) self.annot[i].set_text(text) self.annot[i].get_bbox_patch().set_facecolor(self.basecolors[self.basepair.index(s_.get_label())]) self.annot[i].get_bbox_patch().set_alpha(0.5) self.annot[i].set_visible(True) self.fig.canvas.draw_idle() def get_uppery(self, y): y_ = [ i[-1] for i in y ] return y_ def draw_minor_transition(self, ax, sc, d, a, b): # transition from i to b for i in range(4): if i == b: continue d_ = d[ d['Mn_seq_x'] == i ] if len(d_) == 0: continue y_ = [ linspace(p_.maf_x, p_.maf_y, self.seq_size) for p_ in d_[['maf_x', 'maf_y']].itertuples() ] x_ = array(y_) for j in range(len(d_)): x_[j].fill( d_[self.duma_position].values[j] ) c = [ self.xlin for _ in range(len(x_))] sc[a]['minor'].append(ax.scatter(x_, y_, c=c, cmap=mcols.cmap(self.dcolor[i][b]), s=self.markersize)) # draw major scatter y_ = self.get_uppery(y_) x_ = [ i[0] for i in x_] sc[a]['major'].append(ax.scatter(x_, y_, color=self.basecolors[a], label=self.basepair[a], s=self.markersize-1))
def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.setModal(True) self.setWindowTitle("Running jobs") self.setMinimumWidth(250) #self.setMinimumHeight(250) self.ctrl = SimulationsDialogController(self) self.simulationProgress = QtGui.QProgressBar() self.simulationProgress.setValue(0) self.connect(self.simulationProgress, QtCore.SIGNAL('setValue(int)'), self.updateProgress) self.cogwheel = Cogwheel(size=20) memberLayout = QtGui.QVBoxLayout() progressLayout = QtGui.QHBoxLayout() progressLayout.addWidget(self.simulationProgress) progressLayout.addWidget(self.cogwheel) memberLayout.addLayout(progressLayout) simulationLayout = QtGui.QHBoxLayout() self.simulationList = SimulationList() self.simulationList.contextMenuEvent = self._contextMenu self.connect(self.simulationList, QtCore.SIGNAL('itemSelectionChanged()'), self.ctrl.selectSimulation) simulationLayout.addWidget(self.simulationList) self.simulationPanel = SimulationPanel() simulationLayout.addWidget(self.simulationPanel) memberLayout.addLayout(simulationLayout) legendLayout = QtGui.QHBoxLayout() legendLayout.addLayout( Legend("Not active", SimulationItemDelegate.notactive)) legendLayout.addLayout( Legend("Waiting", SimulationItemDelegate.waiting)) legendLayout.addLayout( Legend("Pending", SimulationItemDelegate.pending)) legendLayout.addLayout( Legend("Running", SimulationItemDelegate.running)) legendLayout.addLayout( Legend("User killed", SimulationItemDelegate.userkilled)) legendLayout.addLayout(Legend("Failed", SimulationItemDelegate.failed)) legendLayout.addLayout( Legend("Finished", SimulationItemDelegate.finished)) memberLayout.addLayout(legendLayout) self.doneButton = QtGui.QPushButton("Done", self) self.connect(self.doneButton, QtCore.SIGNAL('clicked()'), self.accept) buttonLayout = QtGui.QHBoxLayout() self.estimateLabel = QtGui.QLabel() buttonLayout.addWidget(self.estimateLabel) buttonLayout.addStretch(1) buttonLayout.addWidget(self.doneButton) memberLayout.addSpacing(10) memberLayout.addLayout(buttonLayout) self.setLayout(memberLayout)
class Drawable(): """ The :class:`~Drawable` class wraps a matplotlib figure and axis to provide additional functionality. If no axis is given, the default plot axis (:code:`plt.gca()`) is used. The :class:`~Drawable` class can be used as a normal :class:`matplotlib.axis.Axis` object with additional functionality. The axis functionality can be called on the :class:`~Drawable` class. The :class:`~Drawable` instance re-routes method and attribute calls to the :class:`matplotlib.axis.Axis` instance. To create a :class:`~Drawable` instance from a normal plot: .. code-block:: python viz = drawable.Drawable(plt.figure(figsize=(10, 5))) To create a :class:`~Drawable` instance from an axis, or a subplot: .. code-block:: python figure, axis = plt.subplots(2, 1, figsize=(10, 10)) viz = drawable.Drawable(figure, axis[0]) :ivar figure: The figure that the :class:`~Drawable` class wraps. :vartype figure: :class:`matplotlib.figure.Figure` :ivar axis: The axis where the drawable will draw. :vartype axis: :class:`matplotlib.axis.Axis` :var caption: The caption, displayed under the title. :vartype caption: :class:`~text.annotation.Annotation` :ivar timeseries: The time series object that is being used. :vartype timeseries: :class:`~timeseries.timeseries.TimeSeries` :ivar legend: The figure's legend. :vartype legend: :class:`~legend.Legend` :ivar annotations: The annotations in the visualization. :vartype annotations: list of :class:`~text.annotation.Annotation` """ def __init__(self, figure, axis=None): """ Create the drawable with the figure. :param figure: The figure that the :class:`~Drawable` class wraps. This is mainly used to get the figure renderer. :type figure: :class:`matplotlib.figure.Figure` :param axis: The axis (or subplot) where to plot visualizations. If `None` is not given, the plot's main subplot is used instead. :type axis: `None` or :class:`matplotlib.axis.Axis` """ self.figure = figure self.axis = plt.gca() if axis is None else axis self.caption = Annotation(self) self.annotations = [] self.legend = Legend(self) self.timeseries = None def set_caption(self, caption, alpha=0.8, lineheight=1.25, *args, **kwargs): """ Add a caption to the subplot. The caption is added just beneath the title. The method re-draws the title to make space for the caption. The caption is a :class:`~text.text.Annotation` object. Any arguments that the constructor accepts can be provided to this method. :param caption: The caption to add to the axis. :type caption: str :param alpha: The opacity of the caption between 0 and 1. :type alpha: float :param lineheight: The space between lines. :type lineheight: float :return: The drawn caption. :rtype: :class:`~text.annotation.Annotation` """ self.caption = Annotation(self) self.caption.draw(caption, (0, 1), 1, va='bottom', alpha=alpha, lineheight=lineheight, *args, **kwargs, transform=self.axis.transAxes) self.redraw() return self.caption def redraw(self): """ Re-create the title with the necessary padding to fit the caption and the legend. """ """ Move the caption up to make space for the legend. """ legend_bb = self.legend.get_virtual_bb(transform=self.axis.transAxes) self.caption.set_position((0, 1 + 0.01 + legend_bb.height), ha='left', va='bottom', transform=self.axis.transAxes) """ Get the height of the caption and the height of the legend. The title should allow enough padding to make space for both. """ title = self.axis.get_title(loc='left') caption_bb = self.axis.transData.transform( self.caption.get_virtual_bb()) height = abs(caption_bb[0][1] - caption_bb[1][1]) legend_bb = self.axis.transData.transform(self.legend.get_virtual_bb()) height += abs(legend_bb[0][1] - legend_bb[1][1]) pad_px = self.axis.transAxes.transform( (0, 0.01))[1] - self.axis.transAxes.transform((0, 0))[1] self.axis.set_title(title, loc='left', pad=(5 + height + pad_px * 2)) def __getattr__(self, name): """ Get an attribute indicated by `name` from the class. If it gets to this point, then the attribute does not exist. Instead, it is retrieved from the :class:`~Drawable` axis. :param name: The name of the attribute. :type name: str :return: The function applied on the axis. :rtype: function """ def method(*args, **kwargs): """ Try to get the attribute from the axis. If arguments were given, then the attribute is treated as a method call. Otherwise, it is treated as a normal attribute call. """ if callable(getattr(self.axis, name)): return getattr(self.axis, name)(*args, **kwargs) else: return getattr(self.axis, name) return method """ Visualizations """ def draw_text_annotation(self, *args, **kwargs): """ Draw a text annotation visualization on this :class:`~Drawable`. The arguments and keyword arguments are those supported by :meth:`~text.annotation.TextAnnotation.draw` method. :return: The drawn text annotation's lines. Each line is made up of tuples of lists. The first list in each tuple is the list of legend labels. The second list in each tuple is the list of actual tokens. :rtype: list of tuple """ text_annotation = TextAnnotation(self) return text_annotation.draw(*args, **kwargs) def draw_time_series(self, *args, **kwargs): """ Draw a time series visualization on this :class:`~Drawable`. The arguments and keyword arguments are those supported by :meth:`~text.annotation.TextAnnotation.draw` method. :return: A tuple made up of the drawn plot and label. :rtype: tuple """ self.timeseries = self.timeseries if self.timeseries is not None else TimeSeries( self) return self.timeseries.draw(*args, **kwargs) def annotate(self, text, x, y, marker=None, pad=0.01, *args, **kwargs): """ Add an annotation to the plot. Any additional arguments and keyword arguments are passed on to the annotation's :meth:`~text.text.Annotation.draw` function. For example, the `va` can be provided to specify the vertical alignment. The `align` parameter can be used to specify the text's alignment. :param text: The text of the annotation to draw. :type text: str :param x: A tuple containing the start and end x-coordinates of the annotation. :type x: tuple :param y: The y-coordinate of the annotation. :type y: float :param marker: The marker style. If it is not given, no marker is drawn. :type marker: None or dict :param pad: The amount of padding applied to the annotation. :type pad: float """ annotation = Annotation(self) """ Draw the marker if it is given. The color is obtained from the kwargs if a marker color is not given. The point of the marker is based on the alignment of the annotation. """ if marker is not None: marker['color'] = marker.get('color', kwargs.get('color')) if kwargs.get('align', 'left') == 'left': self.axis.plot(x[0], y, *args, **marker) elif kwargs.get('align') == 'right': self.axis.plot(x[1], y, *args, **marker) elif kwargs.get('align') == 'center': self.axis.plot((x[0] + x[1]) / 2., y, *args, **marker) tokens = annotation.draw(text, x, y, pad=pad, *args, **kwargs) self.annotations.append(annotation) return tokens
class ShapeViewer(QMainWindow, Ui_MainWindow): def __init__(self): QMainWindow.__init__(self) self.activeCanvas = None self.activeTool = None # Required by Qt4 to initialize the UI self.setupUi(self) # Set the title for the app self.setWindowTitle("APCartOS v0.2") # Create the legend widget self.createLegendWidget() self.actionAdd_new_window.triggered.connect(self.newWindow) self.mapArea.subWindowActivated.connect(self.subActive) self.actionTile.triggered.connect(self.tileWindows) self.actionStart_editing.triggered.connect(self.toogleEditing) self.actionStart_editing.setEnabled(False) # self.actionAddWms.triggered.connect(self.addWms) # create the actions self.actionAddLayer = QAction(QIcon(":/icons/grass_add_map.png"), QString("Add Layer"), self) self.actionZoomIn = QAction(QIcon(":/icons/zoom-in.png"), QString("Zoom in"), self) self.actionZoomOut = QAction(QIcon(":/icons/zoom-out.png"), QString("Zoom out"), self) self.actionPan = QAction(QIcon(":/icons/pan.png"), QString("Pan"), self) self.actionAddLayer.setCheckable(True) self.actionZoomIn.setCheckable(True) self.actionZoomOut.setCheckable(True) self.actionPan.setCheckable(True) # This is the new syntax to connect to events (signals) of objects self.actionAddLayer.triggered.connect(self.addLayer) self.actionZoomIn.triggered.connect(self.zoomIn) self.actionZoomOut.triggered.connect(self.zoomOut) self.actionPan.triggered.connect(self.pan) QObject.connect(self.legend, SIGNAL("activeLayerChanged"), self.enableEditingButton) self.toolBar.addAction(self.actionAddLayer) self.toolBar.addAction(self.actionZoomIn) self.toolBar.addAction(self.actionZoomOut) self.toolBar.addAction(self.actionPan) self.newWindow() def createLegendWidget(self): # Create the map legend widget and associate to the canvas """ self.legend = Legend(self) self.legend.setObjectName("theMapLegend") self.LegendDock = QDockWidget("Layers", self) self.LegendDock.setObjectName("legend") self.LegendDock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.LegendDock.setWidget(self.legend) self.LegendDock.setContentsMargins(9, 9, 9, 9) self.addDockWidget(Qt.LeftDockWidgetArea, self.LegendDock) def tileWindows(self): self.mapArea.tileSubWindows() def subActive(self, window): if window is None: return if window.widget() == self.activeCanvas: return # Disconnect the last if not self.activeCanvas is None: self.activeCanvas.extentsChanged.disconnect(self.extentsChanged) self.activeCanvas = window.widget() self.legend.setCanvas(self.activeCanvas) self.activeCanvas.extentsChanged.connect(self.extentsChanged) self.legend.clear() for layer in reversed(self.activeCanvas.layers()): self.legend.addLayerToLegend(layer) if self.activeTool == "ZoomIn": self.zoomIn() elif self.activeTool == "ZoomOut": self.zoomOut() elif self.activeTool == "Pan": self.pan() else: return def extentsChanged(self): for window in self.mapArea.subWindowList(): if not window.widget() == self.activeCanvas: extent = self.activeCanvas.extent() can = window.widget() can.setExtent(extent) can.refresh() def newWindow(self): canvas = MyCanvas() canvas.useImageToRender(False) self.mapArea.addSubWindow(canvas) zoomIn = QgsMapToolZoom(canvas, False) zoomIn.setAction(self.actionZoomIn) canvas.setZoomInTool(zoomIn) zoomOut = QgsMapToolZoom(canvas, True) zoomOut.setAction(self.actionZoomOut) canvas.setZoomOutTool(zoomOut) panTool = QgsMapToolPan(canvas) panTool.setAction(self.actionPan) canvas.setPanTool(panTool) canvas.show() self.tileWindows() def zoomIn(self): self.activeCanvas.setMapTool(self.activeCanvas.getZoomInTool()) self.activeTool = "ZoomIn" def zoomOut(self): self.activeCanvas.setMapTool(self.activeCanvas.getZoomOutTool()) self.activeTool = "ZoomOut" def pan(self): self.activeCanvas.setMapTool(self.activeCanvas.getPanTool()) self.activeTool = "Pan" def addLayer(self): # layout is set - open a layer # Add an OGR layer to the map file = QFileDialog.getOpenFileName(self, "Open File", ".", "Shapefile (*.shp);; Raster (*.tif)") fileInfo = QFileInfo(file) # Add the layer extn = os.path.splitext(str(file))[1] if extn.lower() == ".shp": layer = QgsVectorLayer(file, fileInfo.fileName(), "ogr") else: layer = QgsRasterLayer(file, fileInfo.fileName()) if not layer.isValid(): tryagain = QMessageBox.question( self, "APCartOS", "Layer failed to load! Try again?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if tryagain == QMessageBox.Yes: self.addLayer() else: return # Add layer to the registry QgsMapLayerRegistry.instance().addMapLayer(layer) # QgsMapLayerRegistry.instance().layerWasAdded.connect(self.addLayerToLegend) # Set extent to the extent of our layer self.activeCanvas.setExtent(layer.extent()) # Set up the map canvas layer set cl = QgsMapCanvasLayer(layer) self.activeCanvas.innerlayers.append(cl) self.activeCanvas.setLayerSet(self.activeCanvas.innerlayers) # print layers def enableEditingButton(self): layer = self.legend.activeLayer().layer() if type(layer).__name__ == "QgsVectorLayer": self.actionStart_editing.setEnabled(1) else: self.actionStart_editing.setEnabled(0) def toogleEditing(self): layer = self.legend.activeLayer().layer() if not layer.isEditable(): layer.startEditing() self.activeCanvas.refresh() self.actionStart_editing.setText("Stop editing") else: if not layer.isModified(): layer.commitChanges() else: reply = QMessageBox.question( self, "APCartOS", "Save changes?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if reply == QMessageBox.Yes: layer.commitChanges() else: layer.rollBack() self.actionStart_editing.setText("Start editing")
def legend(self, handles, labels, *args, **kwargs): """ Place a legend in the figure. Labels are a sequence of strings, handles is a sequence of :class:`~matplotlib.lines.Line2D` or :class:`~matplotlib.patches.Patch` instances, and loc can be a string or an integer specifying the legend location USAGE:: legend( (line1, line2, line3), ('label1', 'label2', 'label3'), 'upper right') The *loc* location codes are:: 'best' : 0, (currently not supported for figure legends) 'upper right' : 1, 'upper left' : 2, 'lower left' : 3, 'lower right' : 4, 'right' : 5, 'center left' : 6, 'center right' : 7, 'lower center' : 8, 'upper center' : 9, 'center' : 10, *loc* can also be an (x,y) tuple in figure coords, which specifies the lower left of the legend box. figure coords are (0,0) is the left, bottom of the figure and 1,1 is the right, top. The legend instance is returned. The following kwargs are supported *loc* the location of the legend *numpoints* the number of points in the legend line *prop* a :class:`matplotlib.font_manager.FontProperties` instance *pad* the fractional whitespace inside the legend border *markerscale* the relative size of legend markers vs. original *shadow* if True, draw a shadow behind legend *labelsep* the vertical space between the legend entries *handlelen* the length of the legend lines *handletextsep* the space between the legend line and legend text *axespad* the border between the axes and legend edge .. plot:: mpl_examples/pylab_examples/figlegend_demo.py """ handles = flatten(handles) l = Legend(self, handles, labels, *args, **kwargs) self.legends.append(l) return l
class Drawable(): """ The :class:`~Drawable` class wraps a matplotlib figure and axes to provide additional functionality. If no axes is given, the default plot axes (:code:`plt.gca()`) is used. The :class:`~Drawable` class can be used as a normal `matplotlib.axes.Axes <https://matplotlib.org/api/axes_api.html>`_ object with additional functionality. The axes functionality can be called on the :class:`~Drawable` class. The :class:`~Drawable` instance re-routes method and attribute calls to the `matplotlib.axes.Axes <https://matplotlib.org/api/axes_api.html>`_ instance. :ivar figure: The figure that the :class:`~Drawable` class wraps. :vartype figure: :class:`matplotlib.figure.Figure` :ivar axes: The axes where the drawable will draw. :vartype axes: :class:`matplotlib.axes.Axes` :ivar secondary: The secondary axes. Some visualizations, such as the :class:`~slope.slope.Slope` graph uses them to draw different ticks on each y-axis. :vartype secondary: :class:`matplotlib.axes.Axes` :var caption: The caption, displayed under the title. :vartype caption: :class:`~text.annotation.Annotation` :ivar bar100: The 100% bar chart visualization that is being used. When no visualization has been created, it is set to `None`. It is instantiated the first time a 100% bar chart is drawn. :vartype bar100: None or :class:`~bar.100.Bar100` :ivar population: The population visualization that is being used. When no visualization has been created, it is set to `None`. It is instantiated the first time a population is drawn. :vartype population: None or :class:`~population.population.Population` :ivar timeseries: The time series object that is being used. When no visualization has been created, it is set to `None`. It is instantiated the first time a time series is drawn. :vartype timeseries: None or :class:`~timeseries.timeseries.TimeSeries` :ivar legend: The figure's legend. :vartype legend: :class:`~legend.Legend` :ivar annotations: The annotations in the visualization. :vartype annotations: list of :class:`~text.annotation.Annotation` """ def __init__(self, figure, axes=None): """ Create the drawable with the figure. :param figure: The figure that the :class:`~Drawable` class wraps. This is mainly used to get the figure renderer. :type figure: :class:`matplotlib.figure.Figure` :param axes: The axes (or subplot) where to plot visualizations. If `None` is given, the plot's main subplot is used instead. :type axes: `None` or :class:`matplotlib.axes.Axes` """ self.figure = figure self.axes = plt.gca() if axes is None else axes self.secondary = self.axes self.caption = None self.annotations = [] self.legend = Legend(self) self.bar100 = None self.population = None self.slope = None self.timeseries = None def set_caption(self, caption, alpha=0.8, lineheight=1.25, *args, **kwargs): """ Add a caption to the subplot. The caption is added just beneath the title. The method re-draws the title to make space for the caption. The caption is a :class:`~text.text.Annotation` object. Any arguments that the constructor accepts can be provided to this method. :param caption: The caption to add to the axes. :type caption: str :param alpha: The opacity of the caption between 0 and 1. :type alpha: float :param lineheight: The space between lines. :type lineheight: float :return: The drawn caption. :rtype: :class:`~text.annotation.Annotation` """ self.caption = Annotation(self, caption, (0, 1), 1, va='bottom', alpha=alpha, lineheight=lineheight, transform=self.axes.transAxes, *args, **kwargs) self.caption.draw() self.redraw() return self.caption def redraw(self): """ Re-create the title, with the goal of leaving enough space to fit the caption and the legend. Afterwards, it redraws the legend. """ self.figure.canvas.draw() # redraw all visualizations for viz in [self.bar100, self.population, self.slope, self.timeseries]: if viz: viz.redraw() # redraw all annotations for annotation in self.annotations: annotation.redraw() self.legend.redraw() self._redraw_caption() self._redraw_title() def _redraw_caption(self): """ Re-draw the caption, re-positioning so that it does not overlap with the legend or axes. """ if not self.caption: return figure, axes = self.figure, self.axes """ Move the caption up to make space for the legend and the label. """ y = 1.015 y += self.legend.get_virtual_bb(transform=self.axes.transAxes).height """ If the x-label is on top, make space for it in the caption. In this case, it is assumed that the ticks are also at the top. This is because for some reason they may be set to 'unknown'. """ if axes.xaxis.get_label_position() == 'top': y += self._get_xlabel(transform=self.axes.transAxes).height * 2 xtick_labels_bb = self._get_xtick_labels(transform=axes.transAxes) if xtick_labels_bb: y += max(xtick_labels_bb, key=lambda bb: bb.height).height * 2 self.caption.redraw() self.caption.set_position((0, y), ha='left', va='bottom', transform=self.axes.transAxes) def _redraw_title(self): """ Re-draw the title, adding enough padding so that there is enough space for the axes label, the legend and the caption. """ figure, axes = self.figure, self.axes title = axes.get_title(loc='left') """ Get the height of the caption and the height of the legend. The title should allow enough padding to make space for both. """ caption_height = 0 if self.caption: caption_height = util.to_px( axes, self.caption.get_virtual_bb(transform=axes.transAxes), transform=axes.transAxes).height legend_height = util.to_px( axes, self.legend.get_virtual_bb(transform=axes.transAxes), transform=axes.transAxes).height """ If the x-label is on top, make space for it in the title. In this case, it is assumed that the ticks are also at the top. This is because for some reason they may be set to 'unknown'. """ label_height = 0 if axes.xaxis.get_label_position() == 'top': label_bb = self._get_xlabel(transform=axes.transData) label_height = util.to_px(axes, label_bb, transform=axes.transData).height * 2 xtick_labels_bb = self._get_xtick_labels(transform=axes.transData) if xtick_labels_bb: label_bb = max(xtick_labels_bb, key=lambda bb: bb.height) label_height += util.to_px( axes, label_bb, transform=axes.transData).height * 2 """ Add some extra padding to the height. """ height = abs(caption_height) + abs(legend_height) + abs(label_height) pad_px = abs( self.axes.transAxes.transform((0, 0.015))[1] - self.axes.transAxes.transform((0, 0))[1]) pad = pad_px * 2 self.axes.set_title(title, loc='left', pad=(height + pad)) def _get_xlabel(self, transform=None): """ Get the bounding box of the x-axis label. :param transform: The bounding box transformation. If `None` is given, the data transformation is used. :type transform: None or :class:`matplotlib.transforms.TransformNode` :return: The bounding box of the x-axis label. :rtype: :class:`matplotlib.transforms.Bbox` """ figure, axes = self.figure, self.axes transform = transform or axes.transData return util.get_bb(figure, axes, axes.xaxis.get_label(), transform=transform) def _get_xtick_labels(self, transform=None): """ Get the bounding box of the x-axis tick labels. :param transform: The bounding box transformation. If `None` is given, the data transformation is used. :type transform: None or :class:`matplotlib.transforms.TransformNode` :return: The bounding box of the x-axis label. :rtype: :class:`matplotlib.transforms.Bbox` """ figure, axes = self.figure, self.axes figure.canvas.draw() transform = transform or axes.transData return [ util.get_bb(figure, axes, label, transform=transform) for label in axes.xaxis.get_ticklabels(which='both') ] def savefig(self, *args, **kwargs): """ A special function that calls the `matplotlib.pyplot.savefig <https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.savefig.html>`_ function. Before doing that, the function first redraws the drawable. This function is very important when the title and caption are set before drawing the visualization. In these cases, it is possible that the legend or the plot labels cause the caption or title to overlap with the plot. """ self.redraw() plt.savefig(*args, **kwargs) def show(self, *args, **kwargs): """ A special function that calls the `matplotlib.pyplot.show <https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.show.html>`_ function. Before doing that, the function first redraws the drawable. This function is very important when the title and caption are set before drawing the visualization. In these cases, it is possible that the legend or the plot labels cause the caption or title to overlap with the plot. """ self.redraw() plt.show(*args, **kwargs) def __getattr__(self, name): """ The magic function through which most of :class:`~Drawable`'s functionality passes. This function receives any unknown call and passes it on to the :class:`~Drawable`'s `matplotlib.axes.Axes <https://matplotlib.org/api/axes_api.html>`_. This function automatically checks whether the call is referencing a function or a variable. :param name: The name of the attribute. :type name: str :return: The function applied on the axes. :rtype: function """ def method(*args, **kwargs): """ Try to get the attribute from the axes. If arguments were given, then the attribute is treated as a method call. Otherwise, it is treated as a normal attribute call. """ if callable(getattr(self.axes, name)): return getattr(self.axes, name)(*args, **kwargs) else: return getattr(self.axes, name) return method """ Visualizations """ def annotate(self, text, x, y, marker=None, pad=0.01, *args, **kwargs): """ Add a text annotation to the plot. This function can be used to draw attention to certain or describe the visualization on the plot itself. Any additional arguments and keyword arguments are passed on to the :class:`~text.annotation.Annotation`'s :func:`~text.annotation.Annotation.draw` function. For example, the `va` can be provided to specify the text's vertical alignment, andthe `align` parameter can be used to specify the text's alignment. :param text: The text of the annotation to draw. :type text: str :param x: A tuple containing the start and end x-coordinates of the annotation. :type x: tuple :param y: The y-coordinate of the annotation. :type y: float :param marker: The marker style. If it is not given, no marker is drawn. :type marker: None or dict :param pad: The amount of padding applied to the annotation. :type pad: float :return: The drawn annotation. :rtype: :class:`~text.annotation.Annotation` """ annotation = Annotation(self, text, x, y, pad=pad, *args, **kwargs) self.figure.canvas.draw() """ Draw the marker if it is given. The color is obtained from the kwargs if a marker color is not given. The point of the marker is based on the alignment of the annotation. """ if marker is not None: marker = dict( marker) # make a copy to avoid overwriting dictionaries marker['color'] = marker.get('color', kwargs.get('color')) if kwargs.get('align', 'left') == 'left': self.axes.plot(x[0], y, *args, **marker) elif kwargs.get('align') == 'right': self.axes.plot(x[1], y, *args, **marker) elif kwargs.get('align') == 'center': self.axes.plot((x[0] + x[1]) / 2., y, *args, **marker) self.annotations.append(annotation) return annotation def draw_bar_100(self, *args, **kwargs): """ Draw a bar chart that stacks up to 100% on this :class:`~Drawable`. The arguments and keyword arguments are those supported by the :class:`~bar.bar100.Bar100`'s :func:`~bar.bar100.Bar100.draw` method. :return: A list of drawn bars. :rtype: list of :class:`matplotlib.patches.Rectangle` """ self.bar100 = self.bar100 or Bar100(self) return self.bar100.draw(*args, **kwargs) def draw_graph(self, *args, **kwargs): """ Draw a graph visualization on this :class:`~Drawable`. The arguments and keyword arguments are those supported by the :class:`~graph.graph.Graph`'s :func:`~graph.graph.Graph.draw` method. :return: A tuple containing the list of drawn nodes, the rendered node names, edges, and the rendered edge names. :rtype: tuple """ graph = Graph(self) return graph.draw(*args, **kwargs) def draw_population(self, *args, **kwargs): """ Draw a population chart on this :class:`~Drawable`. The arguments and keyword arguments are those supported by the :class:`~population.population.Population`'s :func:`~population.population.Population.draw` method. :return: A list of drawn scatter points, separated by column. :rtype: list of list of :class:`matplotlib.collections.PathCollection` """ self.population = self.population if self.population else Population( self) return self.population.draw(*args, **kwargs) def draw_slope(self, *args, **kwargs): """ Draw a slope graph with two points on this :class:`~Drawable`. The arguments and keyword arguments are those supported by the :class:`~slope.slope.Slope`'s :func:`~slope.slope.Slope.draw` method. :return: A tuple made up of the drawn plot and label. :rtype: tuple (:class:`matplotlib.lines.Line2D`, list of :class:`~text.annotation.Annotation`) """ self.slope = self.slope or Slope(self) return self.slope.draw(*args, **kwargs) def draw_text_annotation(self, *args, **kwargs): """ Draw a text annotation visualization on this :class:`~Drawable`. The arguments and keyword arguments are those supported by the :class:`~text.text.TextAnnotation`'s :func:`~text.text.TextAnnotation.draw` method. :return: The drawn text annotation's lines. Each line is made up of tuples of lists. The first list in each tuple is the list of legend labels. The second list in each tuple is the list of actual tokens. :rtype: list of tuple """ text_annotation = TextAnnotation(self) return text_annotation.draw(*args, **kwargs) def draw_time_series(self, *args, **kwargs): """ Draw a time series visualization on this :class:`~Drawable`. The arguments and keyword arguments are those supported by the :class:`~timeseries.timeseries.TimeSeries`' :func:`~timeseries.timeseries.TimeSeries.draw` method. :return: A tuple made up of the drawn plot and label. :rtype: tuple """ self.timeseries = self.timeseries or TimeSeries(self) return self.timeseries.draw(*args, **kwargs)
class WaferMap: def __init__(self): self.img = None self.legend = Legend() self.title = Title() self.autosize = App.cfg['image']['autosize'] self.autosize_minpx = App.cfg['image']['autosize_minpx'] self.basewidth = App.cfg['image']['basewidth'] self.baseheight = App.cfg['image']['baseheight'] self.padding = App.cfg['image']['padding'] self.showborder = App.cfg['image']['border'] self.showtitle = App.cfg['title']['show'] self.showlegend = App.cfg['legend']['show'] self.showgrid = App.cfg['axis']['grid'] self.showaxis = App.cfg['axis']['show'] self.showflat = App.cfg['image']['flatline'] self.showtimestamp = App.cfg['image']['timestamp'] self.timestampcolor = App.cfg['image']['timestampcolor'] self.bordercolor = App.cfg['image']['bordercolor'] self.streetcolor = App.cfg['image']['streetcolor'] self.flatcolor = App.cfg['image']['flatcolor'] self.gridcolor = App.cfg['axis']['gridcolor'] self.axiscolor = App.cfg['axis']['color'] self.axisfont = App.cfg['axis']['font'] self.bgcolor = App.cfg['image']['bgcolor'] def buildmap(self, lot, wfr, testset): #--------------------------------------------------------------------------- # Get actual extents... that is number of rows & cols in the wafermap, # If the demodata routine was used, a random number of rows/cols were # generated. #--------------------------------------------------------------------------- min_Y = min(d.Y for d in wfr.dielist) max_Y = max(d.Y for d in wfr.dielist) min_X = min(d.X for d in wfr.dielist) max_X = max(d.X for d in wfr.dielist) col_count = max_X - min_X+1 row_count = max_Y - min_Y+1 log.debug('Die Extents: X = {} to {} Y = {} to {}'.format(min_X, max_X, min_Y, max_Y)) log.debug('Columns (X) = {}, Rows (Y) = {} (Total Die={})'.format(col_count, row_count, len(wfr.dielist))) if self.basewidth >= self.baseheight: sizeRatio = float(self.basewidth) / float(self.baseheight) else: sizeRatio = float(self.baseheight) / float(self.basewidth) log.debug('Size Ratio: {:.2f}, width={}, height={}'.format(sizeRatio, self.basewidth, self.baseheight)) #--------------------------------------------------------------------------- # If image autosize is enabled, the canvas size may be increased to ensure # die are visible. This will force a minimum pixel size of "autosize_minpx". # The original aspect ratio will be maintained. #--------------------------------------------------------------------------- if self.autosize: autominX = (self.autosize_minpx * col_count) + (2 * self.padding) autominY = (self.autosize_minpx * row_count) + (2 * self.padding) log.debug('Autosize Minimum Map Size X={} Y={} at Autopx = {}'.format( autominX, autominY, self.autosize_minpx)) if(autominX > self.basewidth): self.basewidth = autominX self.baseheight = int (float(self.basewidth) * float(sizeRatio)) log.debug('Autosize (autominX > bw) Updated sizes - width={}, height={}, sizeRatio={}'.format( self.basewidth, self.baseheight, float(sizeRatio))) elif (autominY > self.baseheight): self.baseheight = autominY self.basewidth = int (float(self.baseheight) * float(sizeRatio)) log.debug('Autosize (autominY > bh) Updated sizes - width={}, height={}, sizeRatio={}'.format( self.basewidth, self.baseheight, float(sizeRatio))) else: log.debug('Autosize sizing not required, width={}, height={}, autominX={}, autominY={}'.format( self.basewidth, self.baseheight, autominX, autominY)) #Store updated values back in cfg # App.cfg['image']['basewidth'] = bw # App.cfg['image']['baseheight'] = bh # Nominal size of map excluding padding mapX = self.basewidth - (self.padding *2) mapY = self.baseheight - (self.padding *2) log.debug('Map Space (BaseSize - 2*Padding) (X,Y)=({},{}) Padding = {}'.format( mapX, mapY, self.padding)) # Calculate the die's pixel size - width (X) and height (Y) based on map size # divided by the number of rows & columns X_pixels = mapX / col_count #X-diesize in pixels Y_pixels = mapY / row_count #Y diesize in pixels log.debug('Pixels per die: X={}, Y={}'.format(X_pixels, Y_pixels)) #--------------------------------------------------------------------------- # Unless the nominal image size (Canvas size minus padding) happens to be an # exact multiple of the calculated die size in pixels, we will have some # space left on all sides. # Calculate the extra space so we can center the image on the canvas #--------------------------------------------------------------------------- slackX = (mapX - (X_pixels * col_count)) / 2 slackY = (mapY - (Y_pixels * row_count)) / 2 log.debug('Slack: X={}, Y={}'.format(slackX, slackY)) log.debug('Calculated Map Size (excluding slack) - (X,Y) = ({},{})'.format( X_pixels * col_count, Y_pixels * row_count)) # #------------------------------------------------------------------------- # Have the legend calculate its size, it will be rendered later, but we # need to know the space it will take up so we can adjust the canvas size # Actual Map width is then adjusted to allow for legend. #-------------------------------------------------------------------------- actualWidth = self.basewidth if self.showlegend: self.legend.getsize(wfr.dielist, testset) actualWidth += self.legend.width log.debug('Legend Width = {}'.format(self.legend.width)) #-------------------------------------------------------------------------- # baseheight and fontsize adjusted for title #-------------------------------------------------------------------------- # Start with a reasonable guess for the size of the title font and then # autosize it from there. May not be the most efficient way to do # this, but it works for now... #-------------------------------------------------------------------------- actualHeight = self.baseheight titleheight = 0 if self.showtitle: #A guess... title_fontsize = actualWidth//40 titlekeys = self.title.autosize(lot.mir, wfr, title_fontsize, testset) upsearch=False # While title string width at current font size is > canvas width, decrease # font size and re-evaluate if(titlekeys['maxfw'] > actualWidth): while titlekeys['maxfw'] > actualWidth: upsearch=False title_fontsize -= 1 titlekeys = self.title.autosize(lot.mir, wfr, title_fontsize, testset) # print "D:imageWith = {}, fontsize = {}, maxfw={}, height={}".format(actualWidth, title_fontsize, titlekeys['maxfw'], titlekeys['maxfh']) # Otherwise, font is too small, increase it and re-evaluate else: while titlekeys['maxfw'] < actualWidth: upsearch=True title_fontsize += 1 titlekeys = self.title.autosize(lot.mir, wfr, title_fontsize, testset) # print "U:imageWith = {}, fontsize = {}, maxfw={}, height={}".format(actualWidth, title_fontsize, titlekeys['maxfw'], titlekeys['maxfh']) # If we were decreasing font size when the while condition became false, all the titlekeys are properly set, # from the last loop cycle.... but if we were increasing font size, the last loop cycle left the titlekeys # in a state for a larger font, so decrease the font size by one and re-evaluate the titlekeys if(upsearch): title_fontsize -=1 titlekeys = self.title.autosize(lot.mir, wfr, title_fontsize, testset) titleheight = titlekeys['maxfh'] # Increase canvas size to allow for title actualHeight += titleheight log.debug('Title Height = {}, Title Fontsize = {}'.format(titleheight, title_fontsize)) #-------------------------------------------------------------------------- # Adjust Width and Height for axis space #-------------------------------------------------------------------------- # if self.showaxis: # actualWidth += axis_space # actualHeight += axis_space #-------------------------------------------------------------------------- # Get axis space, add in 10 pixels for flat spacing. (the axes are # shifted left or up by 10 pixels when they are drawn to account for the # 10 pixels). #-------------------------------------------------------------------------- axis_space = 0 if self.showaxis: axis_space = maputil.do_axis(self, wfr.dielist, X_pixels, Y_pixels) + 10 log.debug('Axis Space = {}'.format(axis_space)) actualWidth += axis_space actualHeight += axis_space log.debug('Axis space = {}'.format(axis_space)) log.debug('Actual Image Size (X,Y) = ({},{})'.format(actualWidth, actualHeight)) #-------------------------------------------------------------------------- # Create the canvas and draw the wafermap #-------------------------------------------------------------------------- self.img = Image.new('RGB', (actualWidth,actualHeight), self.bgcolor) # Get the drawable object... dr = ImageDraw.Draw(self.img) # map_extents are used for tracking the actual pixel extents of the drawn map which makes # calculating the location to draw the flat & axis labels a bit easier... map_extents = {'minx':65535, 'miny':65535, 'maxx':0, 'maxy':0} # limit flag_size factor to a minimum of 1.0 (or else flag will exceed die boundary) App.cfg['flag']['size_factor'] = max(App.cfg['flag']['size_factor'], 1.0) # if we want the grid, draw it first so it's behind everything else... if self.showgrid: maputil.do_grid(self, wfr.dielist, X_pixels, Y_pixels, self.padding, axis_space, slackX, slackY, titleheight) idx=0 imap = [] #----------------------------------------------------------- # Draw All die loop #----------------------------------------------------------- for eachDie in wfr.dielist: mybin=getattr(eachDie,App.cfg['binmap']) idx+=1 x1 = self.padding + axis_space + ((eachDie.X - min_X) * X_pixels) + slackX y1 = self.padding + axis_space + ((eachDie.Y - min_Y) * Y_pixels) + slackY + titleheight x2 = x1 + X_pixels y2 = y1 + Y_pixels # Draw the die and file with bincolor if eachDie.testset == testset: dr.rectangle([x1,y1,x2,y2], fill=App.cfg['bin'][str(mybin)]['color'], outline=self.streetcolor) imap.append({'Row':eachDie.Y,'Col':eachDie.X,'x':x1,'y':y1,'width':x2-x1,'height':y2-y1}) # Draw any specified symbols, if enabled if App.cfg['enable_symbols']: maputil.do_symbol(dr,x1,y1,x2,y2, False, **App.cfg['bin'][str(mybin)]) # Draw flags if appropriate xystr="{}|{}".format(eachDie.X, eachDie.Y) # Bindict Key if (wfr.diedict[xystr]['flags'] & constants.FLAG_MULTITEST) and App.cfg['flag']['show']: if wfr.diedict[xystr]['flags'] & constants.FLAG_DUPLICATE_BIN: flagcolor= App.cfg['flag']['duplicate_color'] else: flagcolor= App.cfg['flag']['split_color'] flagX = int(float(x2-x1)/float(App.cfg['flag']['size_factor'])) flagY = int(float(y2-y1)/float(App.cfg['flag']['size_factor'])) dr.polygon([(x1,y1+flagY),(x1,y1),(x1+flagX,y1)], fill=flagcolor, outline=(0,0,0)) # track minimum and maximums of actual drawn wafermap. map_extents['minx']= min(map_extents['minx'],x1) map_extents['maxx']= max(map_extents['maxx'],x2) map_extents['miny']= min(map_extents['miny'],y1) map_extents['maxy']= max(map_extents['maxy'],y2) #----------------------------------------------------------- log.debug('Drawn Map Extents = {}'.format(map_extents)) # do_html(imap, actualWidth, actualHeight) #-------------------------------------------------------------------------- # Draw title area #-------------------------------------------------------------------------- if self.showtitle: dr.rectangle([0,0,actualWidth-1,titleheight], fill=self.title.bgcolor, outline=self.bordercolor) self.title.render(self.img, wfr, titlekeys) #-------------------------------------------------------------------------- # Draw legend, border, flat indicator, timestamp, axis labels #-------------------------------------------------------------------------- if self.showlegend: self.legend.render(self.img, self.basewidth + axis_space , self.padding + titleheight) if self.showborder: dr.rectangle([0,0,actualWidth-1,actualHeight-1], fill=None, outline=self.bordercolor) if self.showflat: maputil.do_flat(self, lot.wcr['WF_FLAT'], map_extents, self.flatcolor) if self.showtimestamp: maputil.do_timestamp(self, color=self.timestampcolor) if self.showaxis: maputil.do_axis(self, wfr.dielist, X_pixels, Y_pixels, map_extents, True) return(self.img)
def buildmap(lot, wfr, testset): #--------------------------------------------------------------------------- # Get actual extents... that is number of rows & cols in the wafermap, # If the demodata routine was used, a random number of rows/cols were # generated. #--------------------------------------------------------------------------- min_Y = min(d.Y for d in wfr.dielist) max_Y = max(d.Y for d in wfr.dielist) min_X = min(d.X for d in wfr.dielist) max_X = max(d.X for d in wfr.dielist) col_count = max_X - min_X+1 row_count = max_Y - min_Y+1 log.debug('Die Extents: X = {} to {} Y = {} to {}'.format(min_X, max_X, min_Y, max_Y)) log.debug('Columns (X) = {}, Rows (Y) = {} (Total Die={})'.format(col_count, row_count, len(wfr.dielist))) baseWidth=App.cfg['image']['basewidth'] baseHeight=App.cfg['image']['baseheight'] if baseWidth >= baseHeight: sizeRatio = float(baseWidth) / float(baseHeight) else: sizeRatio = float(baseHeight) / float(baseWidth) log.debug('Size Ratio: {:.2f}, width={}, height={}'.format(sizeRatio, baseWidth, baseHeight)) #--------------------------------------------------------------------------- # If image autosize is enabled, the canvas size may be increased to ensure # die are visible. This will force a minimum pixel size of "autosize_minpx". # The original aspect ratio will be maintained. #--------------------------------------------------------------------------- padding=App.cfg['image']['padding'] autosize_minpx = App.cfg['image']['autosize_minpx'] if App.cfg['image']['autosize']: # conveniences for calculations... bw = App.cfg['image']['basewidth'] bh = App.cfg['image']['baseheight'] autominX = (autosize_minpx * col_count) + (2 * padding) autominY = (autosize_minpx * row_count) + (2 * padding) log.debug('Autosize Minimum Map Size X={} Y={} at Autopx = {}'.format( autominX, autominY, autosize_minpx)) if(autominX > bw): bw = autominX bh = int (float(bw) * float(sizeRatio)) log.debug('Autosize (autominX > bw) Updated sizes - width={}, height={}, sizeRatio={}'.format(bw, bh, float(sizeRatio))) elif (autominY > bh): bh = autominY bw = int (float(bh) * float(sizeRatio)) log.debug('Autosize (autominY > bh) Updated sizes - width={}, height={}, sizeRatio={}'.format(bw, bh, float(sizeRatio))) else: log.debug('Autosize sizing not required, width={}, height={}, autominX={}, autominY={}'.format(bw, bh, autominX, autominY)) #Store updated values back in cfg App.cfg['image']['basewidth'] = bw App.cfg['image']['baseheight'] = bh # Nominal size of map excluding padding mapX = App.cfg['image']['basewidth'] - (padding *2) mapY = App.cfg['image']['baseheight'] - (padding *2) log.debug('Map Space (BaseSize - 2*Padding) (X,Y)=({},{})'.format(mapX,mapY)) #Calculate the die's pixel size width (X) and height (Y) X_pixels = mapX / col_count #X-diesize in pixels Y_pixels = mapY / row_count #Y diesize in pixels log.debug('Pixels per die: X={}, Y={}'.format(X_pixels, Y_pixels)) #--------------------------------------------------------------------------- # Unless the nominal image size (Canvas size minus padding) happens to be an # exact multiple of the calculated die size in pixels, we will have some # space left on all sides. # Calculate the extra space so we can center the image on the canvas #--------------------------------------------------------------------------- slackX = (mapX - (X_pixels * col_count)) / 2 slackY = (mapY - (Y_pixels * row_count)) / 2 log.debug('Slack: X={}, Y={}'.format(slackX, slackY)) log.debug('Calculated Map Size (excluding slack) - (X,Y) = ({},{})'.format( X_pixels * col_count, Y_pixels * row_count)) # #------------------------------------------------------------------------- # Generate the legend data, it will be rendered later, but we require # knowing the space it will take up so we can adjust the canvas size #------------------------------------------------------------------------- wfr.legend = Legend(wfr.dielist, testset) #-------------------------------------------------------------------------- # baseWidth adjusted to allow for legend. #-------------------------------------------------------------------------- actualWidth = App.cfg['image']['basewidth'] if App.cfg['legend']['show']: actualWidth += wfr.legend.legend['legendwidth'] log.debug('Legend Width = {}'.format(wfr.legend.legend['legendwidth'])) #-------------------------------------------------------------------------- # Get axis space, add in 10 pixels for flat spacing. (the axes are # shifted left or up by 10 pixels when they are drawn to account for these # 10 pixels). #-------------------------------------------------------------------------- axis_space = 0 if App.cfg['axis']['show']: axis_space = _do_axis(wfr.dielist,X_pixels,Y_pixels) + 10 log.debug('Axis Space = {}'.format(axis_space)) #-------------------------------------------------------------------------- # baseheight and fontsize adjusted for title #-------------------------------------------------------------------------- # Start with a reasonable guess for the size of the title font and then # autosize it from there. May not be the most efficient way to do # this, but it works for now... #-------------------------------------------------------------------------- actualHeight = App.cfg['image']['baseheight'] titleheight = 0 if App.cfg['title']['show']: #A guess... title_fontsize = actualWidth//40 titlekeys = wfr.legend.titlesize(lot.mir, wfr, title_fontsize, testset) upsearch=False if(titlekeys['maxfw'] > actualWidth): while titlekeys['maxfw'] > actualWidth: upsearch=False title_fontsize -= 1 titlekeys = wfr.legend.titlesize(lot.mir, wfr, title_fontsize, testset) # print "D:imageWith = {}, fontsize = {}, maxfw={}, height={}".format(actualWidth, title_fontsize, titlekeys['maxfw'], titlekeys['maxfh']) else: while titlekeys['maxfw'] < actualWidth: upsearch=True title_fontsize += 1 titlekeys = wfr.legend.titlesize(lot.mir, wfr, title_fontsize, testset) # print "U:imageWith = {}, fontsize = {}, maxfw={}, height={}".format(actualWidth, title_fontsize, titlekeys['maxfw'], titlekeys['maxfh']) # If we were decreasing font size when the while condition became false, all the titlekeys are properly set, # from the last loop cycle.... but if we were increasing font size, the last loop cycle left the titlekeys # in a state for a larger font, so decrease the font size by one and re-evaluate the titlekeys if(upsearch): title_fontsize -=1 titlekeys = wfr.legend.titlesize(lot.mir, wfr, title_fontsize, testset) titleheight = titlekeys['maxfh'] actualHeight += titleheight log.debug('Title Height = {}, Title Fontsize = {}'.format(titleheight, title_fontsize)) #-------------------------------------------------------------------------- # Adjust Width and Height for axis space #-------------------------------------------------------------------------- if(App.cfg['axis']['show']): actualWidth += axis_space actualHeight += axis_space log.debug('Actual Image Size (X,Y) = ({},{})'.format(actualWidth, actualHeight)) log.debug('Padding = {}'.format(padding)) #-------------------------------------------------------------------------- # Create the canvas and draw the wafermap #-------------------------------------------------------------------------- img = Image.new('RGB', (actualWidth,actualHeight), App.cfg['image']['bgcolor']) # Get the drawable object... dr = ImageDraw.Draw(img) # map_extents are used for tracking the actual pixel extents of the drawn map which makes # calculating the location to draw the flat & axis labels a bit easier... map_extents = {'minx':65535, 'miny':65535, 'maxx':0, 'maxy':0} # limit flag_size factor to a minimum of 1.0 (or else flag will exceed die boundary) App.cfg['flag']['size_factor'] = max(App.cfg['flag']['size_factor'], 1.0) # if we want the grid, draw it first so it's behind everything else... if App.cfg['axis']['grid']: _do_grid(wfr.dielist, img, X_pixels, Y_pixels, padding, axis_space, slackX, slackY, titleheight) idx=0 imap = [] for eachDie in wfr.dielist: mybin=getattr(eachDie,App.cfg['binmap']) idx+=1 x1 = padding + axis_space + ((eachDie.X - min_X) * X_pixels) + slackX y1 = padding + axis_space + ((eachDie.Y - min_Y) * Y_pixels) + slackY + titleheight x2 = x1 + X_pixels y2 = y1 + Y_pixels # Draw the die and file with bincolor if eachDie.testset == testset: dr.rectangle([x1,y1,x2,y2], fill=App.cfg['bin'][str(mybin)]['color'], outline=(0,0,0)) imap.append({'Row':eachDie.Y,'Col':eachDie.X,'x':x1,'y':y1,'width':x2-x1,'height':y2-y1}) # Draw any specified symbols, if enabled if App.cfg['enable_symbols']: do_symbol(dr,x1,y1,x2,y2, False, **App.cfg['bin'][str(mybin)]) # Draw flags if appropriate xystr="{}|{}".format(eachDie.X, eachDie.Y) # Bindict Key if (wfr.diedict[xystr]['flags'] & constants.FLAG_MULTITEST) and App.cfg['flag']['show']: if wfr.diedict[xystr]['flags'] & constants.FLAG_DUPLICATE_BIN: flagcolor= App.cfg['flag']['duplicate_color'] else: flagcolor= App.cfg['flag']['split_color'] flagX = int(float(x2-x1)/float(App.cfg['flag']['size_factor'])) flagY = int(float(y2-y1)/float(App.cfg['flag']['size_factor'])) dr.polygon([(x1,y1+flagY),(x1,y1),(x1+flagX,y1)], fill=flagcolor, outline=(0,0,0)) # track minimum and maximums of actual drawn wafermap. map_extents['minx']= min(map_extents['minx'],x1) map_extents['maxx']= max(map_extents['maxx'],x2) map_extents['miny']= min(map_extents['miny'],y1) map_extents['maxy']= max(map_extents['maxy'],y2) log.debug('Drawn Map Extents = {}'.format(map_extents)) # do_html(imap, actualWidth, actualHeight) #-------------------------------------------------------------------------- # Draw title area #-------------------------------------------------------------------------- if App.cfg['title']['show']: dr.rectangle([0,0,actualWidth,titleheight], fill=App.cfg['title']['bgcolor'], outline=(0,0,0)) wfr.legend.render_title(img, wfr, titlekeys) #-------------------------------------------------------------------------- # Draw legend, border, flat indicator, timestamp, axis labels #-------------------------------------------------------------------------- if App.cfg['legend']['show']: wfr.legend.render(App.cfg['image']['basewidth'] + axis_space , padding + titleheight, img) if App.cfg['image']['border']: dr.rectangle([0,0,actualWidth-1,actualHeight-1], fill=None, outline=(0,0,0)) if App.cfg['image']['flatline']: wfr.legend.render_flat(img, lot.wcr['WF_FLAT'], map_extents, (127,0,0)) # wfr.legend.render_flat(img, 'L', map_extents, (127,0,0)) if App.cfg['image']['timestamp']: wfr.legend.timestamp(img, color=(0,0,127)) if App.cfg['axis']['show']: _do_axis(wfr.dielist, X_pixels, Y_pixels, map_extents, img, True) # # Add some debug entries to the configuration for examination during a dump # # (-z command option) # App.cfg['debug']={} # # App.cfg['debug']['Map'] = {'mapX':mapX, 'mapY':mapY, 'X_pixels':X_pixels, 'Y_pixels':Y_pixels, # 'Actual Width':actualWidth, 'Actual Height':actualHeight, # 'Title Height':titleheight, 'Axis Space':axis_space, # 'Slack X':slackX, 'Slack Y':slackY, # 'Drawn Map Extents':map_extents} # # # print "Map X,Y............... {},{}".format(mapX,mapY) # # print "Die Pixels X,Y........ {},{}".format(X_pixels,Y_pixels) # # print "Actual Image Size..... {},{}".format(actualWidth,actualHeight) # # print "Title Height.......... {}".format(titleheight) # # print "Axis Space............ {}".format(axis_space) # # print "Padding............... {}".format(padding) # # print "Drawn Map Extents..... {}".format(map_extents) return(img)
def legend(self, handles, labels, *args, **kwargs): """ Place a legend in the figure. Labels are a sequence of strings, handles is a sequence of :class:`~matplotlib.lines.Line2D` or :class:`~matplotlib.patches.Patch` instances, and loc can be a string or an integer specifying the legend location USAGE:: legend( (line1, line2, line3), ('label1', 'label2', 'label3'), 'upper right') The *loc* location codes are:: 'best' : 0, (currently not supported for figure legends) 'upper right' : 1, 'upper left' : 2, 'lower left' : 3, 'lower right' : 4, 'right' : 5, 'center left' : 6, 'center right' : 7, 'lower center' : 8, 'upper center' : 9, 'center' : 10, *loc* can also be an (x,y) tuple in figure coords, which specifies the lower left of the legend box. figure coords are (0,0) is the left, bottom of the figure and 1,1 is the right, top. Keyword arguments: *prop*: [ None | FontProperties | dict ] A :class:`matplotlib.font_manager.FontProperties` instance. If *prop* is a dictionary, a new instance will be created with *prop*. If *None*, use rc settings. *numpoints*: integer The number of points in the legend line, default is 4 *scatterpoints*: integer The number of points in the legend line, default is 4 *scatteroffsets*: list of floats a list of yoffsets for scatter symbols in legend *markerscale*: [ None | scalar ] The relative size of legend markers vs. original. If *None*, use rc settings. *fancybox*: [ None | False | True ] if True, draw a frame with a round fancybox. If None, use rc *shadow*: [ None | False | True ] If *True*, draw a shadow behind legend. If *None*, use rc settings. *ncol* : integer number of columns. default is 1 *mode* : [ "expand" | None ] if mode is "expand", the legend will be horizontally expanded to fill the axes area (or *bbox_to_anchor*) *title* : string the legend title Padding and spacing between various elements use following keywords parameters. The dimensions of these values are given as a fraction of the fontsize. Values from rcParams will be used if None. ================ ================================================================== Keyword Description ================ ================================================================== borderpad the fractional whitespace inside the legend border labelspacing the vertical space between the legend entries handlelength the length of the legend handles handletextpad the pad between the legend handle and text borderaxespad the pad between the axes and legend border columnspacing the spacing between columns ================ ================================================================== .. Note:: Not all kinds of artist are supported by the legend. See LINK (FIXME) for details. **Example:** .. plot:: mpl_examples/pylab_examples/figlegend_demo.py """ handles = flatten(handles) l = Legend(self, handles, labels, *args, **kwargs) self.legends.append(l) return l
#end for a sct.append(sc) ax[i].set_ylim([0, 50]) ax[i].set_ylabel('Variation of MAF(%)') ax[i].set_title(ylabel[i], loc='left', fontdict={'fontsize':13, 'verticalalignment':'top', 'color':'black', 'backgroundcolor':'#FEFEFE'}) # ZoomSlider of Dumas position zaxes = plt.axes([0.08, 0.07, 0.90, 0.03], facecolor='lightgoldenrodyellow') zslider = ZoomSlider(zaxes, 'Dumas Position', valmin=B.minpos, valmax=B.maxpos, valleft=B.minpos, valright=B.maxpos, color='lightblue', valstep=1.0) zslider.on_changed(update_xlim) from legend import Legend spatches = [ mpatches.Patch(color=colors[idx], label=basepair[idx]) for idx in range(4) ] leg_basepair = Legend(fig, { 'spatches' : spatches, 'label' : basepair, 'bbox':(0.66, .91, 0.33, .102), 'loc' : 3, 'ncol' : 4, 'mode' : 'expand', 'borderaxespad' : 0.}) dumalabels = [ key for key in dumas.keys()] spatches = [ mpatches.Patch(color='grey', label=dumalabels[idx]) for idx in range(3) ] leg_dumas = Legend(fig, { 'spatches' : spatches, 'label' : dumalabels, 'bbox':(0.05, .91, 0.33, .102), 'loc' : 3, 'ncol' : 3, 'mode' : 'expand', 'borderaxespad' : 0.}) spatches = [ mpatches.Patch(color=['black', 'magenta'][idx], label=['base', 'variation'][idx]) for idx in range(2) ] leg_filter = Legend(fig, { 'spatches' : spatches, 'label' : ['base_0.0', 'variation_5.0'], 'bbox':(0.40, .91, 0.24, .102), 'loc' : 3, 'ncol' : 3, 'mode' : 'expand', 'borderaxespad' : 0.}) for x in ax: annot.append(x.annotate("", xy=(0,0), xytext=(20,-30),textcoords="offset points", bbox=dict(boxstyle="round", fc="w"), arrowprops=dict(arrowstyle="->"))) # annotate over other axes x.figure.texts.append(x.texts.pop())
def legend(*args, **kwargs): return Legend(*args, **kwargs)
def _do_layout(self): Legend._do_layout(self) self._compute_label_dims()
import json from legend import Legend import logging with open("config/config.json") as f: config = json.load(f) logging.basicConfig(level=logging.CRITICAL) legend = Legend(config)