2010-01-29 46 views
16

我有一個python腳本,一些計算後會生成格式化爲gnuplot輸入的兩個數據文件。從python調用gnuplot

如何從python'調用'gnuplot?

我想給下面的Python字符串輸入到GNUPLOT:

"plot '%s' with lines, '%s' with points;" % (eout,nout) 

其中 'EOUT' 和 'NOUT' 是兩個文件名。

PS: 我寧願不要使用額外的python模塊(例如gnuplot-py),只有標準的API。

謝謝

+0

要撥打的gnuplot使用API​​(這是在C,所以你會寫一些膠水代碼就像gnuplot-py中的代碼)或者只是在shell中執行「gnuplot」? –

+0

只需在shell中執行gnuplot。 –

回答

6

一個簡單的方法可能是隻寫了第三個文件包含您的gnuplot的命令,然後告訴Python的與執行的gnuplot文件。說你寫

"plot '%s' with lines, '%s' with points;" % (eout,nout) 

到一個名爲tmp.gp的文件。然後你可以使用

from os import system, remove 
system('gnuplot tmp.gp') 
remove('tmp.gp') 
+3

謝謝,這個破解最終奏效了。 一提:system('gnuplot -persist tmp.gp')爲了在腳本結束後保持窗口。 –

22

subprocess模塊,您可以調用其他程序:

import subprocess 
plot = subprocess.Popen(['gnuplot'], stdin=subprocess.PIPE) 
plot.communicate("plot '%s' with lines, '%s' with points;" % (eout,nout)) 
+0

看完你的例子後,我寫了一個類似的函數,不幸的是,沒有結果。 (POpen = Popen,我相信這是一個錯字,但這不是問題) –

+1

是的,POpen是一個錯字。除此之外,也許你需要指定gnuplot的完整路徑,或者在另一條評論中添加你提到的'-persist'開關。你也可以檢查'plot.returncode'是否有錯誤。 – sth

14

子過程是在道格Hellemann的 Python Module of the Week

解釋的很清楚這非常適用:

import subprocess 
proc = subprocess.Popen(['gnuplot','-p'], 
         shell=True, 
         stdin=subprocess.PIPE, 
         ) 
proc.stdin.write('set xrange [0:10]; set yrange [-2:2]\n') 
proc.stdin.write('plot sin(x)\n') 
proc.stdin.write('quit\n') #close the gnuplot window 

人們還可以用「溝通」,但繪圖窗口即刻,除非關閉使用gnuplot暫停命令

proc.communicate(""" 
set xrange [0:10]; set yrange [-2:2] 
plot sin(x) 
pause 4 
""") 
+2

如果您只是想顯示窗口,並且沒有將Python關閉,請使用'shell = False'和gnuplot'--persist' ... – sdaau

4

我試圖做類似的事情,但另外我想從python內部輸入數據並輸出文件作爲變量(因此數據和圖形都不是實際文件)。這是我想出了:

#! /usr/bin/env python 

import subprocess 
from sys import stdout, stderr 
from os import linesep as nl 

def gnuplot_ExecuteCommands(commands, data): 
    args = ["gnuplot", "-e", (";".join([str(c) for c in commands]))] 
    program = subprocess.Popen(\ 
     args, \ 
     stdin=subprocess.PIPE, \ 
     stdout=subprocess.PIPE, \ 
     stderr=subprocess.PIPE, \ 
     ) 
    for line in data: 
     program.stdin.write(str(line)+nl) 
    return program 

def gnuplot_GifTest(): 
    commands = [\ 
     "set datafile separator ','",\ 
     "set terminal gif",\ 
     "set output",\ 
     "plot '-' using 1:2 with linespoints, '' using 1:2 with linespoints",\ 
     ] 
    data = [\ 
     "1,1",\ 
     "2,2",\ 
     "3,5",\ 
     "4,2",\ 
     "5,1",\ 
     "e",\ 
     "1,5",\ 
     "2,4",\ 
     "3,1",\ 
     "4,4",\ 
     "5,5",\ 
     "e",\ 
     ] 
    return (commands, data) 

if __name__=="__main__": 
    (commands, data) = gnuplot_GifTest() 
    plotProg = gnuplot_ExecuteCommands(commands, data) 
    (out, err) = (plotProg.stdout, plotProg.stderr) 
    stdout.write(out.read()) 

該腳本轉儲圖形到stdout作爲主要的最後一步。等效命令行(其中所述圖被管道輸送到「out.gif」)將是:

gnuplot -e "set datafile separator ','; set terminal gif; set output; plot '-' using 1:2 with linespoints, '' using 1:2 with linespoints" > out.gif 
1,1 
2,2 
3,5 
4,2 
5,1 
e 
1,5 
2,4 
3,1 
4,4 
5,5 
e 
2

