Merge pull request #3122 from PySimpleGUI/Dev-latest
A new way to generate Matplotlib graphs. Uses PIL to resize drawings …
This commit is contained in:
		
						commit
						54f3d7400e
					
				
					 1 changed files with 944 additions and 0 deletions
				
			
		
							
								
								
									
										944
									
								
								DemoPrograms/Demo_Matplotlib_Grid_of_Graphs_Using_PIL.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										944
									
								
								DemoPrograms/Demo_Matplotlib_Grid_of_Graphs_Using_PIL.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,944 @@ | |||
| #!/usr/bin/env python | ||||
| import PySimpleGUI as sg | ||||
| # import PySimpleGUIQt as sg | ||||
| # import PySimpleGUIWeb as sg | ||||
| 
 | ||||
| import numpy as np | ||||
| from matplotlib.backends.backend_tkagg import FigureCanvasAgg | ||||
| import matplotlib.figure | ||||
| import matplotlib.pyplot as plt | ||||
| import io | ||||
| 
 | ||||
| from matplotlib import cm | ||||
| from mpl_toolkits.mplot3d.axes3d import get_test_data | ||||
| from matplotlib.ticker import NullFormatter  # useful for `logit` scale | ||||
| 
 | ||||
| import PIL | ||||
| import base64 | ||||
| 
 | ||||
| """ | ||||
| Demonstrates one way of embedding Matplotlib figures into a PySimpleGUI window. | ||||
| 
 | ||||
| Basic steps are: | ||||
|  * Create a Canvas Element | ||||
|  * Layout form | ||||
|  * Display form (NON BLOCKING) | ||||
|  * Draw plots onto convas | ||||
|  * Display form (BLOCKING) | ||||
| 
 | ||||
| Each plotting function, complete with imports, was copied directly from Matplot examples page  | ||||
| """ | ||||
| 
 | ||||
| import numpy as np | ||||
| import matplotlib.pyplot as plt | ||||
| 
 | ||||
| 
 | ||||
| def PyplotSimple(): | ||||
|     import numpy as np | ||||
|     import matplotlib.pyplot as plt | ||||
| 
 | ||||
|     # evenly sampled time .2 intervals | ||||
|     t = np.arange(0., 5., 0.2)  # go from 0 to 5 using .2 intervals | ||||
| 
 | ||||
|     # red dashes, blue squares and green triangles | ||||
|     plt.plot(t, t, 'r--', t, t ** 2, 'bs', t, t ** 3, 'g^') | ||||
| 
 | ||||
|     fig = plt.gcf()  # get the figure to show | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def PyplotHistogram(): | ||||
|     """ | ||||
|     ============================================================= | ||||
|     Demo of the histogram (hist) function with multiple data sets | ||||
|     ============================================================= | ||||
| 
 | ||||
|     Plot histogram with multiple sample sets and demonstrate: | ||||
| 
 | ||||
|         * Use of legend with multiple sample sets | ||||
|         * Stacked bars | ||||
|         * Step curve with no fill | ||||
|         * Data sets of different sample sizes | ||||
| 
 | ||||
|     Selecting different bin counts and sizes can significantly affect the | ||||
|     shape of a histogram. The Astropy docs have a great section on how to | ||||
|     select these parameters: | ||||
|     http://docs.astropy.org/en/stable/visualization/histogram.html | ||||
|     """ | ||||
| 
 | ||||
|     import numpy as np | ||||
|     import matplotlib.pyplot as plt | ||||
| 
 | ||||
|     np.random.seed(0) | ||||
| 
 | ||||
|     n_bins = 10 | ||||
|     x = np.random.randn(1000, 3) | ||||
| 
 | ||||
|     fig, axes = plt.subplots(nrows=2, ncols=2) | ||||
|     ax0, ax1, ax2, ax3 = axes.flatten() | ||||
| 
 | ||||
|     colors = ['red', 'tan', 'lime'] | ||||
|     ax0.hist(x, n_bins, normed=1, histtype='bar', color=colors, label=colors) | ||||
|     ax0.legend(prop={'size': 10}) | ||||
|     ax0.set_title('bars with legend') | ||||
| 
 | ||||
|     ax1.hist(x, n_bins, normed=1, histtype='bar', stacked=True) | ||||
|     ax1.set_title('stacked bar') | ||||
| 
 | ||||
|     ax2.hist(x, n_bins, histtype='step', stacked=True, fill=False) | ||||
|     ax2.set_title('stack step (unfilled)') | ||||
| 
 | ||||
|     # Make a multiple-histogram of data-sets with different length. | ||||
|     x_multi = [np.random.randn(n) for n in [10000, 5000, 2000]] | ||||
|     ax3.hist(x_multi, n_bins, histtype='bar') | ||||
|     ax3.set_title('different sample sizes') | ||||
| 
 | ||||
|     fig.tight_layout() | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def PyplotArtistBoxPlots(): | ||||
|     """ | ||||
|     ========================================= | ||||
|     Demo of artist customization in box plots | ||||
|     ========================================= | ||||
| 
 | ||||
|     This example demonstrates how to use the various kwargs | ||||
|     to fully customize box plots. The first figure demonstrates | ||||
|     how to remove and add individual components (note that the | ||||
|     mean is the only value not shown by default). The second | ||||
|     figure demonstrates how the styles of the artists can | ||||
|     be customized. It also demonstrates how to set the limit | ||||
|     of the whiskers to specific percentiles (lower right axes) | ||||
| 
 | ||||
|     A good general reference on boxplots and their history can be found | ||||
|     here: http://vita.had.co.nz/papers/boxplots.pdf | ||||
| 
 | ||||
|     """ | ||||
| 
 | ||||
|     import numpy as np | ||||
|     import matplotlib.pyplot as plt | ||||
| 
 | ||||
|     # fake data | ||||
|     np.random.seed(937) | ||||
|     data = np.random.lognormal(size=(37, 4), mean=1.5, sigma=1.75) | ||||
|     labels = list('ABCD') | ||||
|     fs = 10  # fontsize | ||||
| 
 | ||||
