Converting matplotlib plots to Igor Pro graphs

In my experience dealing with PhD students, who know python well, their difficulty transitioning to Igor Pro for data visualization and analysis is that it's not easy to reproduce a matplotlib graph as an Igor Pro graph. 

Although they realize the power of Igor Pro once the graph is displayed and we have cursors, zoom, pan, fitting, etc, the process of converting numpy data into Igor Pro and then displaying it is complex.

Python integration in Igor Pro 10 is great progress towards leveraging the benefits of both, but I would like an additional feature to convert a matplotlib graph into an Igor Pro graph.  This could be implemented as

1. Standalone python module which exports a graph `pxp` given a matplotlib plot

2. In an Igor Pro 10 python session, a function in the `igorpro` module which, given a matplotlib plot object, generates the Window recreation procedure to create the corresponding graph 

This is not an easy task as matplotlib is a huge module, but the conversion module could be built incrementally, focusing on the simplest types of plots first.  For Python users who are adept with matplotlib, seeing the equivalent Igor Pro code to generate their plot would help them learn Igor Pro.

I would be happy to help develop this.

Here's an example, coded with Claude:

    # Create a sample matplotlib plot
    x = np.linspace(0, 10, 100)
    y1 = np.sin(x)
    y2 = np.cos(x) * 0.5
    
    fig, ax = plt.subplots(figsize=(6, 4))
    ax.plot(x, y1, 'r-', linewidth=2, label='sin(x)')
    ax.plot(x, y2, 'b--', marker='o', markersize=4, 
            markevery=10, label='0.5*cos(x)')
    ax.set_xlabel('Time (s)')
    ax.set_ylabel('Amplitude')
    ax.grid(True)
    ax.legend()
    
    # Convert to Igor Pro format
    matplotlib_to_igor_itx(fig, 'example_output.itx', 'ExampleGraph')

 

Python plot (454.37 KB) Converted igor pro graph (453.42 KB)

That's an interesting idea, and probably doable for simpler graphs. I imagine you could at least get 'close enough' to the matplotlib graph as a starting point for making further modifications in Igor. As a potential alternative to saving Igor commands in an .itx file, you could also generate the graph directly from Python by taking advantage of the `igorpro.execute()` method, which allows you to execute any arbitrary Igor code. Here's a first pass at converting the graph you provided into a graph in Igor. Some things like the grid lines and legend are missing, but you get gist. You pass the 'fig' object in to the 'convert()' method after your code is finished running:

 

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.figure
import matplotlib.colors as colors
import igorpro
 
def convert(figure : matplotlib.figure.Figure):
    axes = figure.axes
    
    igorpro.execute('Display')
    
    # For now, assuming just one axis set for simplicity
    axis = axes[0]
    
    xLimits = axis.get_xlim()
    yLimits = axis.get_ylim()
 
    xLabel = axis.get_xlabel()
    yLabel = axis.get_ylabel()
 
    i = 0
    for trace in axis.get_lines():
 
        # Line data
        xdata = trace.get_xdata()
        ydata = trace.get_ydata()
 
        xWave = igorpro.wave.createfrom(f'xWave_{i}', xdata, overwrite=True)
        yWave = igorpro.wave.createfrom(f'yWave_{i}', ydata, overwrite=True)
 
        yWaveName = yWave.name()
        xWaveName = xWave.name()
 
        igorpro.execute(f'AppendToGraph {yWaveName} vs {xWaveName}')
 
        # Axis limits        
        igorpro.execute(f'SetAxis bottom {xLimits[0]}, {xLimits[1]}')
        igorpro.execute(f'SetAxis left {yLimits[0]}, {yLimits[1]}')
 
        # Axis labels
        igorpro.execute(f'Label bottom "{xLabel}"')
        igorpro.execute(f'Label left "{yLabel}"')
 
        # Line width
        lineWidth = trace.get_linewidth()
        igorpro.execute(f'ModifyGraph lsize({yWaveName})={lineWidth}')
 
        # Line style
        lineStyle = trace.get_linestyle()
        if lineStyle == '-':
            igorpro.execute(f'ModifyGraph lstyle({yWaveName}) = 0')
        elif lineStyle == '--':
            igorpro.execute(f'ModifyGraph lstyle({yWaveName}) = 3')
 
        # Line color
        lineRGBA = colors.to_rgba(trace.get_color())
        igorpro.execute(f"""ModifyGraph  rgb({yWaveName}) = ({0xffff * lineRGBA[0]},
                        {0xffff * lineRGBA[1]},
                        {0xffff * lineRGBA[2]})""")
 
        i += 1

 

For developing this, I created a public git repository that I'll be 'casually' working on (benmurphybaum/IgorPlot: Converting matplotlib plots into Igor graphs using Python). Feel free to clone it and submit pull requests for any code you (or anyone else) wants to add. There are some instructions on setting up the project in the readme. I have also included some Igor code that will automate creating a virtual environment for the project and installing the dependencies -- I'd recommend starting there.

As of today, it will convert simple line plots reasonably well, but other graph types are not implemented yet. Once the graph is converted and displayed in Igor, you can create a graph recreation macro to save the Igor commands that will regenerate the graph.

DoWindow/R NameOfGraph

The code that will regenerate the graph is saved into the main procedure file in Igor. This is going to be one of your best resources for your students to use to get a feel for the Igor commands needed to customize graphs. 

From the sidelines ...

* Consider using the approach ... for index, trace in enumerate(axis.get_lines()) ... rather than using an explicit i = 0 and i += 1 approach

* Consider capturing the translations of the python code into Igor Pro code being dumped a text string, dumped to a procedure file or notebook, and executed via a call igorpro.execute(f"create_py2IPplott()").

* As a step further to the above, separate the translation of python->Igor Pro actions into two parts. The first part is a routine that captures all the plot trace data into waves in a specific root:pythonplot data folder. The second part is a routine that displays waves from the data folder root:matlibplot and applies the given graph style. The latter routine could even be built to create a WinRecreation. Both routines could eventually be designed to become standard sub-modules to the igorpro python library, accessible for example via #import igorpro.captureplottraces or #import igorpro.capturegraphstyle.

Essentially, users of python could eventually be instructed to do the steps below to translate python plots to Igor Pro plots.

...
import igorpro as ip
 
 
ip.capturegraphtraces(...)
ip.capturegraphstyle(...)
igorpro.execute(f"create_py2IPplot()")
 
 
Function create_py2IPplot()
 
    DFREF pplot = root:pythonplot
 
    // display all waves in pplot
    // apply graph style
 
    return 0
end