下面是提供了一個接口,以wgnuplot.exe的類:

from ctypes import * 
import time 
import sys 
import os 

# 
# some win32 constants 
# 
WM_CHAR  = 0X0102 
WM_CLOSE = 16 
SW_HIDE  = 0 
STARTF_USESHOWWINDOW = 1 

WORD = c_ushort 
DWORD = c_ulong 
LPBYTE = POINTER(c_ubyte) 
LPTSTR = POINTER(c_char) 
HANDLE = c_void_p 

class STARTUPINFO(Structure): 
    _fields_ = [("cb",DWORD), 
     ("lpReserved",LPTSTR), 
     ("lpDesktop", LPTSTR), 
     ("lpTitle", LPTSTR), 
     ("dwX", DWORD), 
     ("dwY", DWORD), 
     ("dwXSize", DWORD), 
     ("dwYSize", DWORD), 
     ("dwXCountChars", DWORD), 
     ("dwYCountChars", DWORD), 
     ("dwFillAttribute", DWORD), 
     ("dwFlags", DWORD), 
     ("wShowWindow", WORD), 
     ("cbReserved2", WORD), 
     ("lpReserved2", LPBYTE), 
     ("hStdInput", HANDLE), 
     ("hStdOutput", HANDLE), 
     ("hStdError", HANDLE),] 

class PROCESS_INFORMATION(Structure): 
    _fields_ = [("hProcess", HANDLE), 
     ("hThread", HANDLE), 
     ("dwProcessId", DWORD), 
     ("dwThreadId", DWORD),] 

# 
# Gnuplot 
# 
class Gnuplot: 
    # 
    # __init__ 
    # 
    def __init__(self, path_to_exe): 
     # open gnuplot 
     self.launch(path_to_exe) 
     # wait till it's ready 
     if(windll.user32.WaitForInputIdle(self.hProcess, 1000)): 
      print "Error: Gnuplot timeout!" 
      sys.exit(1) 
     # get window handles 
     self.hwndParent = windll.user32.FindWindowA(None, 'gnuplot') 
     self.hwndText = windll.user32.FindWindowExA(self.hwndParent, None, 'wgnuplot_text', None) 



    # 
    # __del__ 
    # 
    def __del__(self): 
     windll.kernel32.CloseHandle(self.hProcess); 
     windll.kernel32.CloseHandle(self.hThread); 
     windll.user32.PostMessageA(self.hwndParent, WM_CLOSE, 0, 0) 


    # 
    # launch 
    # 
    def launch(self, path_to_exe): 
     startupinfo = STARTUPINFO() 
     process_information = PROCESS_INFORMATION() 

     startupinfo.dwFlags = STARTF_USESHOWWINDOW 
     startupinfo.wShowWindow = SW_HIDE 

     if windll.kernel32.CreateProcessA(path_to_exe, None, None, None, False, 0, None, None, byref(startupinfo), byref(process_information)): 
      self.hProcess = process_information.hProcess 
      self.hThread = process_information.hThread 
     else: 
      print "Error: Create Process - Error code: ", windll.kernel32.GetLastError() 
      sys.exit(1) 



    # 
    # execute 
    # 
    def execute(self, script, file_path): 
     # make sure file doesn't exist 
     try: os.unlink(file_path) 
     except: pass 

     # send script to gnuplot window 
     for c in script: windll.user32.PostMessageA(self.hwndText, WM_CHAR, ord(c), 1L) 

     # wait till gnuplot generates the chart 
     while(not (os.path.exists(file_path) and (os.path.getsize(file_path) > 0))): time.sleep(0.01) 
2

我有點很晚,但是由於花了我一些時間才能使它工作,也許值得注意一下。這些程序在Windows上使用Python 3.3.2。

請注意,在任何地方都可以使用字節,而不是字符串(例如,b「的情節X」,不是簡單的「陰謀X」),但如果這是一個問題,簡單地做一些事情,如:

"plot x".encode("ascii") 

首先解決辦法:用溝通發送一切,並關閉時,它的完成。一定不要忘記暫停,或者窗口一下子關閉。但是,如果使用gnuplot將圖像存儲在文件中,則不會出現問題。

from subprocess import * 
path = "C:\\app\\gnuplot\\bin\\gnuplot" 
p = Popen([path], stdin=PIPE, stdout=PIPE) 
p.communicate(b"splot x*y\npause 4\n") 

第二種解決方案:使用stdin.write(...)一個接一個地發送命令。但是,別忘了沖水! (這是我一開始就不明白的)並且在作業完成時使用終止來關閉連接和gnuplot。

