├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── PyGnuplot.py ├── README.rst ├── example.py ├── fit_out.png ├── install ├── install.bat ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | setup.cfg 2 | *.swp 3 | *.pyc 4 | *.out 5 | *.pdf 6 | .ropeproject 7 | build/ 8 | dist/ 9 | tests/ 10 | adons/ 11 | PyGnuplot.egg-info/ 12 | Pipfile 13 | *.dat 14 | *.log 15 | .DS_Store 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" # this works for Linux but is ignored on macOS or Windows 4 | - "3.8" 5 | # - "nightly" 6 | # PyPy versions 7 | - "pypy" # currently Python 2.7.13, PyPy 7.1.1 8 | - "pypy3" # currently Python 3.6.1, PyPy 7.1.1-beta0 9 | #services: 10 | #- xvfb 11 | before_install: 12 | #- "export DISPLAY=:99.0" 13 | - "sudo apt-get install gnuplot-x11" 14 | install: 15 | - pip3 install --upgrade pip || pip install --upgrade pip # all three OSes agree about 'pip3' 16 | - pip3 install . || pip install . 17 | - pip3 install nose coverage || pip install nose coverage 18 | # - env: PATH=/c/Python38:/c/Python38/Scripts:$PATH 19 | # - pip install pypiview 20 | # - pip install coveralls 21 | script: 22 | - python example.py 23 | - python setup.py nosetests --cover-package pypiview # --with-coverage 24 | - echo 'if example.pdf was generated, test is sucessfull' 25 | - ls example.pdf 26 | - rm example.pdf # if file didn't exist, this will fail 27 | #after_sucess: 28 | #coveralls 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Ben Schneider 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 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.rst 3 | include example.py 4 | -------------------------------------------------------------------------------- /PyGnuplot.py: -------------------------------------------------------------------------------- 1 | 2 | ''' 3 | By Ben Schneider 4 | 5 | Simple python wrapper for Gnuplot 6 | Thanks to steview2000 for suggesting to separate processes, 7 | jrbrearley for help with debugging in python 3.4+ 8 | 9 | Special Thanks to ddip! 10 | This code was rewritten according to ddipp's suggestions resulting in 11 | a cleaner and better code and finnaly giving accesss to gnuplot returns 12 | thus allowing the use of the gnuplot fit function. 13 | 14 | Also thanks to all the others who commented gave inputs and suggestions. 15 | 16 | Example: 17 | from PyGnuplot import gp 18 | import numpy as np 19 | X = np.arange(10) 20 | Y = np.sin(X/(2*np.pi)) 21 | Z = Y**2.0 22 | fig1 = gp() 23 | fig1.save([X,Y,Z]) # saves data into tmp.dat 24 | fig1.c('plot "tmp.dat" u 1:2 w lp) # send 'plot instructions to gnuplot' 25 | fig1.c('replot "tmp.dat" u 1:3' w lp) 26 | fig1.pdf('myfigure.pdf') # outputs pdf file 27 | 28 | ''' 29 | 30 | import sys 31 | from subprocess import PIPE, Popen 32 | from threading import Thread 33 | from time import sleep 34 | 35 | try: 36 | from queue import Queue, Empty # Python 3.x 37 | except ImportError: 38 | from Queue import Queue, Empty # Python 2.x 39 | 40 | ON_POSIX = 'posix' in sys.builtin_module_names 41 | 42 | 43 | class gp(object): 44 | """PyGnuplot object figure 45 | example: 46 | f1 = gp(r"C:\Program Files\gnuplot\bin\gnuplot.exe") 47 | pi = f1.a('print pi') 48 | """ 49 | 50 | def __init__(self, gnuplot_address='gnuplot'): 51 | # also also initialize with gnuplot_address = r"C:\Program Files\gnuplot\bin\gnuplot.exe" 52 | self.gnuplot_address=gnuplot_address 53 | ''' open pipe with gnuplot ''' 54 | self.p = Popen([gnuplot_address], stdin=PIPE, stderr=PIPE, stdout=PIPE, 55 | bufsize=1, close_fds=ON_POSIX, 56 | shell=False, universal_newlines=True) 57 | self.q_err = Queue() 58 | self.t_err = Thread(target=self.enqueue_std, 59 | args=(self.p.stderr, self.q_err)) 60 | self.t_err.daemon = True # thread dies with the program 61 | self.t_err.start() 62 | self.q_out = Queue() 63 | self.t_out = Thread(target=self.enqueue_std, 64 | args=(self.p.stdout, self.q_out)) 65 | self.t_out.daemon = True # thread dies with the program 66 | self.t_out.start() 67 | self.r() # clear return buffer 68 | self.default_term = str(*self.a('print GPVAL_TERM')) 69 | 70 | def enqueue_std(self, out, queue): 71 | ''' used to setup the queues for the return buffers''' 72 | for line in iter(out.readline, ''): 73 | queue.put(line) 74 | out.close() 75 | 76 | def c(self, command): 77 | ''' send a command to gnuplot. 78 | this does not check for responses 79 | >>> w('plot sin(x)') # only send a command to gnuplot''' 80 | self.p.stdin.write(command + '\n') # \n 'send return in python 2.7' 81 | self.p.stdin.flush() # send the command in python 3.4+ 82 | 83 | def r(self, vtype=str, timeout=0.05): 84 | ''' read line without blocking, also clears the buffer. 85 | >>> r() # read response from gnuplot''' 86 | lines = [] 87 | while True: 88 | try: 89 | line = self.q_err.get(timeout=timeout) # or .get_nowait() 90 | lines.append(vtype(line.strip())) 91 | except Empty: 92 | break 93 | return lines 94 | 95 | def a(self, command='', vtype=str, timeout=0.05): 96 | ''' ask gnuplot (write and get answer) 97 | >>> a('print pi') 98 | ''' 99 | self.c(command) 100 | sleep(0.01) # wait 10ms for gnuplot 101 | return self.r(vtype, timeout) 102 | 103 | def m_str(self, data, delimiter=' '): 104 | ''' turn data into string format 105 | this string format can be used when sending data to gnuplot 106 | usually via: plot "-" u 1:2 w lp''' 107 | xy = list(zip(*data)) 108 | ascii_st = '' 109 | for i in xy: 110 | for j in i: 111 | ascii_st += str(j) + delimiter 112 | ascii_st += '\n' 113 | return ascii_st 114 | 115 | def plot(self, data, com='plot "-" u 1:2 w lp'): 116 | ''' quick plot data in gnuplot 117 | it basically pipes the data to gnuplot and plots it 118 | default plot : 119 | com = "plot "-" u 1:2 w lp" 120 | ''' 121 | str_data = self.m_str(data) 122 | self.c(com) 123 | self.c(str_data+'e') # add end character to plot string 124 | return self.r() 125 | 126 | def fit(self, data, func='y(x)=a + b*x', via='a,b', limit=1e-9, filename='tmp.dat', wait=1): 127 | '''simple quick way to fit with gnuplot 128 | this fit function temporarily stores the data in a file. 129 | Inputs: 130 | func : fitting function y(x) or f(x,y) or ... 131 | via : space separated variables to fit 132 | data : data set to fit 133 | filename : location where it can temporarily store its data 134 | wait : timing in s on how long to wait for the fit results 135 | Outputs: 136 | fit results in same order as via is defined 137 | report generated by gnuplot 138 | ''' 139 | self.save(data, filename=filename) 140 | func_name = func.split('=')[0] 141 | self.c(func) # 'y(x)=a+b*x' 142 | self.c('set fit limit '+str(limit)) 143 | self.c('fit ' + func_name + ' "' + filename + '" via ' + via) 144 | sleep(wait) # wait until fitting is done 145 | report = self.a() # if no report is returned maybe increase the wait time here 146 | return self.get_variables(via), report 147 | 148 | def fit2d(self, data, func='y(x)=a + b*x', via='a,b', limit=1e-9): 149 | '''simple quick way to fit with gnuplot 150 | Inputs: 151 | func : fitting function y(x) or f(x,y) or ... 152 | via : space separated variables to fit 153 | data : data set to fit 154 | Outputs: 155 | fit results in same order as via is defined 156 | report generated by gnuplot 157 | ''' 158 | str_data = self.m_str(data) 159 | func_name = func.split('=')[0] 160 | self.c(func) # 'y(x)=a+b*x' 161 | self.c('set fit limit '+str(limit)) 162 | self.c('fit ' + func_name + ' "-" via ' + via) 163 | report = self.a(str_data+'e') 164 | return self.get_variables(via), report 165 | 166 | def get_variables(self, via): 167 | ''' 168 | returns values stored in gnuplot as given by via 169 | Inputs: 170 | via : for example via = 'a b c d e' 171 | Outputs: 172 | results in same order as via is given 173 | ''' 174 | vals = via.split(',') 175 | ret = [] 176 | for i in vals: 177 | r = self.a('print ' + i) 178 | try: 179 | r = float(r[0]) # hard coded conversion if possible 180 | except ValueError: 181 | pass 182 | ret.append(r) 183 | return ret 184 | 185 | def save(self, data, filename='tmp.dat', delimiter=' '): 186 | ''' 187 | saves numbers arrays and text into filename (default = 'tmp.dat) 188 | (assumes equal sizes and 2D data sets) 189 | >>> s(data, filename='tmp.dat') # overwrites/creates tmp.dat 190 | ''' 191 | with open(filename, 'w') as f: 192 | filestr = self.m_str(data, delimiter=delimiter) 193 | f.write(filestr) 194 | f.close() # write the rest and close 195 | 196 | def empty_plot(self): 197 | self.c('plot [][-1:1] 1/0 t""') 198 | 199 | def ps(self, filename='tmp.ps', width=14, height=9, fontsize=12): 200 | '''Script to make gnuplot print into a postscript file 201 | >>> ps(filename='myfigure.ps') # overwrites/creates myfigure.ps 202 | ''' 203 | self.c('set term postscript size ' 204 | + str(width) + 'cm, ' 205 | + str(height) + 'cm color solid ' 206 | + str(fontsize) + " font 'Calibri';") 207 | self.c('set out "' + filename + '";replot;') 208 | self.c('set term ' + self.default_term + ';replot') 209 | return self.r() 210 | 211 | def pdf(self, filename='tmp.pdf', width=8.8, height=6, fontscale=0.5): 212 | '''Script to make gnuplot print into a pdf file 213 | >>> pdf(filename='myfigure.pdf') # overwrites/creates myfigure.pdf 214 | ''' 215 | self.c('set term pdfcairo fontscale ' 216 | + str(fontscale) + 'size ' 217 | + str(width) + 'cm, ' 218 | + str(height) + "cm;") 219 | self.c('set out "' + filename + '";replot;') 220 | self.c('set term ' + self.default_term + '; replot') 221 | return self.r() # clear buffer 222 | 223 | def quit(self): 224 | aa = self.a('exit') # close gnuplot 225 | self.p.kill() # kill pipe 226 | return aa 227 | 228 | 229 | if __name__ == '__main__': 230 | # test functionality 231 | import numpy as np 232 | f1 = gp() 233 | x = np.linspace(0, 20, 1001) 234 | yn = np.random.randn(1001)/10 235 | y = np.sin(x) 236 | data = [x, y+yn] 237 | func = 'y(x) = a + b*cos(x + c)' 238 | (a, b, c), report = f1.fit(data, func, via='a,b,c', limit=1e-9) 239 | f1.save(data, "tmp.dat") 240 | f1.a('plot "tmp.dat" w lp') 241 | f1.a('replot y(x)') 242 | dat_s = f1.m_str([x, y], delimiter='\t') 243 | print() 244 | print("fitting function is: " + func) 245 | print("fit report:") 246 | for line in report: 247 | print(line) 248 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://badge.fury.io/py/PyGnuplot@2x.svg 2 | :target: https://badge.fury.io/py/PyGnuplot 3 | 4 | .. image:: https://anaconda.org/benschneider/pygnuplot/badges/version.svg 5 | :target: https://anaconda.org/benschneider/pygnuplot 6 | 7 | .. image:: https://travis-ci.org/benschneider/PyGnuplot.svg?branch=experimental 8 | :target: https://travis-ci.org/benschneider/PyGnuplot 9 | 10 | .. image:: https://img.shields.io/badge/License-MIT-yellow.svg 11 | :target: https://github.com/benschneider/PyGnuplot/blob/master/LICENSE 12 | 13 | 14 | PyGnuplot: Python wrapper for Gnuplot 15 | ------------------------------------- 16 | 17 | Author: Ben Schneider 18 | 19 | Requires: 20 | ......... 21 | Gnuplot (http://www.gnuplot.info) 22 | 23 | Installation: 24 | ............. 25 | 26 | Using pip 27 | 28 | .. code:: 29 | 30 | pip install PyGnuplot 31 | 32 | Using conda 33 | 34 | .. code:: 35 | 36 | conda install -c benschneider pygnuplot 37 | 38 | Upgrade: 39 | ........ 40 | .. code:: 41 | 42 | pip install --upgrade PyGnuplot 43 | 44 | Basic Usage: 45 | ............ 46 | .. code:: 47 | 48 | from PyGnuplot import gp 49 | figure1 = gp() # Create a new figure handle 50 | figure2 = gp(r"C:\Program Files\gnuplot\bin\gnuplot.exe") # Can also specify which gnuplot to use 51 | figure1.a("plot sin(x)") 52 | figure2.a("plot cos(x)") 53 | pi = figure.a("print pi") 54 | 55 | 56 | Functions available with each figure: 57 | ..................................... 58 | 59 | **c(command)** 60 | 61 | pipe a command to gnuplot as if in gnuplot command promt 62 | 63 | .. code:: python 64 | 65 | c('plot sin(x)') 66 | 67 | **save(data, filename='tmp.dat')** 68 | 69 | save arrays into file (filename = 'tmp.dat') easily read by Gnuplot 70 | 71 | .. code:: python 72 | 73 | s([X,Y,Z]) # creates tmp.dat 74 | 75 | 76 | .. code:: python 77 | 78 | c('plot "tmp.dat" u 1:2') 79 | 80 | **a(command='', vtype=str, timeout=0.05)** 81 | 82 | asks gnuplot: it sends a command to gnuplot and returns its response 83 | This is paricularly handy when using gnuplots fitting features 84 | vtype can be used to change the return format 85 | timeout is the time to wait for a response 86 | 87 | .. code:: python 88 | 89 | a('print pi') # returns the value of pi 90 | 91 | .. code:: python 92 | 93 | a('print pi; print pi') # returns 2 times the value of pi 94 | 95 | **r(vtype=str, timeout=0.05)** 96 | 97 | reads the gnuplot return buffer until its empty 98 | 99 | 100 | **plot(data, filename='tmp.dat')** 101 | 102 | Plot some data. 103 | Sends plot instructions and the data to Gnuplot 104 | 105 | .. code:: python 106 | 107 | plot([x,y]) 108 | 109 | **plot_b(data, v1='d', v2='%double')** 110 | 111 | Similar to plot: 112 | Sends plot instructions and the data to Gnuplot 113 | However it sends them in binary format, 114 | which can be beneficial when the dealing with larger quanities of numbers 115 | 116 | **p(filename='tmp.ps', width=14, height=9, fontsize=12, term='x11')** 117 | 118 | Create postscript file (overwrites existing) 119 | 120 | .. code:: python 121 | 122 | p('myfile.ps') 123 | 124 | 125 | **pdf(filename='tmp.pdf', width=14, height=9, fontsize=12, term='x11')** 126 | 127 | Create a pdf file (overwrites existing) 128 | 129 | .. code:: python 130 | 131 | pdf('myfile.pdf') 132 | 133 | 134 | **quit()** 135 | 136 | Closes windows,then gnuplot, then the pipe 137 | 138 | Setup terminal 139 | .............. 140 | 141 | This script will use the same default terminal that gnuplot used 142 | (it reads the GPVAL_TERM value when gnuplot starts up) 143 | it can still be modified by the 'default_term' variable: 144 | 145 | 146 | .. code:: python 147 | 148 | from PyGnuplot import gp 149 | fig1 = gp() 150 | fig1.default_term = 'wxt' 151 | 152 | 153 | New features: 154 | ............. 155 | 156 | 157 | **fit2d(data, func='y(x)=a + b*x', via='a,b', limit=1e-9)** 158 | 159 | Quickly Fit a simple 2-D data set and return the fitting results. 160 | This uses the new ask function "a()" 161 | Here we gather the fitting info from gnuplot 162 | 163 | and: 164 | 165 | **fit(self, data, func='y(x)=a + b*x', via='a,b', limit=1e-9, filename='tmp.dat', wait=1)** 166 | 167 | Allows for sligtly more complex fitting, 168 | filename: stores data first into a temporary file default: tmp.dat 169 | wait: define a waiting time in sec for gnuplot to finish its fitting default: 1sec 170 | 171 | .. code:: python 172 | 173 | import numpy as np 174 | f1 = gp() 175 | x = np.linspace(0, 20, 1001) 176 | yn = np.random.randn(1001)/10 177 | y = np.sin(x) 178 | data = [x, y+yn] 179 | func = 'y(x) = a + b*cos(x + c)' # define a fitting function here. 180 | (a, b, c), report = f1.fit2d(data, func, via='a,b,c', limit=1e-9) # sending in the data the function used to fit and the variables that are to be found. 181 | f1.save(data, "tmp.dat") 182 | f1.a('plot "tmp.dat" w lp') 183 | f1.a('replot y(x)') 184 | 185 | +-----------------------------------------------------------------------------------------------------------------+ 186 | |.. figure:: https://user-images.githubusercontent.com/4573907/193154658-92513c20-ab3c-4b29-b487-d98b79d85942.png | 187 | +-----------------------------------------------------------------------------------------------------------------+ 188 | 189 | +-----------------------------------------------------------------------------------------------------------------+ 190 | |.. figure:: https://user-images.githubusercontent.com/4573907/193154419-133761a1-3e2f-4c00-87d2-2c47b7da62c5.png | 191 | +-----------------------------------------------------------------------------------------------------------------+ 192 | 193 | Examples: 194 | ......... 195 | 196 | * 1 Example code 197 | 198 | .. code:: python 199 | 200 | from PyGnuplot import gp 201 | import numpy as np 202 | X = np.arange(10) 203 | Y = np.sin(X/(2*np.pi)) 204 | Z = Y**2.0 205 | fig1 = gp() 206 | fig1.save([X,Y,Z]) 207 | fig1.c('plot "tmp.dat" u 1:2 w lp) 208 | fig1.c('replot "tmp.dat" u 1:3' w lp) 209 | fig1.p('myfigure.ps') 210 | 211 | 212 | * 2 Example file 213 | 214 | .. code:: 215 | 216 | python example.py 217 | 218 | +-----------------------------------------------------------------------------------------------------------------+ 219 | |.. figure:: https://cloud.githubusercontent.com/assets/4573907/17233530/e4be9342-5530-11e6-9c71-e812a2fb4000.png | 220 | +-----------------------------------------------------------------------------------------------------------------+ 221 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | from PyGnuplot import gp 2 | 3 | 4 | f1 = gp() 5 | e = 2.718281828459045 6 | 7 | 8 | def sin(x): 9 | ''' returns sin for each x ''' 10 | return (e**(i*1j)).imag 11 | 12 | 13 | x = list(range(1000)) 14 | x = [i/20 for i in x] # x = x/20 15 | y1 = [i-25 for i in x] # y1 = x-25 16 | y2 = [] # calculate y2 = y1*sin(y1) 17 | for i, j1 in enumerate(y1): 18 | y2.append(j1*sin(j1)) 19 | 20 | f1.s([x, y1, y2], filename='example.out') # save data into a file t.out 21 | f1.c('set title "example.pdf"; set xlabel "x-axis"; set ylabel "y-axis"') 22 | f1.c('set yrange [-25:25]; set key center top') 23 | f1.c("plot 'example.out' u 1:2 w l t 'y=x-25") # plot fist part 24 | f1.c("replot 'example.out' u 1:3 w l t 'y=(x-25)*sin(x-25)'") 25 | f1.c("replot 'example.out' u 1:(-$2) w l t 'y=25-x'") 26 | f1.pdf('example.pdf') # export figure into a pdf file 27 | -------------------------------------------------------------------------------- /fit_out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benschneider/PyGnuplot/69ae3ecee6bc446bd6e1aad534c209ccd26919de/fit_out.png -------------------------------------------------------------------------------- /install: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | python3 setup.py install --user 4 | -------------------------------------------------------------------------------- /install.bat: -------------------------------------------------------------------------------- 1 | python setup.py install --user 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = 'README.rst' 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | try: 2 | from setuptools import setup 3 | except ImportError: 4 | from distutils.core import setup 5 | 6 | with open('README.rst', 'r') as f: 7 | long_description = f.read() 8 | 9 | setup(name='PyGnuplot', 10 | py_modules=['PyGnuplot'], 11 | version='0.12.3', 12 | license='MIT', 13 | description='Python Gnuplot wrapper', 14 | long_description=long_description, 15 | author='Ben Schneider', 16 | author_email=' ', 17 | url='https://github.com/benschneider/PyGnuplot', 18 | download_url='https://github.com/benschneider/PyGnuplot/archive/0.12.3.tar.gz', 19 | keywords=['gnuplot', 'plot'], 20 | classifiers=["Topic :: Scientific/Engineering", 21 | "License :: OSI Approved :: MIT License", 22 | "Programming Language :: Python :: 2.6", 23 | "Programming Language :: Python :: 2.7", 24 | "Programming Language :: Python :: 3.5", 25 | "Programming Language :: Python :: 3.6", 26 | "Programming Language :: Python :: 3.7", 27 | "Programming Language :: Python :: 3.8", 28 | "Programming Language :: Python :: 3.9", 29 | "Programming Language :: Python :: 3.10", 30 | "Development Status :: 4 - Beta"], 31 | ) 32 | --------------------------------------------------------------------------------