def draw_ploidy(fig, root, blocksfile, bedfile, blockslayout): switchidsfile = "switch.ids" Synteny( fig, root, blocksfile, bedfile, blockslayout, scalebar=True, switch=switchidsfile, ) # Legend showing the orientation of the genes draw_gene_legend(root, 0.2, 0.3, 0.53) # WGD labels radius = 0.025 tau_color = "#bebada" alpha_color = "#bc80bd" label_color = "k" pad = 0.05 for y in (0.74 + 1.5 * pad, 0.26 - 1.5 * pad): TextCircle( root, 0.25, y, r"$\alpha^{O}$", radius=radius, fc=alpha_color, color=label_color, fontweight="bold", ) TextCircle( root, 0.75, y, r"$\alpha^{O}$", radius=radius, fc=alpha_color, color=label_color, fontweight="bold", ) for y in (0.74 + 3 * pad, 0.26 - 3 * pad): TextCircle(root, 0.5, y, r"$\tau$", radius=radius, fc=tau_color, color=label_color)
def amborella(args): """ %prog amborella seqids karyotype.layout mcscan.out all.bed synteny.layout Build a composite figure that calls graphics.karyotype and graphics.synteny. """ p = OptionParser(amborella.__doc__) p.add_option( "--tree", help="Display trees on the bottom of the figure [default: %default]") p.add_option( "--switch", help="Rename the seqid with two-column file [default: %default]") opts, args, iopts = p.set_image_options(args, figsize="8x7") if len(args) != 5: sys.exit(not p.print_help()) seqidsfile, klayout, datafile, bedfile, slayout = args switch = opts.switch tree = opts.tree fig = plt.figure(1, (iopts.w, iopts.h)) root = fig.add_axes([0, 0, 1, 1]) Karyotype(fig, root, seqidsfile, klayout) Synteny(fig, root, datafile, bedfile, slayout, switch=switch, tree=tree) # legend showing the orientation of the genes draw_gene_legend(root, .5, .68, .5) # annotate the WGD events fc = 'lightslategrey' x = .05 radius = .012 TextCircle(root, x, .86, '$\gamma$', radius=radius) TextCircle(root, x, .95, '$\epsilon$', radius=radius) root.plot([x, x], [.83, .9], ":", color=fc, lw=2) pts = plot_cap((x, .95), np.radians(range(-70, 250)), .02) x, y = zip(*pts) root.plot(x, y, ":", color=fc, lw=2) root.set_xlim(0, 1) root.set_ylim(0, 1) root.set_axis_off() pf = "amborella" image_name = pf + "." + iopts.format savefig(image_name, dpi=iopts.dpi, iopts=iopts)
def ploidy(args): """ %prog ploidy seqids karyotype.layout mcscan.out all.bed synteny.layout Build a figure that calls graphics.karyotype to illustrate the high ploidy of WGD history of pineapple genome. The script calls both graphics.karyotype and graphic.synteny. """ p = OptionParser(ploidy.__doc__) p.add_option("--switch", help="Rename the seqid with two-column file") opts, args, iopts = p.set_image_options(args, figsize="9x7") if len(args) != 5: sys.exit(not p.print_help()) seqidsfile, klayout, datafile, bedfile, slayout = args fig = plt.figure(1, (iopts.w, iopts.h)) root = fig.add_axes([0, 0, 1, 1]) Karyotype(fig, root, seqidsfile, klayout) Synteny(fig, root, datafile, bedfile, slayout, switch=opts.switch) # legend showing the orientation of the genes draw_gene_legend(root, .27, .37, .52) # annotate the WGD events fc = 'lightslategrey' x = .09 radius = .012 TextCircle(root, x, .825, r'$\tau$', radius=radius, fc=fc) TextCircle(root, x, .8, r'$\sigma$', radius=radius, fc=fc) TextCircle(root, x, .72, r'$\rho$', radius=radius, fc=fc) for ypos in (.825, .8, .72): root.text(.12, ypos, r"$\times2$", color=fc, ha="center", va="center") root.plot([x, x], [.85, .775], ":", color=fc, lw=2) root.plot([x, x], [.75, .675], ":", color=fc, lw=2) labels = ((.04, .96, 'A'), (.04, .54, 'B')) panel_labels(root, labels) root.set_xlim(0, 1) root.set_ylim(0, 1) root.set_axis_off() pf = "pineapple-karyotype" image_name = pf + "." + iopts.format savefig(image_name, dpi=iopts.dpi, iopts=iopts)
def draw(self, roundrect=False, plot_label=True): if self.empty: return y = self.y color = self.color ax = self.ax xstart = self.xstart gap = self.gap va = self.va nseqids = len(self.seqids) tr = self.tr for i, sid in enumerate(self.seqids): size = self.sizes[sid] rsize = self.ratio * size xend = xstart + rsize hc = HorizontalChromosome(ax, xstart, xend, y, height=self.height, lw=self.lw, fc=color, roundrect=roundrect) hc.set_transform(tr) sid = sid.rsplit("_", 1)[-1] si = "".join(x for x in sid if x not in string.letters) si = str(int(si)) xx = (xstart + xend) / 2 xstart = xend + gap if nseqids > 2 * MaxSeqids and (i + 1) % 10 != 0: continue if nseqids < 5: continue pad = .02 if va == "bottom": pad = -pad TextCircle(ax, xx, y + pad, si, radius=.01, fc="w", color=color, size=10, transform=tr) xp = min(self.xstart / 2, .1) if (self.xstart + self.xend) / 2 <= .5 \ else max(1 - self.end / 2, .92) label = markup(self.label) c = color if color != "gainsboro" else "k" if plot_label: ax.text(xp, y + self.height * .6, label, ha="center", color=c, transform=tr)
def draw_wgd_xy(ax, xx, yy, wgdline): """Draw WGD at (xx, yy) position Args: ax (axis): Matplotlib axes xx (float): x position yy (float): y position wgdline (WGDInfo): WGDInfoLines that contains the styling information """ TextCircle( ax, xx, yy, wgdline.name, fc=wgdline.color, radius=0.0225, color="k", fontweight="bold", )
def draw_wgd(ax, y, rescale, name, wgdcache): """Draw WGD at (xx yy) position Args: ax (axis): Matplotlib axes xx (float): x position yy (float): y position wgdline (WGDInfo): WGDInfoLines that contains the styling information """ if not wgdcache or name not in wgdcache: return for line in wgdcache[name]: TextCircle( ax, rescale(line.divergence), y, line.name, fc=line.color, radius=0.0225, color="k", fontweight="bold", )
def ploidy(args): """ %prog ploidy seqids layout Build a figure that calls graphics.karyotype to illustrate the high ploidy of B. napus genome. """ p = OptionParser(ploidy.__doc__) opts, args, iopts = p.set_image_options(args, figsize="8x7") if len(args) != 2: sys.exit(not p.print_help()) seqidsfile, klayout = args fig = plt.figure(1, (iopts.w, iopts.h)) root = fig.add_axes([0, 0, 1, 1]) Karyotype(fig, root, seqidsfile, klayout) fc = "darkslategrey" radius = .012 ot = -.05 # use this to adjust vertical position of the left panel TextCircle(root, .1, .9 + ot, r'$\gamma$', radius=radius, fc=fc) root.text(.1, .88 + ot, r"$\times3$", ha="center", va="top", color=fc) TextCircle(root, .08, .79 + ot, r'$\alpha$', radius=radius, fc=fc) TextCircle(root, .12, .79 + ot, r'$\beta$', radius=radius, fc=fc) root.text(.1, .77 + ot, r"$\times3\times2\times2$", ha="center", va="top", color=fc) root.text(.1, .67 + ot, r"Brassica triplication", ha="center", va="top", color=fc, size=11) root.text(.1, .65 + ot, r"$\times3\times2\times2\times3$", ha="center", va="top", color=fc) root.text(.1, .42 + ot, r"Allo-tetraploidy", ha="center", va="top", color=fc, size=11) root.text(.1, .4 + ot, r"$\times3\times2\times2\times3\times2$", ha="center", va="top", color=fc) bb = dict(boxstyle="round,pad=.5", fc="w", ec="0.5", alpha=0.5) root.text(.5, .2 + ot, r"\noindent\textit{Brassica napus}\\" "(A$\mathsf{_n}$C$\mathsf{_n}$ genome)", ha="center", size=16, color="k", bbox=bb) root.set_xlim(0, 1) root.set_ylim(0, 1) root.set_axis_off() pf = "napus" image_name = pf + "." + iopts.format savefig(image_name, dpi=iopts.dpi, iopts=iopts)
def deletion(args): """ %prog deletion [deletion-genes|deletion-bases] C2-deletions boleracea.bed Plot histogram for napus deletions. Can plot deletion-genes or deletion-bases. The three largest segmental deletions will be highlighted along with a drawing of the C2 chromosome. """ import math from jcvi.formats.bed import Bed from jcvi.graphics.chromosome import HorizontalChromosome from jcvi.graphics.base import kb_formatter p = OptionParser(deletion.__doc__) opts, args, iopts = p.set_image_options(args) if len(args) != 3: sys.exit(not p.print_help()) deletion_genes, deletions, bed = args dg = [int(x) for x in open(deletion_genes)] dsg, lsg = "darkslategray", "lightslategray" fig = plt.figure(1, (iopts.w, iopts.h)) root = fig.add_axes([0, 0, 1, 1]) ax = fig.add_axes([.1, .1, .8, .8]) minval = 2 if deletion_genes == "deleted-genes" else 2048 bins = np.logspace(math.log(minval, 10), math.log(max(dg), 10), 16) n, bins, histpatches = ax.hist(dg, bins=bins, \ fc=lsg, alpha=.75) ax.set_xscale('log', basex=2) if deletion_genes == "deleted-genes": ax.xaxis.set_major_formatter(mpl.ticker.FormatStrFormatter('%d')) ax.set_xlabel('No. of deleted genes in each segment') else: ax.xaxis.set_major_formatter(kb_formatter) ax.set_xlabel('No. of deleted bases in each segment') ax.yaxis.set_major_formatter(mpl.ticker.FormatStrFormatter('%d')) ax.set_ylabel('No. of segments') ax.patch.set_alpha(0.1) # Draw chromosome C2 na, nb = .45, .85 root.text((na + nb) / 2, .54, "ChrC02", ha="center") HorizontalChromosome(root, na, nb, .5, height=.025, fc=lsg, fill=True) order = Bed(bed).order fp = open(deletions) scale = lambda x: na + x * (nb - na) / 52886895 for i, row in enumerate(fp): i += 1 num, genes = row.split() genes = genes.split("|") ia, a = order[genes[0]] ib, b = order[genes[-1]] mi, mx = a.start, a.end mi, mx = scale(mi), scale(mx) root.add_patch(Rectangle((mi, .475), mx - mi, .05, fc="red", ec="red")) if i == 1: # offset between two adjacent regions for aesthetics mi -= .015 elif i == 2: mi += .015 TextCircle(root, mi, .44, str(i), fc="red") for i, mi in zip(range(1, 4), (.83, .78, .73)): TextCircle(root, mi, .2, str(i), fc="red") root.set_xlim(0, 1) root.set_ylim(0, 1) root.set_axis_off() image_name = deletion_genes + ".pdf" savefig(image_name, dpi=iopts.dpi, iopts=iopts)
def draw(self, roundrect=False, plot_label=True, pad=.03, vpad=.09): if self.empty: return y = self.y color = self.color ax = self.ax xstart = self.xstart gap = self.gap va = self.va nseqids = len(self.seqids) tr = self.tr for i, sid in enumerate(self.seqids): size = self.sizes[sid] rsize = self.ratio * size xend = xstart + rsize hc = HorizontalChromosome(ax, xstart, xend, y, height=self.height, lw=self.lw, fc=color, roundrect=roundrect) hc.set_transform(tr) sid = sid.rsplit("_", 1)[-1] si = "".join(x for x in sid if x in string.digits) si = str(int(si)) xx = (xstart + xend) / 2 xstart = xend + gap step = 2 if nseqids <= 40 else 10 if nseqids >= 2 * MaxSeqids and (i + 1) % step != 0: continue if nseqids < 5: continue hpad = -pad if va == "bottom" else pad TextCircle(ax, xx, y + hpad, si, radius=.01, fc="w", color=color, size=10, transform=tr) label = markup(self.label) c = color if color != "gainsboro" else "k" if plot_label: if self.label_va == "top": x, y = self.x, self.y + vpad va = "bottom" elif self.label_va == "bottom": x, y = self.x, self.y - vpad va = "top" else: # "center" x, y = self.xstart - vpad, self.y va = "center" ax.text(x, y, label, ha="center", va="center", color=c, transform=tr)
def main(): p = OptionParser(__doc__) p.add_option( "--groups", default=False, action="store_true", help="The first row contains group info", ) p.add_option("--rowgroups", help="Row groupings") p.add_option( "--horizontalbar", default=False, action="store_true", help="Horizontal color bar [default: vertical]", ) opts, args, iopts = p.set_image_options(figsize="8x8") if len(args) != 1: sys.exit(not p.print_help()) (datafile,) = args pf = datafile.rsplit(".", 1)[0] rowgroups = opts.rowgroups groups, rows, cols, data = parse_csv(datafile, vmin=1, groups=opts.groups) cols = [x.replace("ay ", "") for x in cols] if rowgroups: fp = open(rowgroups) rgroups = [] for row in fp: a, b = row.split() irows = [rows.index(x) for x in b.split(",")] rgroups.append((a, min(irows), max(irows))) plt.rcParams["axes.linewidth"] = 0 xstart = 0.18 fig = plt.figure(1, (iopts.w, iopts.h)) root = fig.add_axes([0, 0, 1, 1]) ax = fig.add_axes([xstart, 0.15, 0.7, 0.7]) im = ax.matshow(data, cmap=iopts.cmap, norm=mpl.colors.LogNorm(vmin=1, vmax=10000)) nrows, ncols = len(rows), len(cols) xinterval = 0.7 / ncols yinterval = 0.7 / max(nrows, ncols) plt.xticks(range(ncols), cols, rotation=45, size=10, ha="center") plt.yticks(range(nrows), rows, size=10) for x in ax.get_xticklines() + ax.get_yticklines(): x.set_visible(False) ax.set_xlim(-0.5, ncols - 0.5) t = [1, 10, 100, 1000, 10000] pad = 0.06 if opts.horizontalbar: ypos = 0.5 * (1 - nrows * yinterval) - pad axcolor = fig.add_axes([0.3, ypos, 0.4, 0.02]) orientation = "horizontal" else: axcolor = fig.add_axes([0.9, 0.3, 0.02, 0.4]) orientation = "vertical" fig.colorbar(im, cax=axcolor, ticks=t, orientation=orientation) if groups: groups = [(key, len(list(nn))) for key, nn in groupby(groups)] yy = 0.5 + 0.5 * nrows / ncols * 0.7 + 0.06 e = 0.005 sep = -0.5 for k, kl in groups: # Separator in the array area sep += kl ax.plot([sep, sep], [-0.5, nrows - 0.5], "w-", lw=2) # Group labels on the top kl *= xinterval root.plot([xstart + e, xstart + kl - e], [yy, yy], "-", color="gray", lw=2) root.text(xstart + 0.5 * kl, yy + e, k, ha="center", color="gray") xstart += kl if rowgroups: from jcvi.graphics.glyph import TextCircle xpos = 0.04 tip = 0.015 assert rgroups ystart = 1 - 0.5 * (1 - nrows * yinterval) for gname, start, end in rgroups: start = ystart - start * yinterval end = ystart - (end + 1) * yinterval start -= tip / 3 end += tip / 3 # Bracket the groups root.plot((xpos, xpos + tip), (start, start), "k-", lw=2) root.plot((xpos, xpos), (start, end), "k-", lw=2) root.plot((xpos, xpos + tip), (end, end), "k-", lw=2) TextCircle(root, xpos, 0.5 * (start + end), gname) root.set_xlim(0, 1) root.set_ylim(0, 1) root.set_axis_off() image_name = pf + "." + opts.cmap + "." + iopts.format savefig(image_name, dpi=iopts.dpi, iopts=iopts)
def draw_tree( ax, t, hpd=None, margin=0.1, rmargin=0.2, tip=0.01, treecolor="k", supportcolor="k", internal=True, outgroup=None, dashedoutgroup=False, reroot=True, gffdir=None, sizes=None, trunc_name=None, SH=None, scutoff=0, leafcolor="k", leaffont=12, leafinfo=None, wgdinfo=None, geoscale=False, ): """ main function for drawing phylogenetic tree """ if reroot: if outgroup: R = t.get_common_ancestor(*outgroup) else: # Calculate the midpoint node R = t.get_midpoint_outgroup() if R is not t: t.set_outgroup(R) # By default, the distance to outgroup and non-outgroup is the same # we re-adjust the distances so that the outgroups will appear # farthest from everything else if dashedoutgroup: a, b = t.children # Avoid even split total = a.dist + b.dist newR = t.get_common_ancestor(*outgroup) a.dist = 0.9 * total b.dist = total - a.dist farthest, max_dist = t.get_farthest_leaf() print("max_dist = {}".format(max_dist), file=sys.stderr) xstart = margin ystart = 2 * margin # scale the tree scale = (1 - margin - rmargin) / max_dist def rescale(dist): return xstart + scale * dist def rescale_divergence(divergence): return rescale(max_dist - divergence) num_leaves = len(t.get_leaf_names()) yinterval = (1 - ystart) / num_leaves # get exons structures, if any structures = {} if gffdir: gffiles = glob("{0}/*.gff*".format(gffdir)) setups, ratio = get_setups(gffiles, canvas=rmargin / 2, noUTR=True) structures = dict((a, (b, c)) for a, b, c in setups) if sizes: sizes = Sizes(sizes).mapping coords = {} i = 0 for n in t.traverse("postorder"): dist = n.get_distance(t) xx = rescale(dist) if n.is_leaf(): yy = ystart + i * yinterval i += 1 if trunc_name: name = truncate_name(n.name, rule=trunc_name) else: name = n.name if leafinfo and n.name in leafinfo: line = leafinfo[n.name] lc = line.color sname = line.new_name else: lc = leafcolor sname = None lc = lc or "k" sname = sname or name.replace("_", "-") # if color is given as "R,G,B" if "," in lc: lc = [float(x) for x in lc.split(",")] ax.text( xx + tip, yy, markup(sname), va="center", fontstyle="italic", size=leaffont, color=lc, ) gname = n.name.split("_")[0] if gname in structures: mrnabed, cdsbeds = structures[gname] ExonGlyph( ax, 1 - rmargin / 2, yy, mrnabed, cdsbeds, align="right", ratio=ratio, ) if sizes and gname in sizes: size = sizes[gname] size = size / 3 - 1 # base pair converted to amino acid size = "{0}aa".format(size) ax.text(1 - rmargin / 2 + tip, yy, size, size=leaffont) else: linestyle = "--" if (dashedoutgroup and n is t) else "-" children = [coords[x] for x in n.get_children()] children_x, children_y = zip(*children) min_y, max_y = min(children_y), max(children_y) # plot the vertical bar ax.plot((xx, xx), (min_y, max_y), linestyle, color=treecolor) # plot the horizontal bar for cx, cy in children: ax.plot((xx, cx), (cy, cy), linestyle, color=treecolor) yy = sum(children_y) * 1.0 / len(children_y) # plot HPD if exists if hpd and n.name in hpd: a, b = hpd[n.name] ax.plot( (rescale_divergence(a), rescale_divergence(b)), (yy, yy), "-", color="darkslategray", alpha=0.4, lw=2, ) support = n.support if support > 1: support = support / 100.0 if not n.is_root() and supportcolor: if support > scutoff / 100.0: ax.text( xx, yy + 0.005, "{0:d}".format(int(abs(support * 100))), ha="right", size=leaffont, color=supportcolor, ) if internal and n.name: TextCircle(ax, xx, yy, n.name, size=9) coords[n] = (xx, yy) # WGD info draw_wgd(ax, yy, rescale_divergence, n.name, wgdinfo) # scale bar if geoscale: draw_geoscale(ax, margin=margin, rmargin=rmargin, yy=margin, max_dist=max_dist) else: br = 0.1 x1 = xstart + 0.1 x2 = x1 + br * scale yy = margin ax.plot([x1, x1], [yy - tip, yy + tip], "-", color=treecolor) ax.plot([x2, x2], [yy - tip, yy + tip], "-", color=treecolor) ax.plot([x1, x2], [yy, yy], "-", color=treecolor) ax.text( (x1 + x2) / 2, yy - tip, "{0:g}".format(br), va="top", ha="center", size=leaffont, color=treecolor, ) if SH is not None: xs = x1 ys = (margin + yy) / 2.0 ax.text( xs, ys, "SH test against ref tree: {0}".format(SH), ha="left", size=leaffont, color="g", ) normalize_axes(ax)
def draw(self, roundrect=False, plot_label=True, plot_circles=True, pad=0.03, vpad=0.09): if self.empty: return y = self.y color = self.color ax = self.ax xstart = self.xstart gap = self.gap va = self.va nseqids = len(self.seqids) tr = self.tr for i, sid in enumerate(self.seqids): size = self.sizes[sid] rsize = self.ratio * size xend = xstart + rsize hc = HorizontalChromosome( ax, xstart, xend, y, height=self.height, lw=self.lw, fc=color, roundrect=roundrect, ) hc.set_transform(tr) si = make_circle_name(sid, self.rev) xx = (xstart + xend) / 2 xstart = xend + gap step = 2 if nseqids <= 40 else 10 if nseqids >= 2 * MaxSeqids and (i + 1) % step != 0: continue if nseqids < 5: continue hpad = -pad if va == "bottom" else pad if plot_circles: TextCircle( ax, xx, y + hpad, si, fc="w", color=color, size=10, transform=tr, ) label = markup(self.label) c = color if color != "gainsboro" else "k" if plot_label: if self.label_va == "top": x, y = self.x, self.y + vpad elif self.label_va == "bottom": x, y = self.x, self.y - vpad else: # "center" x, y = self.xstart - vpad / 2, self.y ax.text(x, y, label, ha="center", va="center", color=c, transform=tr)
def bites(args): """ %prog bites Illustrate the pipeline for automated bite discovery. """ p = OptionParser(__doc__) opts, args = p.parse_args() fig = plt.figure(1, (6, 6)) root = fig.add_axes([0, 0, 1, 1]) # HSP pairs hsps = ( ((50, 150), (60, 180)), ((190, 250), (160, 235)), ((300, 360), (270, 330)), ((430, 470), (450, 490)), ((570, 620), (493, 543)), ((540, 555), (370, 385)), # non-collinear hsps ) titlepos = (0.9, 0.65, 0.4) titles = ("Compare orthologous region", "Find collinear HSPs", "Scan paired gaps") ytip = 0.01 mrange = 650.0 m = lambda x: x / mrange * 0.7 + 0.1 for i, (ya, title) in enumerate(zip(titlepos, titles)): yb = ya - 0.1 plt.plot((0.1, 0.8), (ya, ya), "-", color="gray", lw=2, zorder=1) plt.plot((0.1, 0.8), (yb, yb), "-", color="gray", lw=2, zorder=1) RoundLabel(root, 0.5, ya + 4 * ytip, title) root.text(0.9, ya, "A. thaliana", ha="center", va="center") root.text(0.9, yb, "B. rapa", ha="center", va="center") myhsps = hsps if i >= 1: myhsps = hsps[:-1] for (a, b), (c, d) in myhsps: a, b, c, d = [m(x) for x in (a, b, c, d)] r1 = Rectangle((a, ya - ytip), b - a, 2 * ytip, fc="r", lw=0, zorder=2) r2 = Rectangle((c, yb - ytip), d - c, 2 * ytip, fc="r", lw=0, zorder=2) r3 = Rectangle((a, ya - ytip), b - a, 2 * ytip, fill=False, zorder=3) r4 = Rectangle((c, yb - ytip), d - c, 2 * ytip, fill=False, zorder=3) r5 = Polygon( ((a, ya - ytip), (c, yb + ytip), (d, yb + ytip), (b, ya - ytip)), fc="r", alpha=0.2, ) rr = (r1, r2, r3, r4, r5) if i == 2: rr = rr[:-1] for r in rr: root.add_patch(r) # Gap pairs hspa, hspb = zip(*myhsps) gapa, gapb = [], [] for (a, b), (c, d) in pairwise(hspa): gapa.append((b + 1, c - 1)) for (a, b), (c, d) in pairwise(hspb): gapb.append((b + 1, c - 1)) gaps = zip(gapa, gapb) tpos = titlepos[-1] yy = tpos - 0.05 for i, ((a, b), (c, d)) in enumerate(gaps): i += 1 a, b, c, d = [m(x) for x in (a, b, c, d)] xx = (a + b + c + d) / 4 TextCircle(root, xx, yy, str(i)) # Bites ystart = 0.24 ytip = 0.05 bites = ( ("Bite(40=>-15)", True), ("Bite(50=>35)", False), ("Bite(70=>120)", False), ("Bite(100=>3)", True), ) for i, (bite, selected) in enumerate(bites): xx = 0.15 if (i % 2 == 0) else 0.55 yy = ystart - i / 2 * ytip i += 1 TextCircle(root, xx, yy, str(i)) color = "k" if selected else "gray" root.text(xx + ytip, yy, bite, size=10, color=color, va="center") root.set_xlim(0, 1) root.set_ylim(0, 1) root.set_axis_off() figname = fname() + ".pdf" savefig(figname, dpi=300)