from subprocess import * 
path = "C:\\app\\gnuplot\\bin\\gnuplot" 
p = Popen([path], stdin=PIPE, stdout=PIPE) 

p.stdin.write(b"splot x*y\n") 
p.stdin.flush() 
... 
p.stdin.write(b"plot x,x*x\n") 
p.stdin.flush() 
... 
p.terminate() 
2

我和Ben的建議一起使用,因爲我從一份芹菜工作計算圖表,發現從stdout讀取時會鎖定。我重新設計了它,就像使用StringIO創建stdin的目標文件,然後subprocess.communicate通過stdout立即得到結果,不需要讀取。


from subprocess import Popen, PIPE 
from StringIO import StringIO            
from os import linesep as nl 

def gnuplot(commands, data):              
    """ drive gnuplot, expects lists, returns stdout as string """    

    dfile = StringIO()               
    for line in data:               
     dfile.write(str(line) + nl)            

    args = ["gnuplot", "-e", (";".join([str(c) for c in commands]))]    
    p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)      

    dfile.seek(0)                
    return p.communicate(dfile.read())[0] 

def gnuplot_GifTest(): 
    commands = [\ 
     "set datafile separator ','",\ 
     "set terminal gif",\ 
     "set output",\ 
     "plot '-' using 1:2 with linespoints, '' using 1:2 with linespoints",\ 
     ] 
    data = [\ 
     "1,1",\ 
     "2,2",\ 
     "3,5",\ 
     "4,2",\ 
     "5,1",\ 
     "e",\ 
     "1,5",\ 
     "2,4",\ 
     "3,1",\ 
     "4,4",\ 
     "5,5",\ 
     "e",\ 
     ] 
    return (commands, data) 

if __name__=="__main__": 
    (commands, data) = gnuplot_GifTest() 
    print gnuplot(commands, data) 
1

這是另一個例子,它擴展了以前的一些答案。該解決方案需要Gnuplot 5.1,因爲它使用數據塊。有關數據塊的更多信息,請在gnuplot中執行help datablocks。 以前的一些方法存在的問題是plot '-'會立即消耗緊跟在plot命令之後的數據。在後續的繪圖命令中重複使用相同的數據是不可能的。數據塊可以用來緩解這個問題。使用數據塊我們可以模擬多個數據文件。例如,您可能想要使用來自兩個數據文件的數據繪製圖表,例如, plot "myData.dat" using 1:2 with linespoints, '' using 1:3 with linespoints, "myData2.dat" using 1:2 with linespoints。我們可以將這些數據直接提供給gnuplot,而無需創建實際的數據文件。

import sys, subprocess 
from os import linesep as nl 
from subprocess import Popen, PIPE 


def gnuplot(commands, data):              
    """ drive gnuplot, expects lists, returns stdout as string """ 
    script= nl.join(data)+nl.join(commands)+nl 
    print script 
    args = ["gnuplot", "-p"] 
    p = Popen(args, shell=False, stdin=PIPE)      
    return p.communicate(script)[0] 

def buildGraph(): 
    commands = [\ 
     "set datafile separator ','",\ 
     "plot '$data1' using 1:2 with linespoints, '' using 1:3 with linespoints, '$data2' using 1:2 with linespoints",\ 
     ] 
    data = [\ 
     "$data1 << EOD",\ 
     "1,30,12",\ 
     "2,40,15",\ 
     "3,35,20",\ 
     "4,60,21",\ 
     "5,50,30",\ 
     "EOD",\ 
     "$data2 << EOD",\ 
     "1,20",\ 
     "2,40",\ 
     "3,40",\ 
     "4,50",\ 
     "5,60",\ 
     "EOD",\ 
     ] 

    return (commands, data) 


def main(args): 
    (commands, data) = buildGraph() 
    print gnuplot(commands, data) 


if __name__ == "__main__": 
    main(sys.argv[1:]) 

此方法是一個比較通用的比plot '-',因爲它可以更容易地將相同的數據多次重複使用,包括在相同的繪圖命令:https://stackoverflow.com/a/33064402/895245 注意,這種方法要求的數據被饋送到gnuplot之前的劇情指令!

而且,我沒有使用IOString作爲@ppetraki做,因爲很明顯,這不僅僅是一個簡單的列表木匠慢:https://waymoot.org/home/python_string/

+0

數據中的值必須不帶有逗號。列由gnuplot幫助中報告的空格分隔。同樣在python3中,你必須編碼()字符串腳本:'return p.communicate(script。編碼('utf-8'))[0]' –

+0

@terencehill事實並非如此:只要您指定命令'set datafile separator','',就可以使用任何分隔符,見上面的示例。 –

+0

對不起,我改變了命令,錯過了指向分隔符的行。 –