|     # demonstrate how to toggle the display of different elements: | ||||
|     fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(6, 6), sharey=True) | ||||
|     axes[0, 0].boxplot(data, labels=labels) | ||||
|     axes[0, 0].set_title('Default', fontsize=fs) | ||||
| 
 | ||||
|     axes[0, 1].boxplot(data, labels=labels, showmeans=True) | ||||
|     axes[0, 1].set_title('showmeans=True', fontsize=fs) | ||||
| 
 | ||||
|     axes[0, 2].boxplot(data, labels=labels, showmeans=True, meanline=True) | ||||
|     axes[0, 2].set_title('showmeans=True,\nmeanline=True', fontsize=fs) | ||||
| 
 | ||||
|     axes[1, 0].boxplot(data, labels=labels, showbox=False, showcaps=False) | ||||
|     tufte_title = 'Tufte Style \n(showbox=False,\nshowcaps=False)' | ||||
|     axes[1, 0].set_title(tufte_title, fontsize=fs) | ||||
| 
 | ||||
|     axes[1, 1].boxplot(data, labels=labels, notch=True, bootstrap=10000) | ||||
|     axes[1, 1].set_title('notch=True,\nbootstrap=10000', fontsize=fs) | ||||
| 
 | ||||
|     axes[1, 2].boxplot(data, labels=labels, showfliers=False) | ||||
|     axes[1, 2].set_title('showfliers=False', fontsize=fs) | ||||
| 
 | ||||
|     for ax in axes.flatten(): | ||||
|         ax.set_yscale('log') | ||||
|         ax.set_yticklabels([]) | ||||
| 
 | ||||
|     fig.subplots_adjust(hspace=0.4) | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def ArtistBoxplot2(): | ||||
|     # fake data | ||||
|     np.random.seed(937) | ||||
|     data = np.random.lognormal(size=(37, 4), mean=1.5, sigma=1.75) | ||||
|     labels = list('ABCD') | ||||
|     fs = 10  # fontsize | ||||
| 
 | ||||
|     # demonstrate how to customize the display different elements: | ||||
|     boxprops = dict(linestyle='--', linewidth=3, color='darkgoldenrod') | ||||
|     flierprops = dict(marker='o', markerfacecolor='green', markersize=12, | ||||
|                       linestyle='none') | ||||
|     medianprops = dict(linestyle='-.', linewidth=2.5, color='firebrick') | ||||
|     meanpointprops = dict(marker='D', markeredgecolor='black', | ||||
|                           markerfacecolor='firebrick') | ||||
|     meanlineprops = dict(linestyle='--', linewidth=2.5, color='purple') | ||||
| 
 | ||||
|     fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(6, 6), sharey=True) | ||||
|     axes[0, 0].boxplot(data, boxprops=boxprops) | ||||
|     axes[0, 0].set_title('Custom boxprops', fontsize=fs) | ||||
| 
 | ||||
|     axes[0, 1].boxplot(data, flierprops=flierprops, medianprops=medianprops) | ||||
|     axes[0, 1].set_title('Custom medianprops\nand flierprops', fontsize=fs) | ||||
| 
 | ||||
|     axes[0, 2].boxplot(data, whis='range') | ||||
|     axes[0, 2].set_title('whis="range"', fontsize=fs) | ||||
| 
 | ||||
|     axes[1, 0].boxplot(data, meanprops=meanpointprops, meanline=False, | ||||
|                        showmeans=True) | ||||
|     axes[1, 0].set_title('Custom mean\nas point', fontsize=fs) | ||||
| 
 | ||||
|     axes[1, 1].boxplot(data, meanprops=meanlineprops, meanline=True, | ||||
|                        showmeans=True) | ||||
|     axes[1, 1].set_title('Custom mean\nas line', fontsize=fs) | ||||
| 
 | ||||
|     axes[1, 2].boxplot(data, whis=[15, 85]) | ||||
|     axes[1, 2].set_title('whis=[15, 85]\n#percentiles', fontsize=fs) | ||||
| 
 | ||||
|     for ax in axes.flatten(): | ||||
|         ax.set_yscale('log') | ||||
|         ax.set_yticklabels([]) | ||||
| 
 | ||||
|     fig.suptitle("I never said they'd be pretty") | ||||
|     fig.subplots_adjust(hspace=0.4) | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def PyplotScatterWithLegend(): | ||||
|     import matplotlib.pyplot as plt | ||||
|     from numpy.random import rand | ||||
| 
 | ||||
|     fig, ax = plt.subplots() | ||||
|     for color in ['red', 'green', 'blue']: | ||||
|         n = 750 | ||||
|         x, y = rand(2, n) | ||||
|         scale = 200.0 * rand(n) | ||||
|         ax.scatter(x, y, c=color, s=scale, label=color, | ||||
|                    alpha=0.3, edgecolors='none') | ||||
| 
 | ||||
|     ax.legend() | ||||
|     ax.grid(True) | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def PyplotLineStyles(): | ||||
|     """ | ||||
|     ========== | ||||
|     Linestyles | ||||
|     ========== | ||||
| 
 | ||||
|     This examples showcases different linestyles copying those of Tikz/PGF. | ||||
|     """ | ||||
|     import numpy as np | ||||
|     import matplotlib.pyplot as plt | ||||
|     from collections import OrderedDict | ||||
|     from matplotlib.transforms import blended_transform_factory | ||||
| 
 | ||||
|     linestyles = OrderedDict( | ||||
|         [('solid', (0, ())), | ||||
|          ('loosely dotted', (0, (1, 10))), | ||||
|          ('dotted', (0, (1, 5))), | ||||
|          ('densely dotted', (0, (1, 1))), | ||||
| 
 | ||||
|          ('loosely dashed', (0, (5, 10))), | ||||
|          ('dashed', (0, (5, 5))), | ||||
|          ('densely dashed', (0, (5, 1))), | ||||
| 
 | ||||
|          ('loosely dashdotted', (0, (3, 10, 1, 10))), | ||||
|          ('dashdotted', (0, (3, 5, 1, 5))), | ||||
|          ('densely dashdotted', (0, (3, 1, 1, 1))), | ||||
| 
 | ||||
|          ('loosely dashdotdotted', (0, (3, 10, 1, 10, 1, 10))), | ||||
|          ('dashdotdotted', (0, (3, 5, 1, 5, 1, 5))), | ||||
|          ('densely dashdotdotted', (0, (3, 1, 1, 1, 1, 1)))]) | ||||
| 
 | ||||
