├── .gitignore ├── Makefile ├── README ├── README.md ├── docs ├── animated.gif ├── lightdark.png ├── sea.py ├── simple.py ├── subplots.png └── test.py ├── itermplot └── __init__.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | 91 | .DS_Store 92 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | upload: 2 | pandoc --from=markdown --to=rst --output=README README.md 3 | python3 setup.py sdist 4 | twine upload dist/* 5 | 6 | clean: 7 | @rm -fr build dist 8 | @rm -fr itermplot.egg-info 9 | @rm -fr itermplot/__pycache__ 10 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | itermplot 2 | ========= 3 | 4 | An awesome `iTerm2 `__ backend for Matplotlib, 5 | so you can plot directly in your terminal. 6 | 7 | The above is achieved with zero modifications to your Python script. For 8 | example, the above plots are generated with the following code: 9 | 10 | .. code:: {python} 11 | 12 | import numpy as np 13 | import matplotlib.pyplot as plt 14 | import networkx as nx 15 | 16 | plt.rcParams["font.size"] = 10 17 | 18 | plt.figure(figsize=(8,3)) 19 | 20 | ax = plt.subplot(121) 21 | x = np.arange(0,10,0.001) 22 | ax.plot(x, np.sin(np.sinc(x)), 'r', lw=2) 23 | ax.set_title('Nice wiggle') 24 | 25 | ax = plt.subplot(122) 26 | plt.tick_params(axis='both', left='off', top='off', right='off', bottom='off', labelleft='off', labeltop='off', labelright='off', labelbottom='off') 27 | G = nx.random_geometric_graph(200, 0.125) 28 | pos=nx.spring_layout(G) 29 | nx.draw_networkx_edges(G, pos, alpha=0.2) 30 | nx.draw_networkx_nodes(G, pos, node_color='r', node_size=12) 31 | ax.set_title('Random graph') 32 | 33 | plt.show() 34 | 35 | Note: you need to run ``plt.show()`` to display the figure. 36 | 37 | Reverse video 38 | ~~~~~~~~~~~~~ 39 | 40 | If you use a dark background in your terminal, you can enable “reverse 41 | video” mode by adding this to your ``.profile``: 42 | 43 | :: 44 | 45 | export ITERMPLOT=rv 46 | 47 | TMUX support 48 | ~~~~~~~~~~~~ 49 | 50 | itermplot tries to auto-detect TMUX and behave in a sane way. Vertical 51 | split panes do not work well due to a limitation with iTerm2. Luckily, 52 | horizontals do though. 53 | 54 | Animation support 55 | ~~~~~~~~~~~~~~~~~ 56 | 57 | itermplot supports animation created by matplotlib animation module. 58 | 59 | You’ll need to install ImageMagick and have it on the path to use the 60 | animation support. The simplest way to see if ImageMagick is installed 61 | and valid is to run: 62 | 63 | .. code:: {sh} 64 | 65 | $ convert -version 66 | Version: ImageMagick 7.0.4-4 Q16 x86_64 2017-01-14 http://www.imagemagick.org 67 | Copyright: © 1999-2017 ImageMagick Studio LLC 68 | License: http://www.imagemagick.org/script/license.php 69 | Features: Cipher DPC HDRI Modules 70 | Delegates (built-in): bzlib freetype jng jpeg ltdl lzma png tiff xml zlib 71 | 72 | To enable animation support, you need to specifying the desired number 73 | of frames in the output animation. For example, specify it before your 74 | script with: 75 | 76 | :: 77 | 78 | $ ITERMPLOT_FRAMES=30 python script.py 79 | 80 | You can also save the resulting gif file by using ``ITERMPLOT_OUTFILE`` 81 | environment variable: 82 | 83 | :: 84 | 85 | $ ITERMPLOT_FRAMES=30 ITERMPLOT_OUTFILE=out.gif python script.py 86 | 87 | Currently animation does not support reverse video with ITERMPLOT=rv. 88 | 89 | Configure lines 90 | ~~~~~~~~~~~~~~~ 91 | 92 | You can configure the number of lines used with the ``ITERMPLOT_LINES`` 93 | environment variable. For example: 94 | 95 | .. code:: {sh} 96 | 97 | ITERMPLOT_LINES=5 python3 simple.py 98 | 99 | Python 2 and Python 3 support 100 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 101 | 102 | Now supports Python 2, even if this makes me want to cry 😭 103 | 104 | Installation 105 | ------------ 106 | 107 | Using pip 108 | ~~~~~~~~~ 109 | 110 | Install using ``pip`` using the command: 111 | 112 | .. code:: {sh} 113 | 114 | pip3 install itermplot 115 | 116 | itermplot is enabled by setting ``MPLBACKEND`` in your environment. If 117 | you use ``bash``, then this can be accomplished using the command: 118 | 119 | .. code:: {sh} 120 | 121 | export MPLBACKEND="module://itermplot" 122 | 123 | Note: you can add the ``export`` line above to your ``.profile`` file so 124 | that itermplot is always enabled in your terminal. 125 | 126 | Testing 127 | ~~~~~~~ 128 | 129 | To test your installation you can do the following in your iTerm2 130 | console: 131 | 132 | :: 133 | 134 | $ echo $MPLBACKEND 135 | module://itermplot 136 | $ python3 137 | Python 3.5.2 (default, Oct 24 2016, 09:14:06) 138 | [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)] on darwin 139 | Type "help", "copyright", "credits" or "license" for more information. 140 | >>> import matplotlib.pyplot as plt 141 | >>> plt.plot([1,2,3]) 142 | [] 143 | >>> plt.show() 144 | 145 | You should see a plot! 146 | 147 | Uninstall 148 | --------- 149 | 150 | You can disable this backend by unsetting the ``MPLBACKEND`` environment 151 | variable. 152 | 153 | :: 154 | 155 | $ unset MPLBACKEND 156 | $ echo $MPLBACKEND 157 | 158 | $ python3 159 | Python 3.5.2 (default, Oct 24 2016, 09:14:06) 160 | [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)] on darwin 161 | Type "help", "copyright", "credits" or "license" for more information. 162 | >>> import matplotlib.pyplot as plt 163 | >>> plt.plot([1,2,3]) 164 | [] 165 | >>> plt.show() 166 | 167 | To remove the package completely, run: 168 | 169 | :: 170 | 171 | pip3 uninstall itermplot 172 | 173 | Bugs 174 | ---- 175 | 176 | This backend is very alpha, so if you have a problem please raise an 177 | Issue on GitHub and I will try to fix it. 178 | 179 | I also accept (and appreciate!) good patches / pull request. Thanks to 180 | `garrywu `__, 181 | `brenshanny `__, 182 | `hbredin `__, 183 | `zevlg `__ for their patches so far. 184 | 185 | Other cool things 186 | ----------------- 187 | 188 | I encourage you to check-out some of my `other little 189 | projects `__. Lots more coming as I 190 | slowly release them… 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # itermplot 2 | 3 | ``` 4 | pip install itermplot==0.5 5 | ``` 6 | 7 | An awesome [iTerm2](https://www.iterm2.com/) backend for Matplotlib, so you can plot directly in your terminal. 8 | 9 | 10 | 11 | The above is achieved with zero modifications to your Python script. For example, the above 12 | plots are generated with the following code: 13 | ```{python} 14 | import numpy as np 15 | import matplotlib.pyplot as plt 16 | import networkx as nx 17 | 18 | plt.rcParams["font.size"] = 10 19 | 20 | plt.figure(figsize=(8,3)) 21 | 22 | ax = plt.subplot(121) 23 | x = np.arange(0,10,0.001) 24 | ax.plot(x, np.sin(np.sinc(x)), 'r', lw=2) 25 | ax.set_title('Nice wiggle') 26 | 27 | ax = plt.subplot(122) 28 | plt.tick_params(axis='both', left='off', top='off', right='off', bottom='off', labelleft='off', labeltop='off', labelright='off', labelbottom='off') 29 | G = nx.random_geometric_graph(200, 0.125) 30 | pos=nx.spring_layout(G) 31 | nx.draw_networkx_edges(G, pos, alpha=0.2) 32 | nx.draw_networkx_nodes(G, pos, node_color='r', node_size=12) 33 | ax.set_title('Random graph') 34 | 35 | plt.show() 36 | ``` 37 | 38 | Note: you need to run `plt.show()` to display the figure. 39 | 40 | ### Reverse video 41 | 42 | If you use a dark background in your terminal, you can enable "reverse video" mode by adding this to your `.profile`: 43 | ``` 44 | export ITERMPLOT=rv 45 | ``` 46 | 47 | ### TMUX support 48 | 49 | itermplot tries to auto-detect TMUX and behave in a sane way. Vertical split panes do not work well due to a 50 | limitation with iTerm2. Luckily, horizontals do though. 51 | 52 | ### Animation support 53 | 54 | itermplot supports animation created by matplotlib animation module. 55 | 56 | 57 | 58 | You'll need to install ImageMagick and have it on the path to use the animation support. The simplest way to see if ImageMagick is installed and valid is to run: 59 | ```{sh} 60 | $ convert -version 61 | Version: ImageMagick 7.0.4-4 Q16 x86_64 2017-01-14 http://www.imagemagick.org 62 | Copyright: © 1999-2017 ImageMagick Studio LLC 63 | License: http://www.imagemagick.org/script/license.php 64 | Features: Cipher DPC HDRI Modules 65 | Delegates (built-in): bzlib freetype jng jpeg ltdl lzma png tiff xml zlib 66 | ``` 67 | 68 | To enable animation support, you need to specifying the desired number of frames in the output animation. For example, specify it before your script with: 69 | ``` 70 | $ ITERMPLOT_FRAMES=30 python script.py 71 | ``` 72 | 73 | You can also save the resulting gif file by using `ITERMPLOT_OUTFILE` environment variable: 74 | ``` 75 | $ ITERMPLOT_FRAMES=30 ITERMPLOT_OUTFILE=out.gif python script.py 76 | ``` 77 | 78 | Currently animation does not support reverse video with ITERMPLOT=rv. 79 | 80 | ### Configure lines 81 | 82 | You can configure the number of lines used with the `ITERMPLOT_LINES` environment variable. For example: 83 | ```{sh} 84 | ITERMPLOT_LINES=5 python3 simple.py 85 | ``` 86 | 87 | ### Python 2 and Python 3 support 88 | 89 | Now supports Python 2, even if this makes me want to cry 😭 90 | 91 | ## Installation 92 | 93 | ### Using pip 94 | 95 | Install using `pip` using the command: 96 | ```{sh} 97 | pip3 install itermplot 98 | ``` 99 | 100 | itermplot is enabled by setting `MPLBACKEND` in your environment. If you use `bash`, then this can be accomplished using the command: 101 | ```{sh} 102 | export MPLBACKEND="module://itermplot" 103 | ``` 104 | Note: you can add the `export` line above to your `.profile` file so that itermplot is always enabled in your terminal. 105 | 106 | ### Testing 107 | 108 | To test your installation you can do the following in your iTerm2 console: 109 | ``` 110 | $ echo $MPLBACKEND 111 | module://itermplot 112 | $ python3 113 | Python 3.5.2 (default, Oct 24 2016, 09:14:06) 114 | [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)] on darwin 115 | Type "help", "copyright", "credits" or "license" for more information. 116 | >>> import matplotlib.pyplot as plt 117 | >>> plt.plot([1,2,3]) 118 | [] 119 | >>> plt.show() 120 | ``` 121 | 122 | You should see a plot! 123 | 124 | ## Uninstall 125 | 126 | You can disable this backend by unsetting the `MPLBACKEND` environment variable. 127 | ``` 128 | $ unset MPLBACKEND 129 | $ echo $MPLBACKEND 130 | 131 | $ python3 132 | Python 3.5.2 (default, Oct 24 2016, 09:14:06) 133 | [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)] on darwin 134 | Type "help", "copyright", "credits" or "license" for more information. 135 | >>> import matplotlib.pyplot as plt 136 | >>> plt.plot([1,2,3]) 137 | [] 138 | >>> plt.show() 139 | ``` 140 | 141 | To remove the package completely, run: 142 | ``` 143 | pip3 uninstall itermplot 144 | ``` 145 | 146 | ## Bugs 147 | 148 | This backend is very alpha, so if you have a problem please raise an Issue on GitHub and I will try to fix it. 149 | 150 | I also accept (and appreciate!) good patches / pull request. Thanks to [garrywu](https://github.com/garywu), [brenshanny](https://github.com/brenshanny), [hbredin](https://github.com/hbredin), [zevlg](https://github.com/zevlg) for their patches so far. 151 | 152 | ## Other cool things 153 | 154 | I encourage you to check-out some of my [other little projects](https://github.com/daleroberts). Lots more coming as I slowly release them... 155 | -------------------------------------------------------------------------------- /docs/animated.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daleroberts/itermplot/e8427ce87b0d74e18e14d8c092a01696c17b4c3e/docs/animated.gif -------------------------------------------------------------------------------- /docs/lightdark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daleroberts/itermplot/e8427ce87b0d74e18e14d8c092a01696c17b4c3e/docs/lightdark.png -------------------------------------------------------------------------------- /docs/sea.py: -------------------------------------------------------------------------------- 1 | import matplotlib 2 | import matplotlib.pyplot as plt 3 | import seaborn as sns 4 | iris = sns.load_dataset("iris") 5 | species = iris.pop("species") 6 | g = sns.clustermap(iris) 7 | plt.show() 8 | -------------------------------------------------------------------------------- /docs/simple.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | plt.plot([1,2,3]) 3 | plt.show() 4 | -------------------------------------------------------------------------------- /docs/subplots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daleroberts/itermplot/e8427ce87b0d74e18e14d8c092a01696c17b4c3e/docs/subplots.png -------------------------------------------------------------------------------- /docs/test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import networkx as nx 4 | 5 | plt.rcParams["font.size"] = 10 6 | 7 | plt.figure(figsize=(8,3)) 8 | 9 | ax = plt.subplot(121) 10 | x = np.arange(0,10,0.001) 11 | ax.plot(x, np.sin(np.sinc(x)), 'r', lw=2) 12 | ax.set_title('Nice wiggle') 13 | 14 | ax = plt.subplot(122) 15 | plt.tick_params(axis='both', left='off', top='off', right='off', bottom='off', labelleft='off', labeltop='off', labelright='off', labelbottom='off') 16 | G = nx.random_geometric_graph(200, 0.125) 17 | pos=nx.spring_layout(G) 18 | nx.draw_networkx_edges(G, pos, alpha=0.2) 19 | nx.draw_networkx_nodes(G, pos, node_color='r', node_size=12) 20 | ax.set_title('Random graph') 21 | 22 | plt.show() -------------------------------------------------------------------------------- /itermplot/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | An iTerm2 matplotlib backend. 4 | 5 | Author: Dale Roberts 6 | """ 7 | 8 | from __future__ import absolute_import, division, print_function, unicode_literals 9 | 10 | import six 11 | 12 | import numpy as np 13 | import matplotlib 14 | import sys 15 | import os 16 | import io 17 | 18 | from matplotlib._pylab_helpers import Gcf 19 | from matplotlib.colors import ColorConverter 20 | from matplotlib.backend_bases import FigureManagerBase, TimerBase 21 | from matplotlib.backends.backend_mixed import MixedModeRenderer 22 | from matplotlib.backends.backend_pdf import ( 23 | FigureCanvasPdf, 24 | PdfPages, 25 | PdfFile, 26 | RendererPdf, 27 | ) 28 | from matplotlib.backends.backend_agg import FigureCanvasAgg 29 | from matplotlib.animation import ImageMagickWriter 30 | from matplotlib.figure import Figure 31 | from base64 import b64encode 32 | 33 | to_rgba = ColorConverter().to_rgba 34 | rcParams = matplotlib.rcParams 35 | 36 | try: 37 | TMUX = os.getenv("TERM", "").startswith("screen") 38 | LINES = int(os.getenv("ITERMPLOT_LINES", "-1")) 39 | THEME = os.getenv("ITERMPLOT", "") # perhaps rename this now 40 | OUTFILE = os.getenv("ITERMPLOT_OUTFILE", "out.gif") 41 | PLOTFILE = os.getenv("ITERMPLOT_PLOTFILE", "plot.png") 42 | FRAMES = int(os.getenv("ITERMPLOT_FRAMES", "0")) 43 | COLORS = ColorConverter.colors 44 | except ValueError: 45 | print("Error: problems with itermplot configuration.") 46 | sys.exit(1) 47 | 48 | if sys.version_info < (3,): 49 | # Supporting Python 2 makes me want to cry :_( 50 | def B(x): 51 | return bytes(x) 52 | 53 | 54 | else: 55 | 56 | def B(x): 57 | return bytes(x, "utf-8") 58 | 59 | 60 | def revvideo(x): 61 | """Try to 'reverse video' the color. Otherwise, 62 | return the object unchanged if it can't.""" 63 | 64 | def rev(c): 65 | if isinstance(c, six.string_types): 66 | c = to_rgba(c) 67 | 68 | if len(c) == 4: 69 | r, g, b, a = c 70 | return (1.0 - r, 1.0 - g, 1.0 - b, a) 71 | else: 72 | r, g, b = c 73 | return (1.0 - r, 1.0 - g, 1.0 - b, 1.0) 74 | 75 | try: 76 | if isinstance(x, str) and x == "none": 77 | return x 78 | if isinstance(x, np.ndarray): 79 | return np.array([rev(el) for el in x]) 80 | return rev(x) 81 | except (ValueError, KeyError) as e: 82 | print("bad", x, e) 83 | return x 84 | 85 | 86 | def imgcat(data, fn="plot.pdf"): 87 | """Output the image data to the iTerm2 console. If `lines` is greater 88 | than zero then advance the console `lines` number of blank lines, move 89 | back, and then output the image. This is the default behaviour if TMUX 90 | is detected (lines set to 10).""" 91 | 92 | lines = LINES 93 | 94 | if TMUX: 95 | if lines == -1: 96 | lines = 10 97 | osc = b"\033Ptmux;\033\033]" 98 | st = b"\a\033\\" 99 | else: 100 | osc = b"\033]" 101 | st = b"\a" 102 | csi = b"\033[" 103 | 104 | buf = bytes() 105 | 106 | if lines > 0: 107 | buf += lines * b"\n" + csi + b"?25l" + csi + B("%dF" % lines) + osc 108 | dims = "width=auto;height=%d;preserveAspectRatio=1" % lines 109 | else: 110 | buf += osc 111 | dims = "width=auto;height=auto" 112 | 113 | buf += B("1337;File=name=") 114 | buf += b64encode(B(fn)) 115 | buf += B(";size=%d;inline=1;" % len(data) + dims + ":") 116 | buf += b64encode(data) + st 117 | 118 | if lines > 0: 119 | buf += csi + B("%dE" % lines) + csi + b"?25h" 120 | 121 | if hasattr(sys.stdout, "buffer"): 122 | sys.stdout.buffer.write(buf) 123 | else: 124 | sys.stdout.write(buf) 125 | sys.stdout.flush() 126 | 127 | print() 128 | 129 | 130 | def draw_if_interactive(): 131 | if matplotlib.is_interactive(): 132 | figmanager = Gcf.get_active() 133 | if figmanager is not None: 134 | figmanager.show() 135 | 136 | 137 | def show(): 138 | figmanager = Gcf.get_active() 139 | if figmanager is not None: 140 | figmanager.show() 141 | else: 142 | for manager in Gcf.get_all_fig_managers(): 143 | manager.show() 144 | 145 | 146 | def new_figure_manager(num, *args, **kwargs): 147 | FigureClass = kwargs.pop("FigureClass", Figure) 148 | thisFig = FigureClass(*args, **kwargs) 149 | return new_figure_manager_given_figure(num, thisFig) 150 | 151 | 152 | def new_figure_manager_given_figure(num, figure): 153 | canvas = FigureCanvas(figure) 154 | manager = FigureManager(canvas, num) 155 | return manager 156 | 157 | 158 | class ItermplotCanvasMixin: 159 | def __init__(self): 160 | self.reversed = False 161 | self.supports_blit = False 162 | self.timer = None 163 | 164 | def reverse(self, **kwargs): 165 | if self.reversed: 166 | return 167 | 168 | def modify(c): 169 | fcset = False 170 | 171 | try: 172 | ec = obj.get_edgecolor() 173 | obj.set_edgecolor(revvideo(ec)) 174 | except AttributeError as e: 175 | pass 176 | 177 | try: 178 | fc = obj.get_facecolor() 179 | obj.set_facecolor(revvideo(fc)) 180 | fcset = True 181 | except AttributeError as e: 182 | pass 183 | 184 | try: 185 | if not fcset: 186 | c = obj.get_color() 187 | obj.set_color(revvideo(c)) 188 | except AttributeError as e: 189 | pass 190 | 191 | seen = set() 192 | for obj in self.figure.findobj(): 193 | if not obj in seen: 194 | modify(obj) 195 | seen.add(obj) 196 | 197 | self.reversed = True 198 | 199 | def new_timer(self, *args, **kwargs): 200 | self.timer = TimerBase(*args, **kwargs) 201 | return self.timer 202 | 203 | def before_print(self, **kwargs): 204 | transparent = kwargs.pop("transparent", rcParams["savefig.transparent"]) 205 | 206 | if transparent: 207 | kwargs.setdefault("facecolor", "none") 208 | kwargs.setdefault("edgecolor", "none") 209 | original_axes_colors = [] 210 | 211 | for ax in self.figure.axes: 212 | patch = ax.patch 213 | original_axes_colors.append( 214 | (patch.get_facecolor(), patch.get_edgecolor()) 215 | ) 216 | patch.set_facecolor("none") 217 | patch.set_edgecolor("none") 218 | else: 219 | kwargs.setdefault("facecolor", rcParams["savefig.facecolor"]) 220 | kwargs.setdefault("edgecolor", rcParams["savefig.edgecolor"]) 221 | 222 | if "rv" in THEME: 223 | self.reverse() 224 | 225 | 226 | class FigureCanvasItermplotPdf(FigureCanvasPdf, ItermplotCanvasMixin): 227 | def __init__(self, figure): 228 | FigureCanvasPdf.__init__(self, figure) 229 | ItermplotCanvasMixin.__init__(self) 230 | 231 | def print_pdf(self, filename, **kwargs): 232 | ItermplotCanvasMixin.before_print(self, **kwargs) 233 | FigureCanvasPdf.print_pdf(self, filename, **kwargs) 234 | 235 | 236 | class FigureCanvasItermplotPng(FigureCanvasAgg, ItermplotCanvasMixin): 237 | def __init__(self, figure): 238 | FigureCanvasAgg.__init__(self, figure) 239 | ItermplotCanvasMixin.__init__(self) 240 | 241 | def print_png(self, filename, **kwargs): 242 | ItermplotCanvasMixin.before_print(self, **kwargs) 243 | FigureCanvasAgg.print_png(self, filename) 244 | 245 | 246 | class ItermplotImageMagickWriter(ImageMagickWriter): 247 | def cleanup(self): 248 | # ImageMagickWriter perhaps can expose out and err, PR 249 | # to Matplotlib someday 250 | out, err = self._proc.communicate() 251 | self.data = io.BytesIO(out) 252 | self._frame_sink().close() 253 | 254 | 255 | class ItermplotFigureManager(FigureManagerBase): 256 | def __init__(self, canvas, num): 257 | FigureManagerBase.__init__(self, canvas, num) 258 | 259 | def animate(self, loops, outfile=None, dpi=80): 260 | if outfile is None: 261 | outfile = "gif:-" 262 | 263 | self.canvas.draw_event(None) 264 | 265 | writer = ItermplotImageMagickWriter() 266 | with writer.saving(self.canvas.figure, outfile, dpi): 267 | for _ in range(loops): 268 | self.canvas.timer._on_timer() 269 | writer.grab_frame() 270 | 271 | if outfile != "gif:-": 272 | with open(outfile, "rb") as f: 273 | data = io.BytesIO(f.read()) 274 | else: 275 | data = writer.data 276 | 277 | return data 278 | 279 | def show(self): 280 | data = io.BytesIO() 281 | 282 | loops = FRAMES 283 | try: 284 | loops = int(loops) 285 | except ValueError: 286 | loops = 0 287 | if not loops or self.canvas.timer is None: 288 | fn = PLOTFILE 289 | self.canvas.print_figure( 290 | data, dpi=200, facecolor="none", edgecolor="none", transparent=True 291 | ) 292 | else: 293 | outfile = OUTFILE 294 | fn = "plot.gif" 295 | data = self.animate(loops, outfile) 296 | 297 | if hasattr(data, "getbuffer"): 298 | imgcat(data.getbuffer(), fn) 299 | else: # Python 2 300 | imgcat(data.getvalue(), fn) 301 | 302 | 303 | if os.path.splitext(PLOTFILE)[1] == ".png": 304 | FigureCanvas = FigureCanvasItermplotPng 305 | else: 306 | FigureCanvas = FigureCanvasItermplotPdf 307 | 308 | FigureManager = ItermplotFigureManager 309 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | from os import path 3 | from io import open 4 | 5 | here = path.abspath(path.dirname(__file__)) 6 | 7 | try: 8 | with open(path.join(here, 'README'), encoding='utf-8') as f: 9 | long_description = f.read() 10 | except(IOError): 11 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 12 | long_description = f.read() 13 | 14 | setup(name='itermplot', 15 | packages=find_packages(), 16 | install_requires=[ 17 | 'matplotlib', 18 | 'six', 19 | 'numpy' 20 | ], 21 | version='0.4', 22 | description='An awesome iTerm2 backend for Matplotlib, so you can plot directly in your terminal.', 23 | long_description=long_description, 24 | long_description_content_type='text/markdown', 25 | url='http://github.com/daleroberts/itermplot', 26 | author='Dale Roberts', 27 | author_email='dale.o.roberts@gmail.com', 28 | license='MIT', 29 | zip_safe=False) 30 | --------------------------------------------------------------------------------