├── drawnow ├── __init__.py └── drawnow.py ├── setup.cfg ├── .gitignore ├── test-data ├── test.gif ├── test.mov ├── mandrill.png └── matlab_drawnow.m ├── MANIFEST ├── LISCENSE.txt ├── test.py ├── setup.py └── README.md /drawnow/__init__.py: -------------------------------------------------------------------------------- 1 | from .drawnow import * 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | 2 | [metadata] 3 | description-file = README.md 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | drawnow.egg-info/ 4 | __pycache__/ 5 | -------------------------------------------------------------------------------- /test-data/test.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stsievert/python-drawnow/HEAD/test-data/test.gif -------------------------------------------------------------------------------- /test-data/test.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stsievert/python-drawnow/HEAD/test-data/test.mov -------------------------------------------------------------------------------- /test-data/mandrill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stsievert/python-drawnow/HEAD/test-data/mandrill.png -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | # file GENERATED by distutils, do NOT edit 2 | README.txt 3 | setup.cfg 4 | setup.py 5 | drawnow/__init__.py 6 | drawnow/drawnow.py 7 | -------------------------------------------------------------------------------- /test-data/matlab_drawnow.m: -------------------------------------------------------------------------------- 1 | clc; clear all; close all; 2 | 3 | N = 16; 4 | x = zeros(N,N); 5 | 6 | figure() 7 | imshow(x) 8 | %function_to_draw() 9 | 10 | for i=1:N*N, 11 | x(i) = i; 12 | 13 | imshow(x) 14 | %function_to_draw() 15 | 16 | drawnow 17 | pause(0.01) 18 | end -------------------------------------------------------------------------------- /LISCENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright (C) 2014 Scott Sievert 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | from pylab import * 3 | from scipy.sparse.linalg import svds 4 | from drawnow import * 5 | 6 | def approx(x, k): 7 | """ Approximate x using the SVD keeping the k largest singular values. """ 8 | y = zeros_like(x) 9 | for i in arange(4): 10 | u, sigmas, v = svd(x[:,:,i]) 11 | sigmas[k:] = 0 12 | 13 | s = zeros_like(x[:,:,i]) 14 | fill_diagonal(s, sigmas) 15 | 16 | color = u.dot(s).dot(v) 17 | y[:,:,i] = np.clip(color, 0, 1) 18 | return y 19 | 20 | WIDTH = 7 21 | figure(figsize=(WIDTH, WIDTH / 2)) 22 | def draw_fig(): 23 | """ Uses Python's global scope """ 24 | subplot(1, 2, 1) 25 | imshow(x, cmap='gray') 26 | title('Original') 27 | axis('off') 28 | 29 | subplot(1, 2, 2) 30 | imshow(x_hat, cmap='gray') 31 | title('Approx with $k=%d$' % k) 32 | axis('off') 33 | # show() 34 | 35 | x = imread('test-data/mandrill.png') 36 | k_values = around(logspace(0.1, log10(64), num=10)).astype('int') 37 | for k in k_values: 38 | x_hat = approx(x, k) 39 | drawnow(draw_fig, stop_on_close=True) 40 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from os import path 3 | 4 | # Steps to update on PyPI: 5 | # 1. python setup.py sdist bdist_wheel 6 | # 2. twine upload dist/* 7 | 8 | this_directory = path.abspath(path.dirname(__file__)) 9 | with open(path.join(this_directory, 'README.md'), "r") as f: 10 | long_description = f.read() 11 | 12 | setup( 13 | name='drawnow', 14 | packages=['drawnow'], 15 | version='0.72.5', 16 | description='MATLAB-like drawnow', 17 | long_description=long_description, 18 | long_description_content_type='text/markdown', 19 | author='Scott Sievert', 20 | author_email='dev@stsievert.com', 21 | url='https://github.com/stsievert/python-drawnow', 22 | download_url='https://github.com/stsievert/python-drawnow/archive/master.zip', 23 | keywords=['figure', 'plotting', 'visualization', 'matlab'], 24 | use_2to3=True, 25 | requires=['matplotlib'], 26 | install_requires=['matplotlib>=1.5'], 27 | classifiers=[ 28 | "Intended Audience :: Science/Research", 29 | "License :: OSI Approved :: MIT License", 30 | "Natural Language :: English", "Operating System :: OS Independent", 31 | "Programming Language :: Python", "Topic :: Scientific/Engineering", 32 | "Topic :: Scientific/Engineering :: Visualization" 33 | ]) 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | `drawnow` for matplotlib 6 | ======================== 7 | 8 | The scientific community often runs iterative code, often in the form of 9 | simulation. It's often useful to see the results after each iteration. 10 | Accordingly, MATLAB® has a nice feature that allows you to update the 11 | figure, `drawnow`. This repo brings the same feature to Python's 12 | matplotlib, with some extras. 13 | 14 | Example: 15 | 16 | ![image0](test-data/test.gif) 17 | 18 | This is shown with `imshow`, but python-drawnow allows updates of any 19 | figure. 20 | 21 | Usage: 22 | 23 | ``` python 24 | # complete implementation of script found in test/test.py 25 | from pylab import * 26 | from drawnow import drawnow, figure 27 | # if global namespace, import plt.figure before drawnow.figure 28 | 29 | def approx(x, k): 30 | """Approximate x with k singular values""" 31 | ... 32 | 33 | figure(figsize=(7, 7/2)) 34 | def draw_fig(): 35 | subplot(1, 2, 1) 36 | imshow(x) 37 | 38 | subplot(1, 2, 2) 39 | imshow(x_hat) 40 | #show() 41 | 42 | x = imread('test-data/mandrill.png').mean(axis=2) 43 | k_values = around(logspace(0, 2, num=10)) 44 | for k in k_values: 45 | x_hat = approx(x, k) 46 | drawnow(draw_fig) 47 | ``` 48 | 49 | Documentation 50 | ============= 51 | 52 | If you want to wait for confirmation after update or the option to drop 53 | into a debugger, call `drawnow(function_to_draw_figure, confirm=True)`. 54 | 55 | If you only want to show the figure once, call 56 | `drawnow(function_to_draw_figure, show_once=True)`. The full 57 | documentation is included in the doc strings. Use `drawnow?` or 58 | `help(drawnow)` to see these docs. 59 | 60 | Jupyter/Spyder 61 | ============== 62 | 63 | Try running the folloowing code in a Jupyter input cell/in the 64 | console/etc: 65 | 66 | ``` 67 | %matplotlib 68 | ``` 69 | 70 | This will disable the Matplotlin inline mode and use the default 71 | plotting backend. For more detail, see the [IPython plotting 72 | documentation](https://ipython.readthedocs.io/en/stable/interactive/plotting.html#id1). 73 | 74 | Installation 75 | ============ 76 | 77 | Two options: 78 | 79 | 1. Run `pip install drawnow`. 80 | 2. Download this repository and run `python setup.py install`. 81 | 82 | Option 2 assumes a working Python installation with `pip`. I suggest 83 | Anaconda's distribution: For 84 | other options, see . 85 | 86 | Changes to code 87 | --------------- 88 | 89 | This does require *small* changes to your code. All it should really 90 | amount to is moving `figure(); plot(...); show()` inside a function; not 91 | much. 92 | -------------------------------------------------------------------------------- /drawnow/drawnow.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import sys 3 | import pdb 4 | import matplotlib.pyplot as plt 5 | 6 | 7 | def drawnow(draw_fig, show_once=False, confirm=False, stop_on_close=False, 8 | *args, **kwargs): 9 | """A function to refresh the current figure. 10 | 11 | Depends on matplotlib's interactive mode. Similar functionality to MATLAB's 12 | drawnow. 13 | 14 | Parameters 15 | ---------- 16 | draw_fig : callable 17 | The function that draws the figure you want to update 18 | show_once, optional : bool (default: False) 19 | If True, will call show() instead of draw(). 20 | confirm, optional : bool, (default: False) 21 | If True, wait for user input after each iteration and present 22 | option to drop to python debugger (pdb). 23 | stop_on_close, optional : bool (default: False) 24 | When the figure is closed, stop the program execution. 25 | *args : list 26 | The list of parameters to pass ``draw_fig()`` 27 | **kwargs : dict 28 | The keywords to pass to ``draw_fig()`` 29 | 30 | Notes 31 | ----- 32 | 33 | This function does not work with the inline plotting mode, which is 34 | common in Jupyter notebooks/lab, Spyder and the QTConsole. To 35 | disable this, run the following "magic" command: 36 | 37 | %matplotlib 38 | 39 | This will disable the Matplotlib inline mode and use the default plotting backend. For more detail, see `IPython's plotting documentation`_. 40 | 41 | Some limitations are the following: 42 | 43 | - Occaisonally ignores Ctrl-C especially while processing LaTeX. 44 | - The initial processing of Latex labels (typically on the x/y axis and 45 | title) is slow. However, matplotlib caches the results so any subsequent 46 | animations are pretty fast. 47 | - If two figures open and focus moved between figures, then other figure 48 | gets cleared. 49 | 50 | Usage Example 51 | ------------- 52 | >>> from pylab import * # import matplotlib before drawnow 53 | >>> from drawnow import drawnow, figure 54 | >>> def draw_fig_real(): 55 | >>> #figure() # don't call, otherwise opens new window 56 | >>> imshow(z, interpolation='nearest') 57 | >>> colorbar() 58 | >>> #show() 59 | >>> 60 | >>> N = 16 61 | >>> z = zeros((N,N)) 62 | >>> 63 | >>> figure() 64 | >>> for i in arange(N*N): 65 | >>> z.flat[i] = 0 66 | >>> drawnow(draw_fig_real) 67 | 68 | .. _IPython's plotting documentation: https://ipython.readthedocs.io/en/stable/interactive/plotting.html#id1 69 | 70 | """ 71 | # replace the current figure w/o opening new GUI 72 | plt.clf() 73 | draw_fig(*args, **kwargs) 74 | 75 | if show_once: 76 | plt.show() 77 | else: 78 | plt.draw_all() 79 | 80 | plt.pause(1e-3) # allows time to draw 81 | 82 | figures = plt.get_fignums() 83 | if stop_on_close and not figures: 84 | sys.exit() 85 | 86 | if confirm: 87 | string = raw_input('Hit to continue, "d" for debugger and "x" ' 88 | 'to exit: ') 89 | if string == 'x' or string == 'exit': 90 | sys.exit() 91 | if string == 'd' or string == 'debug': 92 | print('\nType "exit" to continue program execution\n') 93 | pdb.runeval('u') 94 | 95 | 96 | def figure(*args, **kwargs): 97 | """ 98 | Enable drawnow. This function just enables interactivity then 99 | call's matplotlib's ion() to enable interactivity. Any arguments passed to 100 | this function are then passed to plt.figure() 101 | 102 | This function only enables interactivity and calls figure. To implement 103 | interactivity yourself, call plt.ion() 104 | 105 | Parameters 106 | ---------- 107 | *argv : any 108 | pass these arguments to plt.figure 109 | **kwargs : any 110 | pass these arguments to plt.figure 111 | """ 112 | plt.ion() 113 | plt.figure(*args, **kwargs) 114 | --------------------------------------------------------------------------------