|     plt.figure(figsize=(10, 6)) | ||||
|     ax = plt.subplot(1, 1, 1) | ||||
| 
 | ||||
|     X, Y = np.linspace(0, 100, 10), np.zeros(10) | ||||
|     for i, (name, linestyle) in enumerate(linestyles.items()): | ||||
|         ax.plot(X, Y + i, linestyle=linestyle, linewidth=1.5, color='black') | ||||
| 
 | ||||
|     ax.set_ylim(-0.5, len(linestyles) - 0.5) | ||||
|     plt.yticks(np.arange(len(linestyles)), linestyles.keys()) | ||||
|     plt.xticks([]) | ||||
| 
 | ||||
|     # For each line style, add a text annotation with a small offset from | ||||
|     # the reference point (0 in Axes coords, y tick value in Data coords). | ||||
|     reference_transform = blended_transform_factory(ax.transAxes, ax.transData) | ||||
|     for i, (name, linestyle) in enumerate(linestyles.items()): | ||||
|         ax.annotate(str(linestyle), xy=(0.0, i), xycoords=reference_transform, | ||||
|                     xytext=(-6, -12), textcoords='offset points', color="blue", | ||||
|                     fontsize=8, ha="right", family="monospace") | ||||
| 
 | ||||
|     plt.tight_layout() | ||||
|     return plt.gcf() | ||||
| 
 | ||||
| 
 | ||||
| def PyplotLinePolyCollection(): | ||||
|     import matplotlib.pyplot as plt | ||||
|     from matplotlib import collections, colors, transforms | ||||
|     import numpy as np | ||||
| 
 | ||||
|     nverts = 50 | ||||
|     npts = 100 | ||||
| 
 | ||||
|     # Make some spirals | ||||
|     r = np.arange(nverts) | ||||
|     theta = np.linspace(0, 2 * np.pi, nverts) | ||||
|     xx = r * np.sin(theta) | ||||
|     yy = r * np.cos(theta) | ||||
|     spiral = np.column_stack([xx, yy]) | ||||
| 
 | ||||
|     # Fixing random state for reproducibility | ||||
|     rs = np.random.RandomState(19680801) | ||||
| 
 | ||||
|     # Make some offsets | ||||
|     xyo = rs.randn(npts, 2) | ||||
| 
 | ||||
|     # Make a list of colors cycling through the default series. | ||||
|     colors = [colors.to_rgba(c) | ||||
|               for c in plt.rcParams['axes.prop_cycle'].by_key()['color']] | ||||
| 
 | ||||
|     fig, axes = plt.subplots(2, 2) | ||||
|     fig.subplots_adjust(top=0.92, left=0.07, right=0.97, | ||||
|                         hspace=0.3, wspace=0.3) | ||||
|     ((ax1, ax2), (ax3, ax4)) = axes  # unpack the axes | ||||
| 
 | ||||
|     col = collections.LineCollection([spiral], offsets=xyo, | ||||
|                                      transOffset=ax1.transData) | ||||
|     trans = fig.dpi_scale_trans + transforms.Affine2D().scale(1.0 / 72.0) | ||||
|     col.set_transform(trans)  # the points to pixels transform | ||||
|     # Note: the first argument to the collection initializer | ||||
|     # must be a list of sequences of x,y tuples; we have only | ||||
|     # one sequence, but we still have to put it in a list. | ||||
|     ax1.add_collection(col, autolim=True) | ||||
|     # autolim=True enables autoscaling.  For collections with | ||||
|     # offsets like this, it is neither efficient nor accurate, | ||||
|     # but it is good enough to generate a plot that you can use | ||||
|     # as a starting point.  If you know beforehand the range of | ||||
|     # x and y that you want to show, it is better to set them | ||||
|     # explicitly, leave out the autolim kwarg (or set it to False), | ||||
|     # and omit the 'ax1.autoscale_view()' call below. | ||||
| 
 | ||||
|     # Make a transform for the line segments such that their size is | ||||
|     # given in points: | ||||
|     col.set_color(colors) | ||||
| 
 | ||||
|     ax1.autoscale_view()  # See comment above, after ax1.add_collection. | ||||
|     ax1.set_title('LineCollection using offsets') | ||||
| 
 | ||||
|     # The same data as above, but fill the curves. | ||||
|     col = collections.PolyCollection([spiral], offsets=xyo, | ||||
|                                      transOffset=ax2.transData) | ||||
|     trans = transforms.Affine2D().scale(fig.dpi / 72.0) | ||||
|     col.set_transform(trans)  # the points to pixels transform | ||||
|     ax2.add_collection(col, autolim=True) | ||||
|     col.set_color(colors) | ||||
| 
 | ||||
|     ax2.autoscale_view() | ||||
|     ax2.set_title('PolyCollection using offsets') | ||||
| 
 | ||||
|     # 7-sided regular polygons | ||||
| 
 | ||||
|     col = collections.RegularPolyCollection( | ||||
|         7, sizes=np.abs(xx) * 10.0, offsets=xyo, transOffset=ax3.transData) | ||||
|     trans = transforms.Affine2D().scale(fig.dpi / 72.0) | ||||
|     col.set_transform(trans)  # the points to pixels transform | ||||
|     ax3.add_collection(col, autolim=True) | ||||
|     col.set_color(colors) | ||||
|     ax3.autoscale_view() | ||||
|     ax3.set_title('RegularPolyCollection using offsets') | ||||
| 
 | ||||
|     # Simulate a series of ocean current profiles, successively | ||||
|     # offset by 0.1 m/s so that they form what is sometimes called | ||||
|     # a "waterfall" plot or a "stagger" plot. | ||||
| 
 | ||||
|     nverts = 60 | ||||
|     ncurves = 20 | ||||
|     offs = (0.1, 0.0) | ||||
| 
 | ||||
|     yy = np.linspace(0, 2 * np.pi, nverts) | ||||
|     ym = np.max(yy) | ||||
|     xx = (0.2 + (ym - yy) / ym) ** 2 * np.cos(yy - 0.4) * 0.5 | ||||
|     segs = [] | ||||
|     for i in range(ncurves): | ||||
|         xxx = xx + 0.02 * rs.randn(nverts) | ||||
|         curve = np.column_stack([xxx, yy * 100]) | ||||
|         segs.append(curve) | ||||
| 
 | ||||
|     col = collections.LineCollection(segs, offsets=offs) | ||||
|     ax4.add_collection(col, autolim=True) | ||||
|     col.set_color(colors) | ||||
|     ax4.autoscale_view() | ||||
|     ax4.set_title('Successive data offsets') | ||||
|     ax4.set_xlabel('Zonal velocity component (m/s)') | ||||
|     ax4.set_ylabel('Depth (m)') | ||||
|     # Reverse the y-axis so depth increases downward | ||||
|     ax4.set_ylim(ax4.get_ylim()[::-1]) | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def PyplotGGPlotSytleSheet(): | ||||
|     import numpy as np | ||||
|     import matplotlib.pyplot as plt | ||||
| 
 | ||||
|     plt.style.use('ggplot') | ||||
| 
 | ||||
|     # Fixing random state for reproducibility | ||||
|     np.random.seed(19680801) | ||||
| 
 | ||||
|     fig, axes = plt.subplots(ncols=2, nrows=2) | ||||
|     ax1, ax2, ax3, ax4 = axes.ravel() | ||||
| 
 | ||||
|     # scatter plot (Note: `plt.scatter` doesn't use default colors) | ||||
|     x, y = np.random.normal(size=(2, 200)) | ||||
|     ax1.plot(x, y, 'o') | ||||
| 
 | ||||
|     # sinusoidal lines with colors from default color cycle | ||||
|     L = 2 * np.pi | ||||
|     x = np.linspace(0, L) | ||||
|     ncolors = len(plt.rcParams['axes.prop_cycle']) | ||||
|     shift = np.linspace(0, L, ncolors, endpoint=False) | ||||
|     for s in shift: | ||||
|         ax2.plot(x, np.sin(x + s), '-') | ||||
|     ax2.margins(0) | ||||
| 
 | ||||
|     # bar graphs | ||||
|     x = np.arange(5) | ||||
|     y1, y2 = np.random.randint(1, 25, size=(2, 5)) | ||||
|     width = 0.25 | ||||
|     ax3.bar(x, y1, width) | ||||
|     ax3.bar(x + width, y2, width, | ||||
|             color=list(plt.rcParams['axes.prop_cycle'])[2]['color']) | ||||
|     ax3.set_xticks(x + width) | ||||
|     ax3.set_xticklabels(['a', 'b', 'c', 'd', 'e']) | ||||
| 
 | ||||
|     # circles with colors from default color cycle | ||||
|     for i, color in enumerate(plt.rcParams['axes.prop_cycle']): | ||||
|         xy = np.random.normal(size=2) | ||||
|         ax4.add_patch(plt.Circle(xy, radius=0.3, color=color['color'])) | ||||
|     ax4.axis('equal') | ||||
|     ax4.margins(0) | ||||
|     fig = plt.gcf()  # get the figure to show | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def PyplotBoxPlot(): | ||||
|     import numpy as np | ||||
|     import matplotlib.pyplot as plt | ||||
| 
 | ||||
|     # Fixing random state for reproducibility | ||||
|     np.random.seed(19680801) | ||||
| 
 | ||||
|     # fake up some data | ||||
|     spread = np.random.rand(50) * 100 | ||||
|     center = np.ones(25) * 50 | ||||
|     flier_high = np.random.rand(10) * 100 + 100 | ||||
|     flier_low = np.random.rand(10) * -100 | ||||
|     data = np.concatenate((spread, center, flier_high, flier_low), 0) | ||||
|     fig1, ax1 = plt.subplots() | ||||
|     ax1.set_title('Basic Plot') | ||||
|     ax1.boxplot(data) | ||||
|     return fig1 | ||||
| 
 | ||||
| 
 | ||||
| def PyplotRadarChart(): | ||||
|     import numpy as np | ||||
| 
 | ||||
|     import matplotlib.pyplot as plt | ||||
|     from matplotlib.path import Path | ||||
|     from matplotlib.spines import Spine | ||||
|     from matplotlib.projections.polar import PolarAxes | ||||
|     from matplotlib.projections import register_projection | ||||
| 
 | ||||
|     def radar_factory(num_vars, frame='circle'): | ||||
|         """Create a radar chart with `num_vars` axes. | ||||
| 
 | ||||
|         This function creates a RadarAxes projection and registers it. | ||||
| 
 | ||||
|         Parameters | ||||
|         ---------- | ||||
|         num_vars : int | ||||
|             Number of variables for radar chart. | ||||
|         frame : {'circle' | 'polygon'} | ||||
|             Shape of frame surrounding axes. | ||||
| 
 | ||||
|         """ | ||||
|         # calculate evenly-spaced axis angles | ||||
|         theta = np.linspace(0, 2 * np.pi, num_vars, endpoint=False) | ||||
| 
 | ||||
|         def draw_poly_patch(self): | ||||
|             # rotate theta such that the first axis is at the top | ||||
|             verts = unit_poly_verts(theta + np.pi / 2) | ||||
|             return plt.Polygon(verts, closed=True, edgecolor='k') | ||||
| 
 | ||||
|         def draw_circle_patch(self): | ||||
|             # unit circle centered on (0.5, 0.5) | ||||
|             return plt.Circle((0.5, 0.5), 0.5) | ||||
| 
 | ||||
|         patch_dict = {'polygon': draw_poly_patch, 'circle': draw_circle_patch} | ||||
|         if frame not in patch_dict: | ||||
|             raise ValueError('unknown value for `frame`: %s' % frame) | ||||
| 
 | ||||
|         class RadarAxes(PolarAxes): | ||||
| 
 | ||||
|             name = 'radar' | ||||
|             # use 1 line segment to connect specified points | ||||
|             RESOLUTION = 1 | ||||
|             # define draw_frame method | ||||
|             draw_patch = patch_dict[frame] | ||||
| 
 | ||||
|             def __init__(self, *args, **kwargs): | ||||
|                 super(RadarAxes, self).__init__(*args, **kwargs) | ||||
|                 # rotate plot such that the first axis is at the top | ||||
|                 self.set_theta_zero_location('N') | ||||
| 
 | ||||
|             def fill(self, *args, **kwargs): | ||||
|                 """Override fill so that line is closed by default""" | ||||
|                 closed = kwargs.pop('closed', True) | ||||
|                 return super(RadarAxes, self).fill(closed=closed, *args, **kwargs) | ||||
| 
 | ||||
|             def plot(self, *args, **kwargs): | ||||
|                 """Override plot so that line is closed by default""" | ||||
|                 lines = super(RadarAxes, self).plot(*args, **kwargs) | ||||
|                 for line in lines: | ||||
|                     self._close_line(line) | ||||
| 
 | ||||
|             def _close_line(self, line): | ||||
|                 x, y = line.get_data() | ||||
|                 # FIXME: markers at x[0], y[0] get doubled-up | ||||
|                 if x[0] != x[-1]: | ||||
|                     x = np.concatenate((x, [x[0]])) | ||||
|                     y = np.concatenate((y, [y[0]])) | ||||
|                     line.set_data(x, y) | ||||
| 
 | ||||
|             def set_varlabels(self, labels): | ||||
|                 self.set_thetagrids(np.degrees(theta), labels) | ||||
| 
 | ||||
|             def _gen_axes_patch(self): | ||||
|                 return self.draw_patch() | ||||
| 
 | ||||
|             def _gen_axes_spines(self): | ||||
|                 if frame == 'circle': | ||||
|                     return PolarAxes._gen_axes_spines(self) | ||||
|                 # The following is a hack to get the spines (i.e. the axes frame) | ||||
|                 # to draw correctly for a polygon frame. | ||||
| 
 | ||||
|                 # spine_type must be 'left', 'right', 'top', 'bottom', or `circle`. | ||||
|                 spine_type = 'circle' | ||||
|                 verts = unit_poly_verts(theta + np.pi / 2) | ||||
|                 # close off polygon by repeating first vertex | ||||
|                 verts.append(verts[0]) | ||||
|                 path = Path(verts) | ||||
| 
 | ||||
|                 spine = Spine(self, spine_type, path) | ||||
|                 spine.set_transform(self.transAxes) | ||||
|                 return {'polar': spine} | ||||
| 
 | ||||
|         register_projection(RadarAxes) | ||||
|         return theta | ||||
| 
 | ||||
|     def unit_poly_verts(theta): | ||||
|         """Return vertices of polygon for subplot axes. | ||||
| 
 | ||||
|         This polygon is circumscribed by a unit circle centered at (0.5, 0.5) | ||||
|         """ | ||||
|         x0, y0, r = [0.5] * 3 | ||||
|         verts = [(r * np.cos(t) + x0, r * np.sin(t) + y0) for t in theta] | ||||
|         return verts | ||||
| 
 | ||||
|     def example_data(): | ||||
|         # The following data is from the Denver Aerosol Sources and Health study. | ||||
|         # See  doi:10.1016/j.atmosenv.2008.12.017 | ||||
|         # | ||||
|         # The data are pollution source profile estimates for five modeled | ||||
|         # pollution sources (e.g., cars, wood-burning, etc) that emit 7-9 chemical | ||||
|         # species. The radar charts are experimented with here to see if we can | ||||
|         # nicely visualize how the modeled source profiles change across four | ||||
|         # scenarios: | ||||
|         #  1) No gas-phase species present, just seven particulate counts on | ||||
|         #     Sulfate | ||||
|         #     Nitrate | ||||
|         #     Elemental Carbon (EC) | ||||
|         #     Organic Carbon fraction 1 (OC) | ||||
|         #     Organic Carbon fraction 2 (OC2) | ||||
|         #     Organic Carbon fraction 3 (OC3) | ||||
|         #     Pyrolized Organic Carbon (OP) | ||||
|         #  2)Inclusion of gas-phase specie carbon monoxide (CO) | ||||
|         #  3)Inclusion of gas-phase specie ozone (O3). | ||||
|         #  4)Inclusion of both gas-phase species is present... | ||||
|         data = [ | ||||
|             ['Sulfate', 'Nitrate', 'EC', 'OC1', 'OC2', 'OC3', 'OP', 'CO', 'O3'], | ||||
|             ('Basecase', [ | ||||
|                 [0.88, 0.01, 0.03, 0.03, 0.00, 0.06, 0.01, 0.00, 0.00], | ||||
|                 [0.07, 0.95, 0.04, 0.05, 0.00, 0.02, 0.01, 0.00, 0.00], | ||||
|                 [0.01, 0.02, 0.85, 0.19, 0.05, 0.10, 0.00, 0.00, 0.00], | ||||
|                 [0.02, 0.01, 0.07, 0.01, 0.21, 0.12, 0.98, 0.00, 0.00], | ||||
|                 [0.01, 0.01, 0.02, 0.71, 0.74, 0.70, 0.00, 0.00, 0.00]]), | ||||
|             ('With CO', [ | ||||
|                 [0.88, 0.02, 0.02, 0.02, 0.00, 0.05, 0.00, 0.05, 0.00], | ||||
|                 [0.08, 0.94, 0.04, 0.02, 0.00, 0.01, 0.12, 0.04, 0.00], | ||||
|                 [0.01, 0.01, 0.79, 0.10, 0.00, 0.05, 0.00, 0.31, 0.00], | ||||
|                 [0.00, 0.02, 0.03, 0.38, 0.31, 0.31, 0.00, 0.59, 0.00], | ||||
|                 [0.02, 0.02, 0.11, 0.47, 0.69, 0.58, 0.88, 0.00, 0.00]]), | ||||
|             ('With O3', [ | ||||
|                 [0.89, 0.01, 0.07, 0.00, 0.00, 0.05, 0.00, 0.00, 0.03], | ||||
|                 [0.07, 0.95, 0.05, 0.04, 0.00, 0.02, 0.12, 0.00, 0.00], | ||||
|                 [0.01, 0.02, 0.86, 0.27, 0.16, 0.19, 0.00, 0.00, 0.00], | ||||
|                 [0.01, 0.03, 0.00, 0.32, 0.29, 0.27, 0.00, 0.00, 0.95], | ||||
|                 [0.02, 0.00, 0.03, 0.37, 0.56, 0.47, 0.87, 0.00, 0.00]]), | ||||
|             ('CO & O3', [ | ||||
|                 [0.87, 0.01, 0.08, 0.00, 0.00, 0.04, 0.00, 0.00, 0.01], | ||||
|                 [0.09, 0.95, 0.02, 0.03, 0.00, 0.01, 0.13, 0.06, 0.00], | ||||
|                 [0.01, 0.02, 0.71, 0.24, 0.13, 0.16, 0.00, 0.50, 0.00], | ||||
|                 [0.01, 0.03, 0.00, 0.28, 0.24, 0.23, 0.00, 0.44, 0.88], | ||||
|                 [0.02, 0.00, 0.18, 0.45, 0.64, 0.55, 0.86, 0.00, 0.16]]) | ||||
|         ] | ||||
|         return data | ||||
| 
 | ||||
|     N = 9 | ||||
|     theta = radar_factory(N, frame='polygon') | ||||
| 
 | ||||
|     data = example_data() | ||||
|     spoke_labels = data.pop(0) | ||||
| 
 | ||||
|     fig, axes = plt.subplots(figsize=(9, 9), nrows=2, ncols=2, | ||||
|                              subplot_kw=dict(projection='radar')) | ||||
|     fig.subplots_adjust(wspace=0.25, hspace=0.20, top=0.85, bottom=0.05) | ||||
| 
 | ||||
|     colors = ['b', 'r', 'g', 'm', 'y'] | ||||
|     # Plot the four cases from the example data on separate axes | ||||
|     for ax, (title, case_data) in zip(axes.flatten(), data): | ||||
|         ax.set_rgrids([0.2, 0.4, 0.6, 0.8]) | ||||
|         ax.set_title(title, weight='bold', size='medium', position=(0.5, 1.1), | ||||
|                      horizontalalignment='center', verticalalignment='center') | ||||
|         for d, color in zip(case_data, colors): | ||||
|             ax.plot(theta, d, color=color) | ||||
|             ax.fill(theta, d, facecolor=color, alpha=0.25) | ||||
|         ax.set_varlabels(spoke_labels) | ||||
| 
 | ||||
|     # add legend relative to top-left plot | ||||
|     ax = axes[0, 0] | ||||
|     labels = ('Factor 1', 'Factor 2', 'Factor 3', 'Factor 4', 'Factor 5') | ||||
|     legend = ax.legend(labels, loc=(0.9, .95), | ||||
|                        labelspacing=0.1, fontsize='small') | ||||
| 
 | ||||
|     fig.text(0.5, 0.965, '5-Factor Solution Profiles Across Four Scenarios', | ||||
|              horizontalalignment='center', color='black', weight='bold', | ||||
|              size='large') | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def DifferentScales(): | ||||
|     import numpy as np | ||||
|     import matplotlib.pyplot as plt | ||||
| 
 | ||||
|     # Create some mock data | ||||
|     t = np.arange(0.01, 10.0, 0.01) | ||||
|     data1 = np.exp(t) | ||||
|     data2 = np.sin(2 * np.pi * t) | ||||
| 
 | ||||
|     fig, ax1 = plt.subplots() | ||||
| 
 | ||||
|     color = 'tab:red' | ||||
|     ax1.set_xlabel('time (s)') | ||||
|     ax1.set_ylabel('exp', color=color) | ||||
|     ax1.plot(t, data1, color=color) | ||||
|     ax1.tick_params(axis='y', labelcolor=color) | ||||
| 
 | ||||
|     ax2 = ax1.twinx()  # instantiate a second axes that shares the same x-axis | ||||
| 
 | ||||
|     color = 'tab:blue' | ||||
|     ax2.set_ylabel('sin', color=color)  # we already handled the x-label with ax1 | ||||
|     ax2.plot(t, data2, color=color) | ||||
|     ax2.tick_params(axis='y', labelcolor=color) | ||||
| 
 | ||||
|     fig.tight_layout()  # otherwise the right y-label is slightly clipped | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def ExploringNormalizations(): | ||||
|     import matplotlib.pyplot as plt | ||||
|     import matplotlib.colors as mcolors | ||||
|     import numpy as np | ||||
|     from numpy.random import multivariate_normal | ||||
| 
 | ||||
|     data = np.vstack([ | ||||
|         multivariate_normal([10, 10], [[3, 2], [2, 3]], size=100000), | ||||
|         multivariate_normal([30, 20], [[2, 3], [1, 3]], size=1000) | ||||
|     ]) | ||||
| 
 | ||||
|     gammas = [0.8, 0.5, 0.3] | ||||
| 
 | ||||
|     fig, axes = plt.subplots(nrows=2, ncols=2) | ||||
| 
 | ||||
|     axes[0, 0].set_title('Linear normalization') | ||||
|     axes[0, 0].hist2d(data[:, 0], data[:, 1], bins=100) | ||||
| 
 | ||||
|     for ax, gamma in zip(axes.flat[1:], gammas): | ||||
|         ax.set_title(r'Power law $(\gamma=%1.1f)$' % gamma) | ||||
|         ax.hist2d(data[:, 0], data[:, 1], | ||||
|                   bins=100, norm=mcolors.PowerNorm(gamma)) | ||||
| 
 | ||||
|     fig.tight_layout() | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def PyplotFormatstr(): | ||||
|     def f(t): | ||||
|         return np.exp(-t) * np.cos(2 * np.pi * t) | ||||
| 
 | ||||
|     t1 = np.arange(0.0, 5.0, 0.1) | ||||
|     t2 = np.arange(0.0, 5.0, 0.02) | ||||
| 
 | ||||
|     plt.figure(1) | ||||
|     plt.subplot(211) | ||||
|     plt.plot(t1, f(t1), 'bo', t2, f(t2), 'k') | ||||
| 
 | ||||
|     plt.subplot(212) | ||||
|     plt.plot(t2, np.cos(2 * np.pi * t2), 'r--') | ||||
|     fig = plt.gcf()  # get the figure to show | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def UnicodeMinus(): | ||||
|     import numpy as np | ||||
|     import matplotlib | ||||
|     import matplotlib.pyplot as plt | ||||
| 
 | ||||
|     # Fixing random state for reproducibility | ||||
|     np.random.seed(19680801) | ||||
| 
 | ||||
|     matplotlib.rcParams['axes.unicode_minus'] = False | ||||
|     fig, ax = plt.subplots() | ||||
|     ax.plot(10 * np.random.randn(100), 10 * np.random.randn(100), 'o') | ||||
|     ax.set_title('Using hyphen instead of Unicode minus') | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def Subplot3d(): | ||||
|     from mpl_toolkits.mplot3d.axes3d import Axes3D | ||||
|     from matplotlib import cm | ||||
|     # from matplotlib.ticker import LinearLocator, FixedLocator, FormatStrFormatter | ||||
|     import matplotlib.pyplot as plt | ||||
|     import numpy as np | ||||
| 
 | ||||
|     fig = plt.figure() | ||||
| 
 | ||||
|     ax = fig.add_subplot(1, 2, 1, projection='3d') | ||||
|     X = np.arange(-5, 5, 0.25) | ||||
|     Y = np.arange(-5, 5, 0.25) | ||||
|     X, Y = np.meshgrid(X, Y) | ||||
|     R = np.sqrt(X ** 2 + Y ** 2) | ||||
|     Z = np.sin(R) | ||||
|     surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.jet, | ||||
|                            linewidth=0, antialiased=False) | ||||
|     ax.set_zlim3d(-1.01, 1.01) | ||||
| 
 | ||||
|     # ax.w_zaxis.set_major_locator(LinearLocator(10)) | ||||
|     # ax.w_zaxis.set_major_formatter(FormatStrFormatter('%.03f')) | ||||
| 
 | ||||
|     fig.colorbar(surf, shrink=0.5, aspect=5) | ||||
| 
 | ||||
|     from mpl_toolkits.mplot3d.axes3d import get_test_data | ||||
|     ax = fig.add_subplot(1, 2, 2, projection='3d') | ||||
|     X, Y, Z = get_test_data(0.05) | ||||
|     ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10) | ||||
|     return fig | ||||
| 
 | ||||
| 
 | ||||
| def PyplotScales(): | ||||
|     import numpy as np | ||||
|     import matplotlib.pyplot as plt | ||||
| 
 | ||||
|     from matplotlib.ticker import NullFormatter  # useful for `logit` scale | ||||
| 
 | ||||
|     # Fixing random state for reproducibility | ||||
|     np.random.seed(19680801) | ||||
| 
 | ||||
|     # make up some data in the interval ]0, 1[ | ||||
|     y = np.random.normal(loc=0.5, scale=0.4, size=1000) | ||||
|     y = y[(y > 0) & (y < 1)] | ||||
|     y.sort() | ||||
|     x = np.arange(len(y)) | ||||
| 
 | ||||
|     # plot with various axes scales | ||||
|     plt.figure(1) | ||||
| 
 | ||||
|     # linear | ||||
|     plt.subplot(221) | ||||
|     plt.plot(x, y) | ||||
|     plt.yscale('linear') | ||||
|     plt.title('linear') | ||||
|     plt.grid(True) | ||||
| 
 | ||||
|     # log | ||||
|     plt.subplot(222) | ||||
|     plt.plot(x, y) | ||||
|     plt.yscale('log') | ||||
|     plt.title('log') | ||||
|     plt.grid(True) | ||||
| 
 | ||||
|     # symmetric log | ||||
|     plt.subplot(223) | ||||
|     plt.plot(x, y - y.mean()) | ||||
|     plt.yscale('symlog', linthreshy=0.01) | ||||
|     plt.title('symlog') | ||||
|     plt.grid(True) | ||||
| 
 | ||||
|     # logit | ||||
|     plt.subplot(224) | ||||
|     plt.plot(x, y) | ||||
|     plt.yscale('logit') | ||||
|     plt.title('logit') | ||||
|     plt.grid(True) | ||||
|     # Format the minor tick labels of the y-axis into empty strings with | ||||
|     # `NullFormatter`, to avoid cumbering the axis with too many labels. | ||||
|     plt.gca().yaxis.set_minor_formatter(NullFormatter()) | ||||
|     # Adjust the subplot layout, because the logit one may take more space | ||||
|     # than usual, due to y-tick labels like "1 - 10^{-3}" | ||||
|     plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95, hspace=0.25, | ||||
|                         wspace=0.35) | ||||
|     return plt.gcf() | ||||
| 
 | ||||
| 
 | ||||
| def AxesGrid(): | ||||
|     import numpy as np | ||||
|     import matplotlib.pyplot as plt | ||||
|     from mpl_toolkits.axes_grid1.axes_rgb import RGBAxes | ||||
| 
 | ||||
|     def get_demo_image(): | ||||
|         # prepare image | ||||
|         delta = 0.5 | ||||
| 
 | ||||
|         extent = (-3, 4, -4, 3) | ||||
|         x = np.arange(-3.0, 4.001, delta) | ||||
|         y = np.arange(-4.0, 3.001, delta) | ||||
|         X, Y = np.meshgrid(x, y) | ||||
|         Z1 = np.exp(-X ** 2 - Y ** 2) | ||||
|         Z2 = np.exp(-(X - 1) ** 2 - (Y - 1) ** 2) | ||||
|         Z = (Z1 - Z2) * 2 | ||||
| 
 | ||||
|         return Z, extent | ||||
| 
 | ||||
|     def get_rgb(): | ||||
|         Z, extent = get_demo_image() | ||||
| 
 | ||||
|         Z[Z < 0] = 0. | ||||
|         Z = Z / Z.max() | ||||
| 
 | ||||
|         R = Z[:13, :13] | ||||
|         G = Z[2:, 2:] | ||||
|         B = Z[:13, 2:] | ||||
| 
 | ||||
|         return R, G, B | ||||
| 
 | ||||
|     fig = plt.figure(1) | ||||
|     ax = RGBAxes(fig, [0.1, 0.1, 0.8, 0.8]) | ||||
| 
 | ||||
|     r, g, b = get_rgb() | ||||
|     kwargs = dict(origin="lower", interpolation="nearest") | ||||
|     ax.imshow_rgb(r, g, b, **kwargs) | ||||
| 
 | ||||
|     ax.RGB.set_xlim(0., 9.5) | ||||
|     ax.RGB.set_ylim(0.9, 10.6) | ||||
| 
 | ||||
|     plt.draw() | ||||
|     return plt.gcf() | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def figure_to_image(figure): | ||||
|     """ | ||||
|     Draws the previously created "figure" in the supplied Image Element | ||||
| 
 | ||||
|     :param element: an Image Element | ||||
|     :param figure: a Matplotlib figure | ||||
|     :return: The figure canvas | ||||
|     """ | ||||
| 
 | ||||
|     plt.close('all')        # erases previously drawn plots | ||||
|     canv = FigureCanvasAgg(figure) | ||||
|     buf = io.BytesIO() | ||||
|     canv.print_figure(buf, format='png') | ||||
|     if buf is None: | ||||
|         return None | ||||
|     buf.seek(0) | ||||
|     return buf.read() | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def convert_to_bytes(file_or_bytes, resize=None): | ||||
|     ''' | ||||
|     Will convert into bytes and optionally resize an image that is a file or a base64 bytes object. | ||||
|     Turns into  PNG format in the process so that can be displayed by tkinter | ||||
|     :param file_or_bytes: either a string filename or a bytes base64 image object | ||||
|     :type file_or_bytes:  (Union[str, bytes]) | ||||
|     :param resize:  optional new size | ||||
|     :type resize: (Tuple[int, int] or None) | ||||
|     :return: (bytes) a byte-string object | ||||
|     :rtype: (bytes) | ||||
|     ''' | ||||
|     if isinstance(file_or_bytes, str): | ||||
|         img = PIL.Image.open(file_or_bytes) | ||||
|     else: | ||||
|         try: | ||||
|             img = PIL.Image.open(io.BytesIO(base64.b64decode(file_or_bytes))) | ||||
|         except Exception as e: | ||||
|             dataBytesIO = io.BytesIO(file_or_bytes) | ||||
|             img = PIL.Image.open(dataBytesIO) | ||||
| 
 | ||||
|     cur_width, cur_height = img.size | ||||
|     if resize: | ||||
|         new_width, new_height = resize | ||||
|         scale = min(new_height/cur_height, new_width/cur_width) | ||||
|         img = img.resize((int(cur_width*scale), int(cur_height*scale)), PIL.Image.ANTIALIAS) | ||||
|     bio = io.BytesIO() | ||||
|     img.save(bio, format="PNG") | ||||
|     del img | ||||
|     return bio.getvalue() | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------- GUI Starts Here -------------------------------# | ||||
| # fig = your figure you want to display.  Assumption is that 'fig' holds the      # | ||||
| #       information to display.                                                   # | ||||
| # --------------------------------------------------------------------------------# | ||||
| 
 | ||||
| fig_dict = {'Pyplot Simple': PyplotSimple, 'Pyplot Formatstr': PyplotFormatstr, 'PyPlot Three': Subplot3d, | ||||
|             'Unicode Minus': UnicodeMinus, 'Pyplot Scales': PyplotScales, 'Axes Grid': AxesGrid, | ||||
|             'Exploring Normalizations': ExploringNormalizations, 'Different Scales': DifferentScales, | ||||
|             'Pyplot Box Plot': PyplotBoxPlot, 'Pyplot ggplot Style Sheet': PyplotGGPlotSytleSheet, | ||||
|             'Pyplot Line Poly Collection': PyplotLinePolyCollection, 'Pyplot Line Styles': PyplotLineStyles, | ||||
|             'Pyplot Scatter With Legend': PyplotScatterWithLegend, 'Artist Customized Box Plots': PyplotArtistBoxPlots, | ||||
|             'Artist Customized Box Plots 2': ArtistBoxplot2, 'Pyplot Histogram': PyplotHistogram} | ||||
| 
 | ||||
| sg.theme('LightGreen') | ||||
| 
 | ||||
| figure_w, figure_h = 250, 250 | ||||
| # define the form layout | ||||
| col_listbox = [[sg.Listbox(values=list(fig_dict.keys()), select_mode=sg.SELECT_MODE_EXTENDED, enable_events=True, size=(28, len(list(fig_dict))), key='-LISTBOX-')], | ||||
|                [sg.Exit(size=(5, 2))]] | ||||
| 
 | ||||
| image_col = sg.Col([[sg.Image(k=(i,j)) for i in range(4)]for j in range(4)]) | ||||
| 
 | ||||
| layout = [[sg.Text('Matplotlib Grid of Plots Using PIL', font=('current 18'))], | ||||
|           [sg.Col(col_listbox, element_justification='c'), | ||||
|           image_col] | ||||
|           ] | ||||
| 
 | ||||
| # create the form and show it without the plot | ||||
| window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI', layout, grab_anywhere=False, finalize=True) | ||||
| figure_agg = None | ||||
| # The GUI Event Loop | ||||
| while True: | ||||
|     event, values = window.read() | ||||
|     # print(event, values)                  # helps greatly when debugging | ||||
|     if event in (sg.WIN_CLOSED, 'Exit'):  # if user closed window or clicked Exit button | ||||
|         break | ||||
|     for i, choice in enumerate(values['-LISTBOX-']): | ||||
|         func = fig_dict[choice]  # get function to call from the dictionary | ||||
|         fig = func()  # call function to get the figure | ||||
|         image = figure_to_image(fig) | ||||
|         image = convert_to_bytes(image, (figure_w, figure_h)) | ||||
|         image = convert_to_bytes(image, (figure_w, figure_h)) | ||||
|         window[(i%4, i//4)].update(data=image) | ||||
| window.close() | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue